From 839a9467c3b7694074321d233e588c0e8a31355c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E8=B0=A8=E6=AC=A3?=
Date: Wed, 4 Sep 2024 17:57:44 +0800
Subject: [PATCH 01/16] chore: Update AddFlowVariableModal component to include
parameter management
---
web/client/api/flow/index.ts | 10 +-
.../canvas-modal/add-flow-variable-modal.tsx | 224 +++++++++++++-----
.../flow/canvas-modal/import-flow-modal.tsx | 2 +-
.../flow/canvas-modal/save-flow-modal.tsx | 4 +
web/types/flow.ts | 21 ++
5 files changed, 206 insertions(+), 55 deletions(-)
diff --git a/web/client/api/flow/index.ts b/web/client/api/flow/index.ts
index 67c6462e4..c50d51a94 100644
--- a/web/client/api/flow/index.ts
+++ b/web/client/api/flow/index.ts
@@ -6,6 +6,7 @@ import {
IFlowRefreshParams,
IFlowResponse,
IFlowUpdateParam,
+ IFlowVariablesParams,
IUploadFileRequestParams,
IUploadFileResponse,
} from '@/types/flow';
@@ -63,7 +64,6 @@ export const downloadFile = (fileId: string) => {
return GET(`/api/v2/serve/file/files/dbgpt/${fileId}`);
};
-// TODO:wait for interface update
export const getFlowTemplateList = () => {
return GET>('/api/v2/serve/awel/flow/templates');
};
@@ -71,3 +71,11 @@ export const getFlowTemplateList = () => {
export const getFlowTemplateById = (id: string) => {
return GET(`/api/v2/serve/awel/flow/templates/${id}`);
};
+
+export const getKeys = () => {
+ return GET>('/api/v2/serve/awel/variables/keys');
+};
+
+export const getVariablesByKey = ({ key, scope }: { key: string; scope: string }) => {
+ return GET('/api/v2/serve/awel/variables', { key, scope });
+};
diff --git a/web/components/flow/canvas-modal/add-flow-variable-modal.tsx b/web/components/flow/canvas-modal/add-flow-variable-modal.tsx
index 961ec76f7..94c65aa2a 100644
--- a/web/components/flow/canvas-modal/add-flow-variable-modal.tsx
+++ b/web/components/flow/canvas-modal/add-flow-variable-modal.tsx
@@ -1,64 +1,131 @@
-// import { IFlowNode } from '@/types/flow';
+import { apiInterceptors, getKeys, getVariablesByKey } from '@/client/api';
+import { IVariableInfo } from '@/types/flow';
import { MinusCircleOutlined, PlusOutlined } from '@ant-design/icons';
-import { Button, Form, Input, Modal, Select, Space } from 'antd';
-import React, { useState } from 'react';
+import { Button, Cascader, Form, Input, Modal, Select, Space } from 'antd';
+import { uniqBy } from 'lodash';
+import React, { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
-// ype GroupType = { category: string; categoryLabel: string; nodes: IFlowNode[] };
-type ValueType = 'str' | 'int' | 'float' | 'bool' | 'ref';
-
const { Option } = Select;
+type ValueType = 'str' | 'int' | 'float' | 'bool' | 'ref';
+interface Option {
+ value?: string | number | null;
+ label: React.ReactNode;
+ children?: Option[];
+ isLeaf?: boolean;
+}
+
+interface VariableDict {
+ key: string;
+ name?: string;
+ scope?: string;
+ scope_key?: string;
+ sys_code?: string;
+ user_name?: string;
+}
+
const DAG_PARAM_KEY = 'dbgpt.core.flow.params';
const DAG_PARAM_SCOPE = 'flow_priv';
-export const AddFlowVariableModal: React.FC = () => {
- const { t } = useTranslation();
- // const [operators, setOperators] = useState>([]);
- // const [resources, setResources] = useState>([]);
- // const [operatorsGroup, setOperatorsGroup] = useState([]);
- // const [resourcesGroup, setResourcesGroup] = useState([]);
- const [isModalOpen, setIsModalOpen] = useState(false);
- const [form] = Form.useForm(); // const [form] = Form.useForm();
+function escapeVariable(value: string, enableEscape: boolean): string {
+ if (!enableEscape) {
+ return value;
+ }
+ return value.replace(/@/g, '\\@').replace(/#/g, '\\#').replace(/%/g, '\\%').replace(/:/g, '\\:');
+}
- const showModal = () => {
- setIsModalOpen(true);
+function buildVariableString(variableDict) {
+ const scopeSig = '@';
+ const sysCodeSig = '#';
+ const userSig = '%';
+ const kvSig = ':';
+ const enableEscape = true;
+
+ const specialChars = new Set([scopeSig, sysCodeSig, userSig, kvSig]);
+
+ // Replace undefined or null with ""
+ const newVariableDict: VariableDict = {
+ key: variableDict.key || '',
+ name: variableDict.name || '',
+ scope: variableDict.scope || '',
+ scope_key: variableDict.scope_key || '',
+ sys_code: variableDict.sys_code || '',
+ user_name: variableDict.user_name || '',
};
- // TODO: get keys
- // useEffect(() => {
- // getNodes();
- // }, []);
+ // Check for special characters in values
+ for (const [key, value] of Object.entries(newVariableDict)) {
+ if (value && [...specialChars].some(char => value.includes(char))) {
+ if (enableEscape) {
+ newVariableDict[key as keyof VariableDict] = escapeVariable(value, enableEscape);
+ } else {
+ throw new Error(
+ `${key} contains special characters, error value: ${value}, special characters: ${[...specialChars].join(', ')}`,
+ );
+ }
+ }
+ }
- // async function getNodes() {
- // const [_, data] = await apiInterceptors(getFlowNodes());
- // if (data && data.length > 0) {
- // localStorage.setItem(FLOW_NODES_KEY, JSON.stringify(data));
- // const operatorNodes = data.filter(node => node.flow_type === 'operator');
- // const resourceNodes = data.filter(node => node.flow_type === 'resource');
- // setOperators(operatorNodes);
- // setResources(resourceNodes);
- // setOperatorsGroup(groupNodes(operatorNodes));
- // setResourcesGroup(groupNodes(resourceNodes));
- // }
- // }
+ const { key, name, scope, scope_key, sys_code, user_name } = newVariableDict;
- // function groupNodes(data: IFlowNode[]) {
- // const groups: GroupType[] = [];
- // const categoryMap: Record = {};
- // data.forEach(item => {
- // const { category, category_label } = item;
- // if (!categoryMap[category]) {
- // categoryMap[category] = { category, categoryLabel: category_label, nodes: [] };
- // groups.push(categoryMap[category]);
- // }
- // categoryMap[category].nodes.push(item);
- // });
- // return groups;
- // }
+ let variableStr = `${key}`;
+
+ if (name) {
+ variableStr += `${kvSig}${name}`;
+ }
+
+ if (scope) {
+ variableStr += `${scopeSig}${scope}`;
+ if (scope_key) {
+ variableStr += `${kvSig}${scope_key}`;
+ }
+ }
+
+ if (sys_code) {
+ variableStr += `${sysCodeSig}${sys_code}`;
+ }
+
+ if (user_name) {
+ variableStr += `${userSig}${user_name}`;
+ }
+
+ return `\${${variableStr}}`;
+}
+
+export const AddFlowVariableModal: React.FC = () => {
+ const { t } = useTranslation();
+ const [isModalOpen, setIsModalOpen] = useState(false);
+ const [form] = Form.useForm();
+ const [controlTypes, setControlTypes] = useState(['str']);
+ const [refVariableOptions, setRefVariableOptions] = useState
-
- {isAllNodesVisible ? t('All_Nodes') : t('Higher_Order_Nodes')}
-
+
diff --git a/web/locales/en/flow.ts b/web/locales/en/flow.ts
index 27b388e74..11b86a39a 100644
--- a/web/locales/en/flow.ts
+++ b/web/locales/en/flow.ts
@@ -19,6 +19,6 @@ export const FlowEn = {
Please_Add_Nodes_First: 'Please add nodes first',
Add_Global_Variable_of_Flow: 'Add global variable of flow',
Add_Parameter: 'Add Parameter',
- Higher_Order_Nodes: 'Higher Order Nodes',
- All_Nodes: 'All Nodes',
+ Higher_Order_Nodes: 'Higher Order',
+ All_Nodes: 'All',
};
diff --git a/web/locales/zh/flow.ts b/web/locales/zh/flow.ts
index f846059c4..462c429fa 100644
--- a/web/locales/zh/flow.ts
+++ b/web/locales/zh/flow.ts
@@ -19,6 +19,6 @@ export const FlowZn = {
Please_Add_Nodes_First: '请先添加节点',
Add_Global_Variable_of_Flow: '添加 Flow 全局变量',
Add_Parameter: '添加参数',
- Higher_Order_Nodes: '高阶节点',
- All_Nodes: '所有节点',
+ Higher_Order_Nodes: '高阶',
+ All_Nodes: '所有',
};
diff --git a/web/pages/mobile/chat/components/InputContainer.tsx b/web/pages/mobile/chat/components/InputContainer.tsx
index f9af2ea3e..59a545684 100644
--- a/web/pages/mobile/chat/components/InputContainer.tsx
+++ b/web/pages/mobile/chat/components/InputContainer.tsx
@@ -6,7 +6,7 @@ import { ClearOutlined, LoadingOutlined, PauseCircleOutlined, RedoOutlined, Send
import { EventStreamContentType, fetchEventSource } from '@microsoft/fetch-event-source';
import { useRequest } from 'ahooks';
import { Button, Input, Popover, Spin, Tag } from 'antd';
-import cls from 'classnames';
+import classnames from 'classnames';
import { useSearchParams } from 'next/navigation';
import React, { useContext, useEffect, useMemo, useState } from 'react';
import { MobileChatContext } from '../';
@@ -245,7 +245,7 @@ const InputContainer: React.FC = () => {
{/* 输入框 */}
{
-
- }
- onClick={handleCreate}
- >
- {t('create_app')}
-
-
+
+ }
+ onClick={handleCreate}
+ >
+ {t('create_app')}
+
{apps.map(item => {
From ef0443290263d2f5f3f57807b0fc8e475a37c7b2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E8=B0=A8=E6=AC=A3?=
Date: Wed, 4 Sep 2024 23:46:05 +0800
Subject: [PATCH 09/16] fix: set flow variable default value
---
.../flow/canvas-modal/add-flow-variable-modal.tsx | 10 +++++++---
web/pages/construct/flow/canvas/index.tsx | 15 +++++++--------
2 files changed, 14 insertions(+), 11 deletions(-)
diff --git a/web/components/flow/canvas-modal/add-flow-variable-modal.tsx b/web/components/flow/canvas-modal/add-flow-variable-modal.tsx
index d246beb14..6be1c6982 100644
--- a/web/components/flow/canvas-modal/add-flow-variable-modal.tsx
+++ b/web/components/flow/canvas-modal/add-flow-variable-modal.tsx
@@ -1,5 +1,5 @@
import { apiInterceptors, getKeys, getVariablesByKey } from '@/client/api';
-import { IGetKeysResponseData, IVariableItem } from '@/types/flow';
+import { IFlowUpdateParam, IGetKeysResponseData, IVariableItem } from '@/types/flow';
import { buildVariableString } from '@/utils/flow';
import { MinusCircleOutlined, PlusOutlined } from '@ant-design/icons';
import { Button, Cascader, Form, Input, Modal, Select, Space } from 'antd';
@@ -17,7 +17,11 @@ interface Option {
isLeaf?: boolean;
}
-export const AddFlowVariableModal: React.FC = () => {
+type Props = {
+ flowInfo?: IFlowUpdateParam;
+};
+
+export const AddFlowVariableModal: React.FC = ({ flowInfo }) => {
const { t } = useTranslation();
const [isModalOpen, setIsModalOpen] = useState(false);
const [form] = Form.useForm();
@@ -155,7 +159,7 @@ export const AddFlowVariableModal: React.FC = () => {
autoComplete='off'
layout='vertical'
className='mt-8'
- initialValues={{ parameters: [{}] }}
+ initialValues={{ parameters: flowInfo?.variables || [{}] }}
>
{(fields, { add, remove }) => (
diff --git a/web/pages/construct/flow/canvas/index.tsx b/web/pages/construct/flow/canvas/index.tsx
index b3d0889a5..c3c91e515 100644
--- a/web/pages/construct/flow/canvas/index.tsx
+++ b/web/pages/construct/flow/canvas/index.tsx
@@ -26,14 +26,13 @@ import 'reactflow/dist/style.css';
const nodeTypes = { customNode: CanvasNode };
const edgeTypes = { buttonedge: ButtonEdge };
+
const Canvas: React.FC = () => {
-
const { t } = useTranslation();
- const [messageApi, contextHolder] = message.useMessage();
-
const searchParams = useSearchParams();
const id = searchParams?.get('id') || '';
const reactFlow = useReactFlow();
+ const [messageApi, contextHolder] = message.useMessage();
const [loading, setLoading] = useState(false);
const [nodes, setNodes, onNodesChange] = useNodesState([]);
@@ -43,10 +42,10 @@ const Canvas: React.FC = () => {
const [isSaveFlowModalOpen, setIsSaveFlowModalOpen] = useState(false);
const [isExportFlowModalOpen, setIsExportFlowModalOpen] = useState(false);
const [isImportModalOpen, setIsImportFlowModalOpen] = useState(false);
-
- if (localStorage.getItem('importFlowData') ) {
- const importFlowData = JSON.parse(localStorage.getItem('importFlowData') );
- localStorage.removeItem('importFlowData')
+
+ if (localStorage.getItem('importFlowData')) {
+ const importFlowData = JSON.parse(localStorage.getItem('importFlowData') || '');
+ localStorage.removeItem('importFlowData');
setLoading(true);
const flowData = mapUnderlineToHump(importFlowData.flow_data);
setFlowInfo(importFlowData);
@@ -260,7 +259,7 @@ const Canvas: React.FC = () => {
-
+
From 5617385b881a007ec47930116d129cab93db1155 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E8=B0=A8=E6=AC=A3?=
Date: Wed, 4 Sep 2024 23:57:59 +0800
Subject: [PATCH 10/16] feat: Add helper function to render control component
in AddFlowVariableModal
---
.../canvas-modal/add-flow-variable-modal.tsx | 45 +++++++++++++------
1 file changed, 32 insertions(+), 13 deletions(-)
diff --git a/web/components/flow/canvas-modal/add-flow-variable-modal.tsx b/web/components/flow/canvas-modal/add-flow-variable-modal.tsx
index 6be1c6982..5105acb94 100644
--- a/web/components/flow/canvas-modal/add-flow-variable-modal.tsx
+++ b/web/components/flow/canvas-modal/add-flow-variable-modal.tsx
@@ -126,6 +126,37 @@ export const AddFlowVariableModal: React.FC = ({ flowInfo }) => {
}
};
+ // Helper function to render the appropriate control component
+ const renderVariableValue = (type: string, index: number) => {
+ switch (type) {
+ case 'ref':
+ return (
+ onRefTypeValueChange(value, selectedOptions, index)}
+ changeOnSelect
+ />
+ );
+ case 'str':
+ return ;
+ case 'int':
+ return ;
+ case 'float':
+ return ;
+ case 'bool':
+ return (
+
+ );
+ default:
+ return ;
+ }
+ };
+
return (
<>