Skip to content

Commit

Permalink
perf: workflow snapshots;fix: extrat node selected (#3334)
Browse files Browse the repository at this point in the history
* doc

* perf: workflow snapshots

* fix: extrat node selected

* refresh page reset snapshot
  • Loading branch information
c121914yu authored Dec 6, 2024
1 parent 0c308fc commit 90d7d2a
Show file tree
Hide file tree
Showing 27 changed files with 447 additions and 524 deletions.
Binary file added docSite/assets/imgs/image-20.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docSite/assets/imgs/image-21.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docSite/assets/imgs/image-22.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docSite/assets/imgs/image-23.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docSite/assets/imgs/image-24.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
23 changes: 22 additions & 1 deletion docSite/content/zh-cn/docs/development/upgrading/4815.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,26 @@ toc: true
weight: 809
---

## 新功能预览

### API 知识库

| | |
| --- | --- |
| ![alt text](/imgs/image-20.png) | ![alt text](/imgs/image-21.png) |

### HTML 渲染

| 源码模式 | 预览模式 | 全屏模式 |
| --- | --- | --- |
| ![alt text](/imgs/image-22.png) | ![alt text](/imgs/image-23.png) | ![alt text](/imgs/image-24.png) |

## 升级指南

- 更新 FastGPT 镜像 tag: v4.8.15-alpha
- 更新 FastGPT 商业版镜像 tag: v4.8.15-alpha (fastgpt-pro镜像)
- Sandbox 镜像,可以不更新


## 完整更新内容

Expand All @@ -24,4 +44,5 @@ weight: 809
12. 修复 - 分享链接点赞鉴权问题。
13. 修复 - 对话页面切换自动执行应用时,会误触发非自动执行应用。
14. 修复 - 语言播放鉴权问题。
15. 修复 - 插件应用知识库引用上限始终为 3000
15. 修复 - 插件应用知识库引用上限始终为 3000
16. 修复 - 工作流编辑记录存储上限,去掉本地存储,增加异常离开时,强制自动保存。
2 changes: 2 additions & 0 deletions packages/web/i18n/en/app.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"auto_execute": "Automatic execution",
"auto_execute_default_prompt_placeholder": "Default questions sent when executing automatically",
"auto_execute_tip": "After turning it on, the workflow will be automatically triggered when the user enters the conversation interface. \nExecution order: 1. Dialogue starter; 2. Global variables; 3. Automatic execution.",
"auto_save": "Auto save",
"chat_debug": "Chat Preview",
"chat_logs": "Conversation Logs",
"chat_logs_tips": "Logs will record the online, shared, and API (requires chatId) conversation records of this app.",
Expand Down Expand Up @@ -135,6 +136,7 @@
"type.Plugin": "Plugin",
"type.Simple bot": "Simple App",
"type.Workflow bot": "Workflow",
"unusual_leave_auto_save": "Abnormal exit, automatic saving",
"upload_file_max_amount": "Maximum File Quantity",
"upload_file_max_amount_tip": "Maximum number of files uploaded in a single round of conversation",
"variable.select type_desc": "You can define a global variable that does not need to be filled in by the user.\n\nThe value of this variable can come from the API interface, the Query of the shared link, or assigned through the [Variable Update] module.",
Expand Down
2 changes: 2 additions & 0 deletions packages/web/i18n/zh-CN/app.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"auto_execute": "自动执行",
"auto_execute_default_prompt_placeholder": "自动执行时,发送的默认问题",
"auto_execute_tip": "开启后,用户进入对话界面将自动触发工作流。执行顺序:1、对话开场白;2、全局变量;3、自动执行。",
"auto_save": "自动保存",
"chat_debug": "调试预览",
"chat_logs": "对话日志",
"chat_logs_tips": "日志会记录该应用的在线、分享和 API(需填写 chatId)对话记录",
Expand Down Expand Up @@ -135,6 +136,7 @@
"type.Plugin": "插件",
"type.Simple bot": "简易应用",
"type.Workflow bot": "工作流",
"unusual_leave_auto_save": "异常离开,自动保存",
"upload_file_max_amount": "最大文件数量",
"upload_file_max_amount_tip": "单轮对话中最大上传文件数量",
"variable.select type_desc": "可以为工作流定义全局变量,常用临时缓存。赋值的方式包括:\n1. 从对话页面的 query 参数获取。\n2. 通过 API 的 variables 对象传递。\n3. 通过【变量更新】节点进行赋值。",
Expand Down
2 changes: 2 additions & 0 deletions packages/web/i18n/zh-Hant/app.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"auto_execute": "自動執行",
"auto_execute_default_prompt_placeholder": "自動執行時,發送的預設問題",
"auto_execute_tip": "開啟後,使用者進入對話式介面將自動觸發工作流程。\n執行順序:1、對話開場白;2、全域變數;3、自動執行。",
"auto_save": "自動儲存",
"chat_debug": "聊天預覽",
"chat_logs": "對話紀錄",
"chat_logs_tips": "紀錄會記錄此應用程式的線上、分享和 API(需填寫 chatId)對話紀錄",
Expand Down Expand Up @@ -135,6 +136,7 @@
"type.Plugin": "外掛",
"type.Simple bot": "簡易應用程式",
"type.Workflow bot": "工作流程",
"unusual_leave_auto_save": "異常離開,自動儲存",
"upload_file_max_amount": "最大檔案數量",
"upload_file_max_amount_tip": "單輪對話中最大上傳檔案數量",
"variable.select type_desc": "可以為工作流程定義全域變數,常用於暫存。賦值的方式包括:\n1. 從對話頁面的 query 參數取得。\n2. 透過 API 的 variables 物件傳遞。\n3. 透過【變數更新】節點進行賦值。",
Expand Down
2 changes: 1 addition & 1 deletion projects/app/src/components/core/app/AutoExecConfig.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { defaultAutoExecuteConfig } from '@fastgpt/global/core/app/constants';
import { AppAutoExecuteConfigType } from '@fastgpt/global/core/app/type';
import MyIcon from '@fastgpt/web/components/common/Icon';
import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
import { useTranslation } from 'react-i18next';
import { useTranslation } from 'next-i18next';
import ChatFunctionTip from './Tip';
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
import MyModal from '@fastgpt/web/components/common/MyModal';
Expand Down
92 changes: 27 additions & 65 deletions projects/app/src/pages/app/detail/components/Plugin/Header.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useCallback, useMemo, useState } from 'react';
import React, { useCallback, useMemo } from 'react';
import {
Box,
Flex,
Expand All @@ -22,19 +22,13 @@ import AppCard from '../WorkflowComponents/AppCard';
import { useSystem } from '@fastgpt/web/hooks/useSystem';
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
import MyModal from '@fastgpt/web/components/common/MyModal';
import { compareSnapshot } from '@/web/core/workflow/utils';
import { formatTime2YMDHMS } from '@fastgpt/global/common/string/time';
import { useToast } from '@fastgpt/web/hooks/useToast';
import { useDebounceEffect } from 'ahooks';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import SaveButton from '../Workflow/components/SaveButton';
import PublishHistories from '../PublishHistoriesSlider';
import {
WorkflowNodeEdgeContext,
WorkflowInitContext
} from '../WorkflowComponents/context/workflowInitContext';
import { WorkflowEventContext } from '../WorkflowComponents/context/workflowEventContext';
import { getAppConfigByDiff } from '@/web/core/app/diff';
import { WorkflowStatusContext } from '../WorkflowComponents/context/workflowStatusContext';

const Header = () => {
const { t } = useTranslation();
Expand All @@ -50,17 +44,13 @@ const Header = () => {
onClose: onCloseBackConfirm
} = useDisclosure();

const nodes = useContextSelector(WorkflowInitContext, (v) => v.nodes);
const edges = useContextSelector(WorkflowNodeEdgeContext, (v) => v.edges);

const flowData2StoreData = useContextSelector(WorkflowContext, (v) => v.flowData2StoreData);
const flowData2StoreDataAndCheck = useContextSelector(
WorkflowContext,
(v) => v.flowData2StoreDataAndCheck
);
const setWorkflowTestData = useContextSelector(WorkflowContext, (v) => v.setWorkflowTestData);
const past = useContextSelector(WorkflowContext, (v) => v.past);
const future = useContextSelector(WorkflowContext, (v) => v.future);
const setPast = useContextSelector(WorkflowContext, (v) => v.setPast);
const onSwitchTmpVersion = useContextSelector(WorkflowContext, (v) => v.onSwitchTmpVersion);
const onSwitchCloudVersion = useContextSelector(WorkflowContext, (v) => v.onSwitchCloudVersion);
Expand All @@ -71,39 +61,10 @@ const Header = () => {
(v) => v.setShowHistoryModal
);

const { lastAppListRouteType } = useSystemStore();

const [isPublished, setIsPublished] = useState(false);
useDebounceEffect(
() => {
const savedSnapshot =
[...future].reverse().find((snapshot) => snapshot.isSaved) ||
past.find((snapshot) => snapshot.isSaved);

const initialState = past[past.length - 1]?.state;
const savedSnapshotState = getAppConfigByDiff(initialState, savedSnapshot?.diff);
const isSaved = useContextSelector(WorkflowStatusContext, (v) => v.isSaved);
const leaveSaveSign = useContextSelector(WorkflowStatusContext, (v) => v.leaveSaveSign);

const val = compareSnapshot(
// nodes of the saved snapshot
{
nodes: savedSnapshotState?.nodes,
edges: savedSnapshotState?.edges,
chatConfig: savedSnapshotState?.chatConfig
},
// nodes of the current canvas
{
nodes,
edges,
chatConfig: appDetail.chatConfig
}
);
setIsPublished(val);
},
[future, past, nodes, edges, appDetail.chatConfig],
{
wait: 500
}
);
const { lastAppListRouteType } = useSystemStore();

const { runAsync: onClickSave, loading } = useRequest2(
async ({
Expand Down Expand Up @@ -140,16 +101,15 @@ const Header = () => {
);

const onBack = useCallback(async () => {
try {
router.push({
pathname: '/app/list',
query: {
parentId: appDetail.parentId,
type: lastAppListRouteType
}
});
} catch (error) {}
}, [appDetail.parentId, lastAppListRouteType, router]);
leaveSaveSign.current = false;
router.push({
pathname: '/app/list',
query: {
parentId: appDetail.parentId,
type: lastAppListRouteType
}
});
}, [appDetail.parentId, lastAppListRouteType, leaveSaveSign, router]);

const Render = useMemo(() => {
return (
Expand Down Expand Up @@ -189,13 +149,13 @@ const Header = () => {
name={'common/leftArrowLight'}
w={6}
cursor={'pointer'}
onClick={isPublished ? onBack : onOpenBackConfirm}
onClick={isSaved ? onBack : onOpenBackConfirm}
/>
</Box>

{/* app info */}
<Box ml={1}>
<AppCard isPublished={isPublished} showSaveStatus={isV2Workflow} />
<AppCard isSaved={isSaved} showSaveStatus={isV2Workflow} />
</Box>

{isPc && (
Expand Down Expand Up @@ -247,7 +207,7 @@ const Header = () => {
}, [
isPc,
currentTab,
isPublished,
isSaved,
onBack,
onOpenBackConfirm,
isV2Workflow,
Expand Down Expand Up @@ -290,14 +250,16 @@ const Header = () => {
<Button
isLoading={loading}
onClick={async () => {
await onClickSave({});
onCloseBackConfirm();
onBack();
toast({
status: 'success',
title: t('app:saved_success'),
position: 'top-right'
});
try {
await onClickSave({});
onCloseBackConfirm();
onBack();
toast({
status: 'success',
title: t('app:saved_success'),
position: 'top-right'
});
} catch (error) {}
}}
>
{t('common:common.Save_and_exit')}
Expand Down
107 changes: 2 additions & 105 deletions projects/app/src/pages/app/detail/components/SimpleApp/Edit.tsx
Original file line number Diff line number Diff line change
@@ -1,129 +1,26 @@
import React from 'react';
import { Box } from '@chakra-ui/react';
import { useDebounceEffect, useLocalStorageState, useMount } from 'ahooks';
import { useDatasetStore } from '@/web/core/dataset/store/dataset';
import { appWorkflow2Form } from '@fastgpt/global/core/app/utils';

import ChatTest from './ChatTest';
import AppCard from './AppCard';
import EditForm from './EditForm';
import { AppSimpleEditFormType } from '@fastgpt/global/core/app/type';
import { v1Workflow2V2 } from '@/web/core/workflow/adapt';
import { AppContext } from '@/pages/app/detail/components/context';
import { useContextSelector } from 'use-context-selector';
import { cardStyles } from '../constants';

import styles from './styles.module.scss';
import { useSystem } from '@fastgpt/web/hooks/useSystem';
import { useTranslation } from 'next-i18next';
import { onSaveSnapshotFnType, SimpleAppSnapshotType } from './useSnapshots';
import { getAppConfigByDiff, getAppDiffConfig } from '@/web/core/app/diff';
import { formatTime2YMDHMS } from '@fastgpt/global/common/string/time';

const convertOldFormatHistory = (past: SimpleAppSnapshotType[]) => {
const baseState = past[past.length - 1].appForm;

return past.map((item, index) => {
if (index === past.length - 1) {
return {
title: item.title,
isSaved: item.isSaved,
state: baseState
};
}

const currentState = item.appForm;

const diff = getAppDiffConfig(baseState, currentState);

return {
title: item.title || formatTime2YMDHMS(new Date()),
isSaved: item.isSaved,
diff
};
});
};
import { SimpleAppSnapshotType } from './useSnapshots';

const Edit = ({
appForm,
setAppForm,
past,
setPast,
saveSnapshot
setPast
}: {
appForm: AppSimpleEditFormType;
setAppForm: React.Dispatch<React.SetStateAction<AppSimpleEditFormType>>;
past: SimpleAppSnapshotType[];
setPast: (value: React.SetStateAction<SimpleAppSnapshotType[]>) => void;
saveSnapshot: onSaveSnapshotFnType;
}) => {
const { isPc } = useSystem();
const { loadAllDatasets } = useDatasetStore();
const { appDetail } = useContextSelector(AppContext, (v) => v);
const { t } = useTranslation();

// Reset old edit history to new variables
const [oldPast, setOldPast] = useLocalStorageState<SimpleAppSnapshotType[]>(
`${appDetail._id}-past-simple`,
{}
);

// Save snapshot to local
useDebounceEffect(
() => {
saveSnapshot({
appForm
});
},
[appForm],
{ wait: 500 }
);

// Init app form
useMount(() => {
// show selected dataset
loadAllDatasets();

if (appDetail.version !== 'v2') {
return setAppForm(
appWorkflow2Form({
nodes: v1Workflow2V2((appDetail.modules || []) as any)?.nodes,
chatConfig: appDetail.chatConfig
})
);
}

// Get the latest snapshot
if (past?.[0]?.diff) {
const pastState = getAppConfigByDiff(past[past.length - 1].state, past[0].diff);

return setAppForm(pastState);
} else if (oldPast && oldPast.length > 0 && oldPast?.every((item) => item.appForm)) {
// 格式化成 diff
const newPast = convertOldFormatHistory(oldPast);

setPast(newPast);
setOldPast && setOldPast([]);

return setAppForm(getAppConfigByDiff(newPast[newPast.length - 1].state, newPast[0].diff));
}

const appForm = appWorkflow2Form({
nodes: appDetail.modules,
chatConfig: appDetail.chatConfig
});

// Set the first snapshot
if (past.length === 0) {
saveSnapshot({
appForm,
title: t('app:initial_form'),
isSaved: true
});
}

setAppForm(appForm);
});

return (
<Box
Expand Down
Loading

0 comments on commit 90d7d2a

Please sign in to comment.