mirror of
https://github.com/labring/FastGPT.git
synced 2025-07-22 04:06:18 +00:00
Ai histories (#1376)
* perf: workflow node ui * i18n * rename controller * fix: zindex * fix: leave page callback * revert button
This commit is contained in:
6
.vscode/settings.json
vendored
6
.vscode/settings.json
vendored
@@ -6,10 +6,10 @@
|
||||
"i18n-ally.localesPaths": [
|
||||
"projects/app/i18n",
|
||||
],
|
||||
"i18n-ally.enabledParsers": ["json"],
|
||||
"i18n-ally.enabledParsers": ["json", "yaml", "js", "ts"],
|
||||
"i18n-ally.keystyle": "nested",
|
||||
"i18n-ally.sortKeys": true,
|
||||
"i18n-ally.keepFulfilled": true,
|
||||
"i18n-ally.keepFulfilled": false,
|
||||
"i18n-ally.sourceLanguage": "zh", // 根据此语言文件翻译其他语言文件的变量和内容
|
||||
"i18n-ally.displayLanguage": "zh", // 显示语言
|
||||
"i18n-ally.displayLanguage": "zh" // 显示语言
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
import { FlowNodeTemplateTypeEnum, WorkflowIOValueTypeEnum } from '../../constants';
|
||||
import { getHandleConfig } from '../utils';
|
||||
import { FlowNodeTypeEnum } from '../../node/constant';
|
||||
import { FlowNodeOutputTypeEnum, FlowNodeTypeEnum } from '../../node/constant';
|
||||
import { VariableItemType } from '../../../app/type';
|
||||
import { FlowNodeTemplateType } from '../../type';
|
||||
|
||||
@@ -25,6 +25,7 @@ export const getGlobalVariableNode = ({
|
||||
id: item.key,
|
||||
key: item.key,
|
||||
valueType: WorkflowIOValueTypeEnum.string,
|
||||
type: FlowNodeOutputTypeEnum.static,
|
||||
label: item.label
|
||||
}))
|
||||
};
|
||||
|
@@ -27,7 +27,7 @@ export const ToolModule: FlowNodeTemplateType = {
|
||||
sourceHandle: getHandleConfig(true, true, false, true),
|
||||
targetHandle: getHandleConfig(true, true, false, true),
|
||||
avatar: '/imgs/workflow/tool.svg',
|
||||
name: '工具调用(实验)',
|
||||
name: '工具调用(实验)',
|
||||
intro: '通过AI模型自动选择一个或多个功能块进行调用,也可以对插件进行调用。',
|
||||
showStatus: true,
|
||||
inputs: [
|
||||
|
@@ -47,7 +47,7 @@ export const filterToolNodeIdByEdges = ({
|
||||
|
||||
export const getHistories = (history?: ChatItemType[] | number, histories: ChatItemType[] = []) => {
|
||||
if (!history) return [];
|
||||
if (typeof history === 'number') return histories.slice(-history);
|
||||
if (typeof history === 'number') return histories.slice(-(history * 2));
|
||||
if (Array.isArray(history)) return history;
|
||||
|
||||
return [];
|
||||
|
@@ -8,7 +8,7 @@ export const useBeforeunload = (props?: { callback?: () => any; tip?: string })
|
||||
|
||||
useEffect(() => {
|
||||
const listen =
|
||||
process.env.NODE_ENV !== 'production'
|
||||
process.env.NODE_ENV === 'production'
|
||||
? (e: any) => {
|
||||
e.preventDefault();
|
||||
e.returnValue = tip;
|
||||
|
@@ -92,9 +92,9 @@ export const useEditTextarea = ({
|
||||
closeBtnText?: string;
|
||||
}) => (
|
||||
<MyModal isOpen={isOpen} onClose={onClose} iconSrc={iconSrc} title={title} maxW={'500px'}>
|
||||
<ModalBody>
|
||||
<ModalBody pt={tip ? '3 !important' : '5 !important'}>
|
||||
{!!tip && (
|
||||
<Box mb={2} color={'myGray.500'} fontSize={'sm'}>
|
||||
<Box mb={3} color={'myGray.500'} fontSize={'sm'}>
|
||||
{tip}
|
||||
</Box>
|
||||
)}
|
||||
|
@@ -1,13 +1,11 @@
|
||||
{
|
||||
"App": "App",
|
||||
"Create New": "Create New",
|
||||
"Export": "Export",
|
||||
"Folder": "Folder",
|
||||
"Move": "Move",
|
||||
"Name": "Name",
|
||||
"Rename": "Rename",
|
||||
"Running": "Running",
|
||||
"Select value is empty": "Selected value is empty",
|
||||
"UnKnow": "Unknown",
|
||||
"Warning": "Warning",
|
||||
"app": {
|
||||
@@ -646,7 +644,8 @@
|
||||
"success": "Sync started"
|
||||
}
|
||||
},
|
||||
"training": {}
|
||||
"training": {
|
||||
}
|
||||
},
|
||||
"data": {
|
||||
"Auxiliary Data": "Auxiliary Data",
|
||||
@@ -1167,7 +1166,7 @@
|
||||
},
|
||||
"publish": {
|
||||
"OnRevert version": "Click back to that version",
|
||||
"OnRevert version confirm": "Are you sure to roll back the version?",
|
||||
"OnRevert version confirm": "Sure you want to roll back to this version? The configuration for the version in editing is saved for you and a new release is created for the rollback version.",
|
||||
"histories": "Publiish histories"
|
||||
},
|
||||
"tool": {
|
||||
@@ -1204,7 +1203,6 @@
|
||||
"Select Dataset": "Select this knowledge base",
|
||||
"Select Dataset Tips": "Only knowledge bases of the same index model can be selected",
|
||||
"Select Folder": "Enter folder",
|
||||
"System Data Queue": "Queue Length",
|
||||
"Training Name": "Data Training",
|
||||
"Upload Time": "Upload Time",
|
||||
"collections": {
|
||||
|
@@ -1,13 +1,11 @@
|
||||
{
|
||||
"App": "应用",
|
||||
"Create New": "",
|
||||
"Export": "导出",
|
||||
"Folder": "文件夹",
|
||||
"Move": "移动",
|
||||
"Name": "名称",
|
||||
"Rename": "重命名",
|
||||
"Running": "运行中",
|
||||
"Select value is empty": "选择的内容为空",
|
||||
"UnKnow": "未知",
|
||||
"Warning": "提示",
|
||||
"app": {
|
||||
@@ -646,7 +644,8 @@
|
||||
"success": "开始同步"
|
||||
}
|
||||
},
|
||||
"training": {}
|
||||
"training": {
|
||||
}
|
||||
},
|
||||
"data": {
|
||||
"Auxiliary Data": "辅助数据",
|
||||
@@ -1167,7 +1166,7 @@
|
||||
},
|
||||
"publish": {
|
||||
"OnRevert version": "点击回退到该版本",
|
||||
"OnRevert version confirm": "确认回退该版本?",
|
||||
"OnRevert version confirm": "确认回退至该版本?会为您保存编辑中版本的配置,并为回退版本创建一个新的发布版本。",
|
||||
"histories": "发布记录"
|
||||
},
|
||||
"tool": {
|
||||
@@ -1204,7 +1203,6 @@
|
||||
"Select Dataset": "选择该知识库",
|
||||
"Select Dataset Tips": "仅能选择同一个索引模型的知识库",
|
||||
"Select Folder": "进入文件夹",
|
||||
"System Data Queue": "排队长度",
|
||||
"Training Name": "数据训练",
|
||||
"Upload Time": "上传时间",
|
||||
"collections": {
|
||||
|
@@ -99,7 +99,7 @@ const ChatTest = (
|
||||
return (
|
||||
<>
|
||||
<Flex
|
||||
zIndex={3}
|
||||
zIndex={101}
|
||||
flexDirection={'column'}
|
||||
position={'absolute'}
|
||||
top={5}
|
||||
|
@@ -90,7 +90,7 @@ const NodeDatasetConcat = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
||||
</Box>
|
||||
<Box flex={'1 0 0'} />
|
||||
<Button
|
||||
variant={'transparentBase'}
|
||||
variant={'whitePrimary'}
|
||||
leftIcon={<SmallAddIcon />}
|
||||
iconSpacing={1}
|
||||
size={'sm'}
|
||||
|
@@ -38,6 +38,8 @@ type Props = FlowNodeItemType & {
|
||||
|
||||
const NodeCard = (props: Props) => {
|
||||
const { t } = useTranslation();
|
||||
const { toast } = useToast();
|
||||
|
||||
const {
|
||||
children,
|
||||
avatar = LOGO_ICON,
|
||||
@@ -59,6 +61,13 @@ const NodeCard = (props: Props) => {
|
||||
const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList);
|
||||
const setHoverNodeId = useContextSelector(WorkflowContext, (v) => v.setHoverNodeId);
|
||||
const onUpdateNodeError = useContextSelector(WorkflowContext, (v) => v.onUpdateNodeError);
|
||||
const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode);
|
||||
|
||||
// custom title edit
|
||||
const { onOpenModal: onOpenCustomTitleModal, EditModal: EditTitleModal } = useEditTitle({
|
||||
title: t('common.Custom Title'),
|
||||
placeholder: t('app.module.Custom Title Tip') || ''
|
||||
});
|
||||
|
||||
const showToolHandle = useMemo(
|
||||
() => isTool && !!nodeList.find((item) => item?.flowNodeType === FlowNodeTypeEnum.tools),
|
||||
@@ -81,13 +90,42 @@ const NodeCard = (props: Props) => {
|
||||
<Box ml={3} fontSize={'lg'} fontWeight={'medium'}>
|
||||
{t(name)}
|
||||
</Box>
|
||||
{!menuForbid?.rename && (
|
||||
<MyIcon
|
||||
className="controller-rename"
|
||||
display={'none'}
|
||||
name={'edit'}
|
||||
w={'14px'}
|
||||
cursor={'pointer'}
|
||||
ml={1}
|
||||
color={'myGray.500'}
|
||||
_hover={{ color: 'primary.600' }}
|
||||
onClick={() => {
|
||||
onOpenCustomTitleModal({
|
||||
defaultVal: name,
|
||||
onSuccess: (e) => {
|
||||
if (!e) {
|
||||
return toast({
|
||||
title: t('app.modules.Title is required'),
|
||||
status: 'warning'
|
||||
});
|
||||
}
|
||||
onChangeNode({
|
||||
nodeId,
|
||||
type: 'attr',
|
||||
key: 'name',
|
||||
value: e
|
||||
});
|
||||
}
|
||||
});
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Flex>
|
||||
<MenuRender
|
||||
name={name}
|
||||
nodeId={nodeId}
|
||||
pluginId={pluginId}
|
||||
flowNodeType={flowNodeType}
|
||||
inputs={inputs}
|
||||
menuForbid={menuForbid}
|
||||
/>
|
||||
<NodeIntro nodeId={nodeId} intro={intro} />
|
||||
@@ -101,11 +139,13 @@ const NodeCard = (props: Props) => {
|
||||
avatar,
|
||||
t,
|
||||
name,
|
||||
menuForbid,
|
||||
pluginId,
|
||||
flowNodeType,
|
||||
inputs,
|
||||
menuForbid,
|
||||
intro
|
||||
intro,
|
||||
onOpenCustomTitleModal,
|
||||
onChangeNode,
|
||||
toast
|
||||
]);
|
||||
|
||||
return (
|
||||
@@ -123,6 +163,9 @@ const NodeCard = (props: Props) => {
|
||||
},
|
||||
'& .controller-debug': {
|
||||
display: 'block'
|
||||
},
|
||||
'& .controller-rename': {
|
||||
display: 'block'
|
||||
}
|
||||
}}
|
||||
onMouseEnter={() => setHoverNodeId(nodeId)}
|
||||
@@ -140,6 +183,8 @@ const NodeCard = (props: Props) => {
|
||||
{children}
|
||||
<ConnectionSourceHandle nodeId={nodeId} />
|
||||
<ConnectionTargetHandle nodeId={nodeId} />
|
||||
|
||||
<EditTitleModal maxLength={20} />
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
@@ -147,18 +192,14 @@ const NodeCard = (props: Props) => {
|
||||
export default React.memo(NodeCard);
|
||||
|
||||
const MenuRender = React.memo(function MenuRender({
|
||||
name,
|
||||
nodeId,
|
||||
pluginId,
|
||||
flowNodeType,
|
||||
inputs,
|
||||
menuForbid
|
||||
}: {
|
||||
name: string;
|
||||
nodeId: string;
|
||||
pluginId?: string;
|
||||
flowNodeType: Props['flowNodeType'];
|
||||
inputs: Props['inputs'];
|
||||
menuForbid?: Props['menuForbid'];
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
@@ -169,11 +210,7 @@ const MenuRender = React.memo(function MenuRender({
|
||||
const { openConfirm: onOpenConfirmSync, ConfirmModal: ConfirmSyncModal } = useConfirm({
|
||||
content: t('module.Confirm Sync Plugin')
|
||||
});
|
||||
// custom title edit
|
||||
const { onOpenModal: onOpenCustomTitleModal, EditModal: EditTitleModal } = useEditTitle({
|
||||
title: t('common.Custom Title'),
|
||||
placeholder: t('app.module.Custom Title Tip') || ''
|
||||
});
|
||||
|
||||
const { openConfirm: onOpenConfirmDeleteNode, ConfirmModal: ConfirmDeleteModal } = useConfirm({
|
||||
content: t('core.module.Confirm Delete Node'),
|
||||
type: 'delete'
|
||||
@@ -182,7 +219,6 @@ const MenuRender = React.memo(function MenuRender({
|
||||
const setNodes = useContextSelector(WorkflowContext, (v) => v.setNodes);
|
||||
const onResetNode = useContextSelector(WorkflowContext, (v) => v.onResetNode);
|
||||
const setEdges = useContextSelector(WorkflowContext, (v) => v.setEdges);
|
||||
const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode);
|
||||
|
||||
const onCopyNode = useCallback(
|
||||
(nodeId: string) => {
|
||||
@@ -236,6 +272,16 @@ const MenuRender = React.memo(function MenuRender({
|
||||
onClick: () => openDebugNode({ entryNodeId: nodeId })
|
||||
}
|
||||
]),
|
||||
...(menuForbid?.copy
|
||||
? []
|
||||
: [
|
||||
{
|
||||
icon: 'copy',
|
||||
label: t('common.Copy'),
|
||||
variant: 'whiteBase',
|
||||
onClick: () => onCopyNode(nodeId)
|
||||
}
|
||||
]),
|
||||
...(flowNodeType === FlowNodeTypeEnum.pluginModule
|
||||
? [
|
||||
{
|
||||
@@ -264,43 +310,7 @@ const MenuRender = React.memo(function MenuRender({
|
||||
}
|
||||
]
|
||||
: []),
|
||||
...(menuForbid?.rename
|
||||
? []
|
||||
: [
|
||||
{
|
||||
icon: 'edit',
|
||||
label: t('common.Rename'),
|
||||
variant: 'whiteBase',
|
||||
onClick: () =>
|
||||
onOpenCustomTitleModal({
|
||||
defaultVal: name,
|
||||
onSuccess: (e) => {
|
||||
if (!e) {
|
||||
return toast({
|
||||
title: t('app.modules.Title is required'),
|
||||
status: 'warning'
|
||||
});
|
||||
}
|
||||
onChangeNode({
|
||||
nodeId,
|
||||
type: 'attr',
|
||||
key: 'name',
|
||||
value: e
|
||||
});
|
||||
}
|
||||
})
|
||||
}
|
||||
]),
|
||||
...(menuForbid?.copy
|
||||
? []
|
||||
: [
|
||||
{
|
||||
icon: 'copy',
|
||||
label: t('common.Copy'),
|
||||
variant: 'whiteBase',
|
||||
onClick: () => onCopyNode(nodeId)
|
||||
}
|
||||
]),
|
||||
|
||||
...(menuForbid?.delete
|
||||
? []
|
||||
: [
|
||||
@@ -342,7 +352,6 @@ const MenuRender = React.memo(function MenuRender({
|
||||
</Box>
|
||||
))}
|
||||
</Box>
|
||||
<EditTitleModal maxLength={20} />
|
||||
<ConfirmSyncModal />
|
||||
<ConfirmDeleteModal />
|
||||
<DebugInputModal />
|
||||
@@ -352,20 +361,15 @@ const MenuRender = React.memo(function MenuRender({
|
||||
ConfirmDeleteModal,
|
||||
ConfirmSyncModal,
|
||||
DebugInputModal,
|
||||
EditTitleModal,
|
||||
flowNodeType,
|
||||
menuForbid?.copy,
|
||||
menuForbid?.debug,
|
||||
menuForbid?.delete,
|
||||
menuForbid?.rename,
|
||||
name,
|
||||
nodeId,
|
||||
onChangeNode,
|
||||
onCopyNode,
|
||||
onDelNode,
|
||||
onOpenConfirmDeleteNode,
|
||||
onOpenConfirmSync,
|
||||
onOpenCustomTitleModal,
|
||||
onResetNode,
|
||||
openDebugNode,
|
||||
pluginId,
|
||||
@@ -388,7 +392,7 @@ const NodeIntro = React.memo(function NodeIntro({
|
||||
const splitToolInputs = useContextSelector(WorkflowContext, (ctx) => ctx.splitToolInputs);
|
||||
const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode);
|
||||
|
||||
const moduleIsTool = useMemo(() => {
|
||||
const NodeIsTool = useMemo(() => {
|
||||
const { isTool } = splitToolInputs([], nodeId);
|
||||
return isTool;
|
||||
}, [nodeId, splitToolInputs]);
|
||||
@@ -407,7 +411,7 @@ const NodeIntro = React.memo(function NodeIntro({
|
||||
<Box fontSize={'xs'} color={'myGray.600'} flex={'1 0 0'}>
|
||||
{t(intro)}
|
||||
</Box>
|
||||
{moduleIsTool && (
|
||||
{NodeIsTool && (
|
||||
<Button
|
||||
size={'xs'}
|
||||
variant={'whiteBase'}
|
||||
@@ -432,7 +436,7 @@ const NodeIntro = React.memo(function NodeIntro({
|
||||
<EditIntroModal maxLength={500} />
|
||||
</>
|
||||
);
|
||||
}, [EditIntroModal, intro, moduleIsTool, nodeId, onChangeNode, onOpenIntroModal, t]);
|
||||
}, [EditIntroModal, intro, NodeIsTool, nodeId, onChangeNode, onOpenIntroModal, t]);
|
||||
|
||||
return Render;
|
||||
});
|
||||
|
@@ -413,7 +413,7 @@ const WorkflowContextProvider = ({
|
||||
});
|
||||
|
||||
/* If the module is connected by a tool, the tool input and the normal input are separated */
|
||||
const splitToolInputs = useMemoizedFn((inputs: FlowNodeInputItemType[], nodeId: string) => {
|
||||
const splitToolInputs = (inputs: FlowNodeInputItemType[], nodeId: string) => {
|
||||
const isTool = !!edges.find(
|
||||
(edge) => edge.targetHandle === NodeOutputKeyEnum.selectedTools && edge.target === nodeId
|
||||
);
|
||||
@@ -426,7 +426,7 @@ const WorkflowContextProvider = ({
|
||||
return !item.toolDescription;
|
||||
})
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
const initData = useMemoizedFn(
|
||||
async (e: { nodes: StoreNodeItemType[]; edges: StoreEdgeItemType[] }) => {
|
||||
|
@@ -232,6 +232,8 @@ const RenderHeaderContainer = React.memo(function RenderHeaderContainer({
|
||||
|
||||
<Box flex={1} />
|
||||
|
||||
{!isShowVersionHistories && (
|
||||
<>
|
||||
<MyMenu
|
||||
Button={
|
||||
<IconButton
|
||||
@@ -265,6 +267,8 @@ const RenderHeaderContainer = React.memo(function RenderHeaderContainer({
|
||||
variant={'whitePrimary'}
|
||||
onClick={() => setIsShowVersionHistories(true)}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
<Button
|
||||
size={'sm'}
|
||||
|
Reference in New Issue
Block a user