From f4d4d6516c4adc34f2f9cac7a91abef5c6d7d671 Mon Sep 17 00:00:00 2001
From: Archer <545436317@qq.com>
Date: Mon, 23 Sep 2024 15:43:57 +0800
Subject: [PATCH] Support simpleApp select workflow (#2772)
* fix: share page id error
* feat: simple workflow support childApp tool
* perf: aichat box animation
---
.../zh-cn/docs/development/upgrading/4811.md | 2 +-
.../global/core/workflow/template/input.ts | 1 +
.../service/core/app/plugin/controller.ts | 2 +-
packages/web/i18n/zh/app.json | 4 +-
.../ChatBox/components/ChatItem.tsx | 2 +-
.../PluginRunBox/components/RenderOutput.tsx | 2 +-
.../components/renderPluginInput.tsx | 4 +-
.../core/chat/components/AIResponseBox.tsx | 10 ++--
.../Publish/Link/SelectUsingWayModal.tsx | 1 +
.../SimpleApp/components/ToolSelectModal.tsx | 46 +++++++++++--------
.../nodes/NodePluginIO/InputEditModal.tsx | 3 +-
projects/app/src/pages/chat/share.tsx | 14 ++----
projects/app/src/web/core/app/utils.ts | 18 +++++++-
.../app/src/web/core/chat/storeShareChat.ts | 25 ++++++----
14 files changed, 83 insertions(+), 51 deletions(-)
diff --git a/docSite/content/zh-cn/docs/development/upgrading/4811.md b/docSite/content/zh-cn/docs/development/upgrading/4811.md
index 2103e32228f8..32737cd2393d 100644
--- a/docSite/content/zh-cn/docs/development/upgrading/4811.md
+++ b/docSite/content/zh-cn/docs/development/upgrading/4811.md
@@ -92,7 +92,7 @@ weight: 813
6. 新增 - 支持 Openai o1 模型,需增加模型的 `defaultConfig` 配置,覆盖 `temperature`、`max_tokens` 和 `stream`配置,o1 不支持 stream 模式, 详细可重新拉取 `config.json` 配置文件查看。
7. 新增 - AI 对话节点知识库引用,支持配置 role=system 和 role=user,已配置的过自定义提示词的节点将会保持 user 模式,其余用户将转成 system 模式。
8. 新增 - 插件支持上传系统文件。
-9. 新增 - 支持工作流嵌套子应用时,可以设置`非流模式`。
+9. 新增 - 支持工作流嵌套子应用时,可以设置`非流模式`,同时简易模式也可以选择工作流作为插件了,简易模式调用子应用时,都将强制使用非流模式。
10. 新增 - 调试模式下,子应用调用,支持返回详细运行数据。
11. 新增 - 保留所有模式下子应用嵌套调用的日志。
12. 优化 - 工作流嵌套层级限制 20 层,避免因编排不合理导致的无限死循环。
diff --git a/packages/global/core/workflow/template/input.ts b/packages/global/core/workflow/template/input.ts
index f2b392f7ad35..766a7881da9e 100644
--- a/packages/global/core/workflow/template/input.ts
+++ b/packages/global/core/workflow/template/input.ts
@@ -23,6 +23,7 @@ export const Input_Template_UserChatInput: FlowNodeInputItemType = {
renderTypeList: [FlowNodeInputTypeEnum.reference, FlowNodeInputTypeEnum.textarea],
valueType: WorkflowIOValueTypeEnum.string,
label: i18nT('workflow:user_question'),
+ toolDescription: i18nT('workflow:user_question'),
required: true
};
diff --git a/packages/service/core/app/plugin/controller.ts b/packages/service/core/app/plugin/controller.ts
index b64287646626..14a808ae7146 100644
--- a/packages/service/core/app/plugin/controller.ts
+++ b/packages/service/core/app/plugin/controller.ts
@@ -89,7 +89,7 @@ export async function getChildAppPreviewNode({
intro: app.intro,
inputExplanationUrl: app.inputExplanationUrl,
showStatus: app.showStatus,
- isTool: isPlugin,
+ isTool: true,
version: app.version,
sourceHandle: getHandleConfig(true, true, true, true),
targetHandle: getHandleConfig(true, true, true, true),
diff --git a/packages/web/i18n/zh/app.json b/packages/web/i18n/zh/app.json
index 5fd003b8c6ce..acf1de8a00ed 100644
--- a/packages/web/i18n/zh/app.json
+++ b/packages/web/i18n/zh/app.json
@@ -81,7 +81,7 @@
"permission.des.write": "可查看和编辑应用",
"plugin_cost_per_times": "{{cost}}/次",
"plugin_dispatch": "插件调用",
- "plugin_dispatch_tip": "给模型附加额外的能力,具体调用哪些插件,将由模型自主决定。\n若选择了插件,知识库调用将自动作为一个特殊的插件。",
+ "plugin_dispatch_tip": "给模型附加获取外部数据的能力,具体调用哪些插件,将由模型自主决定,所有插件都将以非流模式运行。\n若选择了插件,知识库调用将自动作为一个特殊的插件。",
"publish_channel": "发布渠道",
"publish_success": "发布成功",
"saved_success": "保存成功",
@@ -155,4 +155,4 @@
"workflow.user_file_input_desc": "用户上传的文档和图片链接",
"workflow.user_select": "用户选择",
"workflow.user_select_tip": "该模块可配置多个选项,以供对话时选择。不同选项可导向不同工作流支线"
-}
\ No newline at end of file
+}
diff --git a/projects/app/src/components/core/chat/ChatContainer/ChatBox/components/ChatItem.tsx b/projects/app/src/components/core/chat/ChatContainer/ChatBox/components/ChatItem.tsx
index be1af1b858a2..91f89439ea7d 100644
--- a/projects/app/src/components/core/chat/ChatContainer/ChatBox/components/ChatItem.tsx
+++ b/projects/app/src/components/core/chat/ChatContainer/ChatBox/components/ChatItem.tsx
@@ -95,7 +95,7 @@ const AIContentCard = React.memo(function AIContentCard({
);
diff --git a/projects/app/src/components/core/chat/ChatContainer/PluginRunBox/components/RenderOutput.tsx b/projects/app/src/components/core/chat/ChatContainer/PluginRunBox/components/RenderOutput.tsx
index 0a575cd45875..03429edf5198 100644
--- a/projects/app/src/components/core/chat/ChatContainer/PluginRunBox/components/RenderOutput.tsx
+++ b/projects/app/src/components/core/chat/ChatContainer/PluginRunBox/components/RenderOutput.tsx
@@ -33,7 +33,7 @@ const RenderOutput = () => {
);
diff --git a/projects/app/src/components/core/chat/ChatContainer/PluginRunBox/components/renderPluginInput.tsx b/projects/app/src/components/core/chat/ChatContainer/PluginRunBox/components/renderPluginInput.tsx
index fb96f19a3c22..b3a34b6829e9 100644
--- a/projects/app/src/components/core/chat/ChatContainer/PluginRunBox/components/renderPluginInput.tsx
+++ b/projects/app/src/components/core/chat/ChatContainer/PluginRunBox/components/renderPluginInput.tsx
@@ -110,9 +110,9 @@ const RenderPluginInput = ({
*
)}
- {input.label}
+ {t(input.label as any)}
- {input.description && }
+ {input.description && }
{render}
diff --git a/projects/app/src/components/core/chat/components/AIResponseBox.tsx b/projects/app/src/components/core/chat/components/AIResponseBox.tsx
index 60732856d55c..9efa62e9d7d6 100644
--- a/projects/app/src/components/core/chat/components/AIResponseBox.tsx
+++ b/projects/app/src/components/core/chat/components/AIResponseBox.tsx
@@ -24,7 +24,7 @@ import { onSendPrompt } from '../ChatContainer/useChat';
type props = {
value: UserChatItemValueItemType | AIChatItemValueItemType;
- isLastChild: boolean;
+ isLastResponseValue: boolean;
isChatting: boolean;
};
@@ -167,11 +167,13 @@ const RenderInteractive = React.memo(function RenderInteractive({
);
});
-const AIResponseBox = ({ value, isLastChild, isChatting }: props) => {
+const AIResponseBox = ({ value, isLastResponseValue, isChatting }: props) => {
if (value.type === ChatItemValueTypeEnum.text && value.text)
- return ;
+ return (
+
+ );
if (value.type === ChatItemValueTypeEnum.tool && value.tools)
- return ;
+ return ;
if (
value.type === ChatItemValueTypeEnum.interactive &&
value.interactive &&
diff --git a/projects/app/src/pages/app/detail/components/Publish/Link/SelectUsingWayModal.tsx b/projects/app/src/pages/app/detail/components/Publish/Link/SelectUsingWayModal.tsx
index 07f3d4d45f19..240443e07962 100644
--- a/projects/app/src/pages/app/detail/components/Publish/Link/SelectUsingWayModal.tsx
+++ b/projects/app/src/pages/app/detail/components/Publish/Link/SelectUsingWayModal.tsx
@@ -120,6 +120,7 @@ console.log("Chat box loaded")
return (
void;
};
+const childAppSystemKey: string[] = [
+ NodeInputKeyEnum.forbidStream,
+ NodeInputKeyEnum.history,
+ NodeInputKeyEnum.historyMaxAmount,
+ NodeInputKeyEnum.userChatInput
+];
+
enum TemplateTypeEnum {
'systemPlugin' = 'systemPlugin',
'teamPlugin' = 'teamPlugin'
@@ -61,6 +62,7 @@ enum TemplateTypeEnum {
const ToolSelectModal = ({ onClose, ...props }: Props & { onClose: () => void }) => {
const { t } = useTranslation();
+ const { appDetail } = useContextSelector(AppContext, (v) => v);
const [templateType, setTemplateType] = useState(TemplateTypeEnum.teamPlugin);
const [parentId, setParentId] = useState('');
@@ -85,9 +87,8 @@ const ToolSelectModal = ({ onClose, ...props }: Props & { onClose: () => void })
} else if (type === TemplateTypeEnum.teamPlugin) {
return getTeamPlugTemplates({
parentId,
- searchKey: searchVal,
- type: [AppTypeEnum.folder, AppTypeEnum.httpPlugin, AppTypeEnum.plugin]
- });
+ searchKey: searchVal
+ }).then((res) => res.filter((app) => app.id !== appDetail._id));
}
},
{
@@ -238,20 +239,24 @@ const RenderList = React.memo(function RenderList({
}
}, [configTool, reset]);
- const { mutate: onClickAdd, isLoading } = useRequest({
- mutationFn: async (template: FlowNodeTemplateType) => {
+ const { runAsync: onClickAdd, loading: isLoading } = useRequest2(
+ async (template: NodeTemplateListItemType) => {
const res = await getPreviewPluginNode({ appId: template.id });
// All input is tool params
- if (res.inputs.every((input) => input.toolDescription)) {
+ if (
+ res.inputs.every((input) => childAppSystemKey.includes(input.key) || input.toolDescription)
+ ) {
onAddTool(res);
} else {
reset();
setConfigTool(res);
}
},
- errorToast: t('common:core.module.templates.Load plugin error')
- });
+ {
+ errorToast: t('common:core.module.templates.Load plugin error')
+ }
+ );
return templates.length === 0 && !isLoadingData ? (
@@ -340,6 +345,7 @@ const RenderList = React.memo(function RenderList({
{!!configTool && (
{configTool.inputs
- .filter((item) => !item.toolDescription)
+ .filter((item) => !item.toolDescription && !childAppSystemKey.includes(item.key))
.map((input) => {
return (
{
const { shareId, authToken } = props;
- const { localUId, setLocalUId } = useShareChatStore();
+ const { localUId, loaded } = useShareChatStore();
const contextParams = useMemo(() => {
- if (!localUId) {
- const localId = `shareChat-${Date.now()}-${nanoid()}`;
- setLocalUId(localId);
- return { shareId, outLinkUid: authToken || localId };
- }
-
return { shareId, outLinkUid: authToken || localUId };
- }, []);
+ }, [authToken, localUId, shareId]);
+
+ if (!loaded || !contextParams.outLinkUid) return <>>;
return (
@@ -375,7 +371,7 @@ const Render = (props: Props) => {
);
};
-export default Render;
+export default React.memo(Render);
export async function getServerSideProps(context: any) {
const shareId = context?.query?.shareId || '';
diff --git a/projects/app/src/web/core/app/utils.ts b/projects/app/src/web/core/app/utils.ts
index c7cf05e47766..35c7f01ff509 100644
--- a/projects/app/src/web/core/app/utils.ts
+++ b/projects/app/src/web/core/app/utils.ts
@@ -378,7 +378,23 @@ export function form2AppWorkflow(
y: 545
},
version: tool.version,
- inputs: tool.inputs,
+ inputs: tool.inputs.map((input) => {
+ // Special key value
+ if (input.key === NodeInputKeyEnum.forbidStream) {
+ input.value = true;
+ }
+ // Special tool
+ if (
+ tool.flowNodeType === FlowNodeTypeEnum.appModule &&
+ input.key === NodeInputKeyEnum.history
+ ) {
+ return {
+ ...input,
+ value: formData.aiSettings.maxHistories
+ };
+ }
+ return input;
+ }),
outputs: tool.outputs
}
],
diff --git a/projects/app/src/web/core/chat/storeShareChat.ts b/projects/app/src/web/core/chat/storeShareChat.ts
index 3341097dc41a..6256f0e204eb 100644
--- a/projects/app/src/web/core/chat/storeShareChat.ts
+++ b/projects/app/src/web/core/chat/storeShareChat.ts
@@ -1,25 +1,34 @@
import { create } from 'zustand';
import { devtools, persist } from 'zustand/middleware';
import { immer } from 'zustand/middleware/immer';
+import { customAlphabet } from 'nanoid';
+const nanoid = customAlphabet(
+ 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWSYZ1234567890_',
+ 24
+);
type State = {
localUId: string;
- setLocalUId: (id: string) => void;
+ loaded: boolean;
};
export const useShareChatStore = create()(
devtools(
persist(
immer((set, get) => ({
- localUId: '',
- setLocalUId(id) {
- set((state) => {
- state.localUId = id;
- });
- }
+ localUId: `shareChat-${Date.now()}-${nanoid()}`,
+ loaded: false
})),
{
- name: 'shareChatStore'
+ name: 'shareChatStore',
+ onRehydrateStorage: () => (state) => {
+ if (state) {
+ state.loaded = true;
+ }
+ },
+ partialize: (state) => ({
+ localUId: state.localUId
+ })
}
)
)