diff --git a/docSite/content/docs/development/upgrading/48.md b/docSite/content/docs/development/upgrading/48.md
index 1d415ed279e..ad52ce4d2ef 100644
--- a/docSite/content/docs/development/upgrading/48.md
+++ b/docSite/content/docs/development/upgrading/48.md
@@ -1,5 +1,5 @@
---
-title: 'V4.8(进行中)'
+title: 'V4.8(开发中)'
description: 'FastGPT V4.8 更新说明'
icon: 'upgrade'
draft: false
@@ -18,10 +18,14 @@ FastGPT workflow V2上线,支持更加简洁的工作流模式。
## V4.8 更新说明
1. 重构 - 工作流
-2. 新增 - 工作流 Debug 模式,可以调试单个节点或者逐步调试工作流。
-3. 新增 - 定时执行应用。可轻松实现定时任务。
-4. 新增 - 插件自定义输入优化,可以渲染输入组件。
-6. 优化 - 工作流连线,可以四向连接,方便构建循环工作流。
-7. 优化 - 工作流上下文传递,性能🚀。
-8. 优化 - 简易模式,更新配置后自动更新调试框内容,无需保存。
-9. 优化 - worker进程管理,并将计算 Token 任务分配给 worker 进程。
\ No newline at end of file
+2. 新增 - 判断器。支持 if elseIf else 判断。
+3. 新增 - 变量更新节点。支持更新运行中工作流输出变量,或更新全局变量。
+4. 新增 - 工作流 Debug 模式,可以调试单个节点或者逐步调试工作流。
+5. 新增 - 定时执行应用。可轻松实现定时任务。
+6. 新增 - 插件自定义输入优化,可以渲染输入组件。
+7. 优化 - 工作流连线,可以四向连接,方便构建循环工作流。
+8. 优化 - 工作流上下文传递,性能🚀。
+9. 优化 - 简易模式,更新配置后自动更新调试框内容,无需保存。
+10. 优化 - worker进程管理,并将计算 Token 任务分配给 worker 进程。
+11. 修复 - 工具调用时候,name不能是数字开头(随机数有概率数字开头)
+12. 修复 - 分享链接, query 全局变量会被缓存。
\ No newline at end of file
diff --git a/packages/global/common/string/tools.ts b/packages/global/common/string/tools.ts
index 8e3c8da7cc7..26147f5d347 100644
--- a/packages/global/common/string/tools.ts
+++ b/packages/global/common/string/tools.ts
@@ -50,8 +50,18 @@ export const replaceSensitiveText = (text: string) => {
return text;
};
+/* Make sure the first letter is definitely lowercase */
export const getNanoid = (size = 12) => {
- return customAlphabet('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890', size)();
+ const firstChar = customAlphabet('abcdefghijklmnopqrstuvwxyz', 1)();
+
+ if (size === 1) return firstChar;
+
+ const randomsStr = customAlphabet(
+ 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890',
+ size - 1
+ )();
+
+ return `${firstChar}${randomsStr}`;
};
export const replaceRegChars = (text: string) => text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
diff --git a/packages/global/core/workflow/template/input.ts b/packages/global/core/workflow/template/input.ts
index a588910a9a3..695fec1e22d 100644
--- a/packages/global/core/workflow/template/input.ts
+++ b/packages/global/core/workflow/template/input.ts
@@ -9,9 +9,10 @@ export const Input_Template_History: FlowNodeInputItemType = {
renderTypeList: [FlowNodeInputTypeEnum.numberInput, FlowNodeInputTypeEnum.reference],
valueType: WorkflowIOValueTypeEnum.chatHistory,
label: 'core.module.input.label.chat history',
+ description: '最多携带多少轮对话记录',
required: true,
min: 0,
- max: 30,
+ max: 50,
value: 6
};
diff --git a/packages/web/components/common/DndDrag/DragIcon.tsx b/packages/web/components/common/DndDrag/DragIcon.tsx
new file mode 100644
index 00000000000..493a409d2d3
--- /dev/null
+++ b/packages/web/components/common/DndDrag/DragIcon.tsx
@@ -0,0 +1,14 @@
+import { DragHandleIcon } from '@chakra-ui/icons';
+import { Box } from '@chakra-ui/react';
+import React from 'react';
+import { DraggableProvided } from 'react-beautiful-dnd';
+
+const DragIcon = ({ provided }: { provided: DraggableProvided }) => {
+ return (
+
+
+
+ );
+};
+
+export default DragIcon;
diff --git a/packages/web/components/common/DndDrag/index.tsx b/packages/web/components/common/DndDrag/index.tsx
new file mode 100644
index 00000000000..a78cedc7951
--- /dev/null
+++ b/packages/web/components/common/DndDrag/index.tsx
@@ -0,0 +1,61 @@
+import { Box } from '@chakra-ui/react';
+import React, { useState } from 'react';
+import {
+ DragDropContext,
+ DroppableProps,
+ Droppable,
+ DraggableChildrenFn,
+ DragStart,
+ DropResult
+} from 'react-beautiful-dnd';
+
+type Props = {
+ onDragEndCb: (result: T[]) => void;
+ renderClone?: DraggableChildrenFn;
+ children: DroppableProps['children'];
+ dataList: T[];
+};
+
+function DndDrag({ children, renderClone, onDragEndCb, dataList }: Props) {
+ const [draggingItemHeight, setDraggingItemHeight] = useState(0);
+
+ const onDragStart = (start: DragStart) => {
+ const draggingNode = document.querySelector(`[data-rbd-draggable-id="${start.draggableId}"]`);
+ setDraggingItemHeight(draggingNode?.getBoundingClientRect().height || 0);
+ };
+
+ const onDragEnd = (result: DropResult) => {
+ if (!result.destination) {
+ return;
+ }
+ setDraggingItemHeight(0);
+
+ const startIndex = result.source.index;
+ const endIndex = result.destination.index;
+
+ const list = Array.from(dataList);
+ const [removed] = list.splice(startIndex, 1);
+ list.splice(endIndex, 0, removed);
+
+ onDragEndCb(list);
+ };
+
+ return (
+
+
+ {(provided, snapshot) => {
+ return (
+
+ {children(provided, snapshot)}
+ {snapshot.isDraggingOver && }
+
+ );
+ }}
+
+
+ );
+}
+
+export default DndDrag;
+
+export * from 'react-beautiful-dnd';
diff --git a/packages/web/package.json b/packages/web/package.json
index dad5429d262..934f1c6651e 100644
--- a/packages/web/package.json
+++ b/packages/web/package.json
@@ -31,12 +31,14 @@
"react": "18.2.0",
"react-day-picker": "^8.7.1",
"react-dom": "18.2.0",
- "react-i18next": "13.5.0"
+ "react-i18next": "13.5.0",
+ "react-beautiful-dnd": "^13.1.1"
},
"devDependencies": {
"@types/lodash": "^4.14.191",
"@types/papaparse": "^5.3.7",
"@types/react": "18.2.0",
- "@types/react-dom": "18.2.0"
+ "@types/react-dom": "18.2.0",
+ "@types/react-beautiful-dnd": "^13.1.8"
}
}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 73c666de60c..8b174057742 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -292,6 +292,9 @@ importers:
react:
specifier: 18.2.0
version: 18.2.0
+ react-beautiful-dnd:
+ specifier: ^13.1.1
+ version: 13.1.1(react-dom@18.2.0)(react@18.2.0)
react-day-picker:
specifier: ^8.7.1
version: 8.7.1(date-fns@2.30.0)(react@18.2.0)
@@ -311,6 +314,9 @@ importers:
'@types/react':
specifier: 18.2.0
version: 18.2.0
+ '@types/react-beautiful-dnd':
+ specifier: ^13.1.8
+ version: 13.1.8
'@types/react-dom':
specifier: 18.2.0
version: 18.2.0
@@ -431,9 +437,6 @@ importers:
react:
specifier: 18.2.0
version: 18.2.0
- react-beautiful-dnd:
- specifier: ^13.1.1
- version: 13.1.1(react-dom@18.2.0)(react@18.2.0)
react-day-picker:
specifier: ^8.7.1
version: 8.7.1(date-fns@2.30.0)(react@18.2.0)
@@ -504,9 +507,6 @@ importers:
'@types/react':
specifier: 18.2.0
version: 18.2.0
- '@types/react-beautiful-dnd':
- specifier: ^13.1.8
- version: 13.1.8
'@types/react-dom':
specifier: 18.2.0
version: 18.2.0
diff --git a/projects/app/i18n/zh/common.json b/projects/app/i18n/zh/common.json
index 7069ba9f202..0b9f171a599 100644
--- a/projects/app/i18n/zh/common.json
+++ b/projects/app/i18n/zh/common.json
@@ -644,8 +644,7 @@
"success": "开始同步"
}
},
- "training": {
- }
+ "training": {}
},
"data": {
"Auxiliary Data": "辅助数据",
@@ -920,7 +919,7 @@
"AppId": "应用的ID",
"ChatId": "当前对话ID",
"Current time": "当前时间",
- "Histories": "历史记录,最多取10条",
+ "Histories": "最近10条聊天记录",
"Key already exists": "Key 已经存在",
"Key cannot be empty": "参数名不能为空",
"Props name": "参数名",
diff --git a/projects/app/package.json b/projects/app/package.json
index 0667849c0a3..6975d9fa1e4 100644
--- a/projects/app/package.json
+++ b/projects/app/package.json
@@ -47,7 +47,6 @@
"nextjs-node-loader": "^1.1.5",
"nprogress": "^0.2.0",
"react": "18.2.0",
- "react-beautiful-dnd": "^13.1.1",
"react-day-picker": "^8.7.1",
"react-dom": "18.2.0",
"react-hook-form": "7.43.1",
@@ -73,7 +72,6 @@
"@types/lodash": "^4.14.191",
"@types/node": "^20.8.5",
"@types/react": "18.2.0",
- "@types/react-beautiful-dnd": "^13.1.8",
"@types/react-dom": "18.2.0",
"@types/react-syntax-highlighter": "^15.5.6",
"@types/request-ip": "^0.0.37",
diff --git a/projects/app/src/components/ChatBox/components/VariableInput.tsx b/projects/app/src/components/ChatBox/components/VariableInput.tsx
index a78edbe3e8a..9135a19dc6b 100644
--- a/projects/app/src/components/ChatBox/components/VariableInput.tsx
+++ b/projects/app/src/components/ChatBox/components/VariableInput.tsx
@@ -1,5 +1,5 @@
import { VariableItemType } from '@fastgpt/global/core/app/type.d';
-import React, { useState } from 'react';
+import React, { useEffect, useState } from 'react';
import { UseFormReturn } from 'react-hook-form';
import { useTranslation } from 'next-i18next';
import { Box, Button, Card, Input, Textarea } from '@chakra-ui/react';
@@ -24,10 +24,23 @@ const VariableInput = ({
chatForm: UseFormReturn;
}) => {
const { t } = useTranslation();
- const [refresh, setRefresh] = useState(false);
- const { register, setValue, handleSubmit: handleSubmitChat, watch } = chatForm;
+ const { register, unregister, setValue, handleSubmit: handleSubmitChat, watch } = chatForm;
const variables = watch('variables');
+ useEffect(() => {
+ // 重新注册所有字段
+ variableModules.forEach((item) => {
+ register(`variables.${item.key}`, { required: item.required });
+ });
+
+ return () => {
+ // 组件卸载时注销所有字段
+ variableModules.forEach((item) => {
+ unregister(`variables.${item.key}`);
+ });
+ };
+ }, [register, unregister, variableModules]);
+
return (
{/* avatar */}
@@ -92,7 +105,6 @@ const VariableInput = ({
value={variables[item.key]}
onchange={(e) => {
setValue(`variables.${item.key}`, e);
- setRefresh((state) => !state);
}}
/>
)}
@@ -116,4 +128,4 @@ const VariableInput = ({
);
};
-export default React.memo(VariableInput);
+export default VariableInput;
diff --git a/projects/app/src/components/ChatBox/index.tsx b/projects/app/src/components/ChatBox/index.tsx
index 807e265849e..66c23d31e54 100644
--- a/projects/app/src/components/ChatBox/index.tsx
+++ b/projects/app/src/components/ChatBox/index.tsx
@@ -158,12 +158,6 @@ const ChatBox = (
isChatting
} = useChatProviderStore();
- /* variable */
- const filterVariableModules = useMemo(
- () => variableModules.filter((item) => item.type !== VariableInputEnum.custom),
- [variableModules]
- );
-
// compute variable input is finish.
const chatForm = useForm({
defaultValues: {
@@ -174,9 +168,15 @@ const ChatBox = (
}
});
const { setValue, watch, handleSubmit } = chatForm;
- const variables = watch('variables');
const chatStarted = watch('chatStarted');
- const variableIsFinish = useMemo(() => {
+
+ /* variable */
+ const variables = watch('variables');
+ const filterVariableModules = useMemo(
+ () => variableModules.filter((item) => item.type !== VariableInputEnum.custom),
+ [variableModules]
+ );
+ const variableIsFinish = (() => {
if (!filterVariableModules || filterVariableModules.length === 0 || chatHistories.length > 0)
return true;
@@ -188,7 +188,7 @@ const ChatBox = (
}
return chatStarted;
- }, [filterVariableModules, chatHistories.length, chatStarted, variables]);
+ })();
// 滚动到底部
const scrollToBottom = (behavior: 'smooth' | 'auto' = 'smooth') => {
@@ -360,6 +360,12 @@ const ChatBox = (
[questionGuide, shareId, outLinkUid, teamId, teamToken]
);
+ /* Abort chat completions, questionGuide */
+ const abortRequest = useCallback(() => {
+ chatController.current?.abort('stop');
+ questionGuideController.current?.abort('stop');
+ }, []);
+
/**
* user confirm send prompt
*/
@@ -383,6 +389,8 @@ const ChatBox = (
return;
}
+ abortRequest();
+
text = text.trim();
if (!text && files.length === 0) {
@@ -472,7 +480,8 @@ const ChatBox = (
generatingMessage: (e) => generatingMessage({ ...e, autoTTSResponse }),
variables
});
- setValue('variables', newVariables || []);
+
+ newVariables && setValue('variables', newVariables);
isNewChatReplace.current = isNewChat;
@@ -540,6 +549,7 @@ const ChatBox = (
})();
},
[
+ abortRequest,
chatHistories,
createQuestionGuide,
finishSegmentedAudio,
@@ -710,7 +720,7 @@ const ChatBox = (
});
};
},
- [appId, chatId, feedbackType, teamId, teamToken]
+ [appId, chatId, feedbackType, setChatHistories, teamId, teamToken]
);
const onADdUserDislike = useCallback(
(chat: ChatSiteItemType) => {
@@ -747,7 +757,7 @@ const ChatBox = (
return () => setFeedbackId(chat.dataId);
}
},
- [appId, chatId, feedbackType, outLinkUid, shareId, teamId, teamToken]
+ [appId, chatId, feedbackType, outLinkUid, setChatHistories, shareId, teamId, teamToken]
);
const onReadUserDislike = useCallback(
(chat: ChatSiteItemType) => {
@@ -868,6 +878,7 @@ const ChatBox = (
setValue('variables', e || defaultVal);
},
resetHistory(e) {
+ abortRequest();
setValue('chatStarted', e.length > 0);
setChatHistories(e);
},
diff --git a/projects/app/src/components/core/workflow/Flow/nodes/NodeIfElse/ListItem.tsx b/projects/app/src/components/core/workflow/Flow/nodes/NodeIfElse/ListItem.tsx
index 61c172e5828..7a5b19e83e2 100644
--- a/projects/app/src/components/core/workflow/Flow/nodes/NodeIfElse/ListItem.tsx
+++ b/projects/app/src/components/core/workflow/Flow/nodes/NodeIfElse/ListItem.tsx
@@ -1,5 +1,8 @@
import { Box, Button, Flex } from '@chakra-ui/react';
-import { DraggableProvided, DraggableStateSnapshot } from 'react-beautiful-dnd';
+import {
+ DraggableProvided,
+ DraggableStateSnapshot
+} from '@fastgpt/web/components/common/DndDrag/index';
import Container from '../../components/Container';
import { DragHandleIcon, MinusIcon, SmallAddIcon } from '@chakra-ui/icons';
import { IfElseListItemType } from '@fastgpt/global/core/workflow/template/system/ifElse/type';
@@ -25,6 +28,7 @@ import { getElseIFLabel, getHandleId } from '@fastgpt/global/core/workflow/utils
import { SourceHandle } from '../render/Handle';
import { Position, useReactFlow } from 'reactflow';
import { getReferenceDataValueType } from '@/web/core/workflow/utils';
+import DragIcon from '@fastgpt/web/components/common/DndDrag/DragIcon';
const ListItem = ({
provided,
@@ -63,11 +67,7 @@ const ListItem = ({
>
- {ifElseList.length > 1 && (
-
-
-
- )}
+ {ifElseList.length > 1 && }
{getElseIFLabel(conditionIndex)}
diff --git a/projects/app/src/components/core/workflow/Flow/nodes/NodeIfElse/index.tsx b/projects/app/src/components/core/workflow/Flow/nodes/NodeIfElse/index.tsx
index b1042ecdb8a..026245d9862 100644
--- a/projects/app/src/components/core/workflow/Flow/nodes/NodeIfElse/index.tsx
+++ b/projects/app/src/components/core/workflow/Flow/nodes/NodeIfElse/index.tsx
@@ -9,7 +9,7 @@ import { IfElseListItemType } from '@fastgpt/global/core/workflow/template/syste
import { useContextSelector } from 'use-context-selector';
import { WorkflowContext } from '../../../context';
import Container from '../../components/Container';
-import { DragDropContext, DragStart, Draggable, DropResult, Droppable } from 'react-beautiful-dnd';
+import DndDrag, { Draggable, DropResult } from '@fastgpt/web/components/common/DndDrag/index';
import { SourceHandle } from '../render/Handle';
import { getHandleId } from '@fastgpt/global/core/workflow/utils';
import ListItem from './ListItem';
@@ -20,8 +20,6 @@ const NodeIfElse = ({ data, selected }: NodeProps) => {
const { nodeId, inputs = [] } = data;
const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode);
- const [draggingItemHeight, setDraggingItemHeight] = useState(0);
-
const ifElseList = useMemo(
() =>
(inputs.find((input) => input.key === NodeInputKeyEnum.ifElseList)
@@ -47,73 +45,49 @@ const NodeIfElse = ({ data, selected }: NodeProps) => {
[inputs, nodeId, onChangeNode]
);
- const reorder = (list: IfElseListItemType[], startIndex: number, endIndex: number) => {
- const result = Array.from(list);
- const [removed] = result.splice(startIndex, 1);
- result.splice(endIndex, 0, removed);
-
- return result;
- };
-
- const onDragStart = (start: DragStart) => {
- const draggingNode = document.querySelector(`[data-rbd-draggable-id="${start.draggableId}"]`);
- setDraggingItemHeight(draggingNode?.getBoundingClientRect().height || 0);
- };
-
- const onDragEnd = (result: DropResult) => {
- if (!result.destination) {
- return;
- }
- const newList = reorder(ifElseList, result.source.index, result.destination.index);
-
- onUpdateIfElseList(newList);
- setDraggingItemHeight(0);
- };
-
return (
-
-
- (
-
- )}
- >
- {(provided, snapshot) => (
-
- {ifElseList.map((conditionItem, conditionIndex) => (
-
- {(provided, snapshot) => (
-
- )}
-
- ))}
- {snapshot.isDraggingOver && }
-
- )}
-
-
+
+
+ onDragEndCb={(list) => onUpdateIfElseList(list)}
+ dataList={ifElseList}
+ renderClone={(provided, snapshot, rubric) => (
+
+ )}
+ >
+ {(provided) => (
+
+ {ifElseList.map((conditionItem, conditionIndex) => (
+
+ {(provided, snapshot) => (
+
+ )}
+
+ ))}
+
+ )}
+
+
diff --git a/projects/app/src/pages/api/v1/chat/completions.ts b/projects/app/src/pages/api/v1/chat/completions.ts
index 7f3b4625f90..f1bd84207ba 100644
--- a/projects/app/src/pages/api/v1/chat/completions.ts
+++ b/projects/app/src/pages/api/v1/chat/completions.ts
@@ -45,7 +45,6 @@ import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runti
import { dispatchWorkFlowV1 } from '@fastgpt/service/core/workflow/dispatchV1';
import { setEntryEntries } from '@fastgpt/service/core/workflow/dispatchV1/utils';
import { NextAPI } from '@/service/middle/entry';
-import { MongoAppVersion } from '@fastgpt/service/core/app/versionSchema';
import { getAppLatestVersion } from '@fastgpt/service/core/app/controller';
type FastGptWebChatProps = {
diff --git a/projects/app/src/pages/app/detail/components/FlowEdit/index.tsx b/projects/app/src/pages/app/detail/components/FlowEdit/index.tsx
index 0f9738e8989..71bd0865425 100644
--- a/projects/app/src/pages/app/detail/components/FlowEdit/index.tsx
+++ b/projects/app/src/pages/app/detail/components/FlowEdit/index.tsx
@@ -28,7 +28,7 @@ const Render = ({ app, onClose }: Props) => {
useEffect(() => {
if (!isV2Workflow) return;
initData(JSON.parse(workflowStringData));
- }, [isV2Workflow, initData, workflowStringData]);
+ }, [isV2Workflow, initData, app._id]);
useEffect(() => {
if (!isV2Workflow) {
diff --git a/projects/app/src/pages/chat/share.tsx b/projects/app/src/pages/chat/share.tsx
index 439cb725c81..32933f45041 100644
--- a/projects/app/src/pages/chat/share.tsx
+++ b/projects/app/src/pages/chat/share.tsx
@@ -99,8 +99,8 @@ const OutLink = ({
data: {
messages: prompts,
variables: {
- ...customVariables,
- ...variables
+ ...variables,
+ ...customVariables
},
shareId,
chatId: completionChatId,