diff --git a/agent-flow/.eslintrc.cjs b/agent-flow/.eslintrc.cjs new file mode 100644 index 0000000000..3e212e1d43 --- /dev/null +++ b/agent-flow/.eslintrc.cjs @@ -0,0 +1,21 @@ +module.exports = { + root: true, + env: { browser: true, es2020: true }, + extends: [ + 'eslint:recommended', + 'plugin:react/recommended', + 'plugin:react/jsx-runtime', + 'plugin:react-hooks/recommended', + ], + ignorePatterns: ['dist', '.eslintrc.cjs'], + parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, + settings: { react: { version: '18.2' } }, + plugins: ['react-refresh'], + rules: { + 'react/jsx-no-target-blank': 'off', + 'react-refresh/only-export-components': [ + 'warn', + { allowConstantExport: true }, + ], + }, +} diff --git a/agent-flow/.gitignore b/agent-flow/.gitignore new file mode 100644 index 0000000000..2a170a8d36 --- /dev/null +++ b/agent-flow/.gitignore @@ -0,0 +1,3 @@ +package-lock.json +node_modules/ +build/ diff --git a/agent-flow/.npmrc b/agent-flow/.npmrc new file mode 100644 index 0000000000..1130e2a50c --- /dev/null +++ b/agent-flow/.npmrc @@ -0,0 +1,3 @@ +strict-ssl=false +package-lock=true # 启用lockfiles +registry=https://registry.npmjs.org/ diff --git a/agent-flow/README.md b/agent-flow/README.md new file mode 100644 index 0000000000..065631e5c8 --- /dev/null +++ b/agent-flow/README.md @@ -0,0 +1,154 @@ +# @fit-elsa/agent-flow + +## 简介 + +@fit-elsa/agent-flow 是基于React的前端应用模块,为 @fit-elsa/elsa 核心框架提供React封装和UI组件。 + +## 功能亮点 + +### 集成React能力 +- 基于Context的上下文传递 +- 节点渲染缓存:React.memo + 自定义shouldComponentUpdate + +### 集成Ant Design能力 +- 基于Form组件的实时校验提示系统 +- 基于Tree组件封装的节点上下文观察者机制 + +## 安装 + +```bash +npm install @fit-elsa/agent-flow @fit-elsa/elsa +``` + +## 快速开始 + +### 基本用法 + +```jsx +import React from 'react'; +import { JadeFlow, createGraphOperator } from '@fit-elsa/agent-flow'; + +function MyFlowApp() { + // 创建图形操作器 + const graphOperator = createGraphOperator(); + + // 处理图形数据变更 + const handleGraphChange = (graphData) => { + console.log('图形数据已变更:', graphData); + }; + + return ( +
+ +
+ ); +} + +export default MyFlowApp; +``` + +### 多会话示例 + +```jsx +import React, { useState } from 'react'; +import { MultiConversation, MultiConversationContent } from '@fit-elsa/agent-flow'; + +function MultiConvApp() { + const [selectedConversation, setSelectedConversation] = useState(null); + + const conversations = [ + { id: '1', title: '会话 1', content: '这是第一个会话的内容' }, + { id: '2', title: '会话 2', content: '这是第二个会话的内容' } + ]; + + return ( +
+
+ +
+
+ {selectedConversation && ( + + )} +
+
+ ); +} + +export default MultiConvApp; +``` + +## 组件API + +### JadeFlow + +主要的流程图组件,用于显示和编辑流程图。 + +**属性**: +- `graphOperator`: 图形操作器实例 +- `onGraphChange`: 图形数据变更时的回调函数 +- `height`: 流程图高度 +- `width`: 流程图宽度 +- `readOnly`: 是否只读模式 +- `locale`: 语言设置 + +### MultiConversation + +多会话列表组件,用于显示和选择多个会话。 + +**属性**: +- `conversations`: 会话列表数据 +- `onSelectConversation`: 选择会话时的回调函数 +- `selectedConversationId`: 当前选中的会话ID + +### MultiConversationContent + +会话内容组件,用于显示选中会话的详细内容。 + +**属性**: +- `conversation`: 会话对象数据 + +### createGraphOperator + +创建图形操作器的函数,用于管理图形数据和操作。 + +## 开发 + +### 构建命令 + +```bash +# 安装依赖 +npm install + +# 启动开发服务器 +npm run dev + +# 构建生产版本 +npm run build + +# 预览生产版本 +npm run preview +``` + +## 许可证 + +MIT License + +## 版权 + +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ \ No newline at end of file diff --git a/agent-flow/docs/features/llm-tool-detail-view.md b/agent-flow/docs/features/llm-tool-detail-view.md new file mode 100644 index 0000000000..b1a4efc656 --- /dev/null +++ b/agent-flow/docs/features/llm-tool-detail-view.md @@ -0,0 +1,173 @@ +# 大模型节点工具详情查看功能 + +## 功能概述 + +为大模型节点的工具卡片添加了查看详情功能,用户可以点击眼睛图标跳转到对应工具流的详情页面。 + +## 实现细节 + +### 1. 数据存储 + +在选择工具时,系统会自动提取并存储以下信息: +- **appId**: 从 `selectedData.runnables.APP.appId` 提取 +- **tenantId**: 从 `selectedData.schema.parameters.properties.tenantId.default` 提取 + +这些信息会保存在 `tools` 的 `value` 数组中,每个工具项包含 `appId` 和 `tenantId` 字段。 + +### 2. UI 增强 + +每个工具卡片显示: +- **工具图标** (左侧) +- **工具名称和类型** (中间) +- **版本号** (右侧) +- **眼睛图标** (右侧,仅当存在 appId 和 tenantId 时显示) - 用于查看详情 +- **删除图标** (最右侧) + +眼睛图标按钮特性: +- **仅在工具包含 appId 和 tenantId 时显示** +- 鼠标悬停时显示"工具详情"提示 +- 在禁用状态下不可点击 +- 点击时不会触发父元素的点击事件 + +### 3. 跳转逻辑 + +点击眼睛图标时: +1. 从工具配置中获取 `appId` 和 `tenantId` +2. 从 graph configs 中获取 `endpoint` 配置(如果未配置,使用当前域名) +3. 构建跳转 URL: `{endpoint}/app-develop/{tenantId}/add-flow/${appId}?type=workFlow` +4. 在新标签页中打开目标页面 + +### 4. 配置说明 + +如果需要自定义 endpoint,可以在 configs 中配置: + +```javascript +configs.push({ + node: 'llmNodeState', + urls: { + endpoint: 'https://your-domain.com' + } +}); +``` + +如果未配置 endpoint,系统会自动使用 `window.location.origin`。 + +## 修改文件列表 + +1. **SkillForm.jsx** + - 导入 `EyeOutlined` 图标 + - 在 `onSelect` 中提取 `appId` 和 `tenantId` + - 添加 `handleViewDetails` 函数实现跳转逻辑 + - 添加 `renderViewIcon` 函数渲染眼睛图标(包含条件判断) + - 在工具卡片上显示眼睛图标 + +2. **LlmFormWrapper.jsx** + - 在 `processToolData` 函数中,从 `tool.value` 获取 `appId` 和 `tenantId` + - 将 `appId` 和 `tenantId` 添加到 `toolOptions` 中 + +3. **reducers/reducers.js** + - 更新 `AddSkillReducer` 中的 `newSkill` 对象 + - 将 `appId` 和 `tenantId` 作为新的字段存储 + +## 数据结构 + +大模型节点中的工具数据结构示例: + +```javascript +{ + inputParams: [ + { + name: "tools", + value: [ + { + id: "uuid", + type: "String", + from: "Input", + value: "tool-unique-name", + appId: "app-123", // 新增 + tenantId: "tenant-456", // 新增 + name: "工具名称", // updateTools 时更新 + tags: ["TOOL"], // updateTools 时更新 + version: "1.0.0" // updateTools 时更新 + } + ] + } + ] +} +``` + +## 工作流程 + +1. **用户选择工具**: 用户点击"添加技能"按钮选择工具 +2. **数据提取**: `onSelect` 回调从选择的数据中提取 `appId` 和 `tenantId` +3. **数据存储**: `AddSkillReducer` 将工具信息(包括 `appId` 和 `tenantId`)存储到 config 中 +4. **获取详情**: `getSkillInfo` 函数从后端获取工具的详细信息(名称、标签、版本等) +5. **构建选项**: `processToolData` 函数构建 `toolOptions`,同时保留 `appId` 和 `tenantId` +6. **更新显示**: `updateTools` reducer 更新工具的显示信息 +7. **渲染卡片**: `SkillContent` 组件渲染工具卡片,包括眼睛图标(如果有 `appId` 和 `tenantId`) + +## 注意事项 + +1. **眼睛图标显示条件**: 只有当工具同时包含 `appId` 和 `tenantId` 信息时,才会显示眼睛图标 +2. **老数据兼容**: 对于不包含 `appId` 和 `tenantId` 的老数据,不显示眼睛图标,只显示工具名称、版本和删除功能 +3. 跳转在新标签页中打开,不影响当前编辑状态 +4. 即使没有配置 endpoint,系统也会使用当前域名作为默认值 +5. 工具信息通过 `getSkillInfo` 从后端获取,`appId` 和 `tenantId` 从本地 config 中保留 + +## 兼容性 + +- 不会影响现有功能 +- 对于不包含 `appId` 和 `tenantId` 的旧数据,不显示眼睛图标,工具卡片正常显示所有其他功能 +- 所有更改向后兼容,老数据可以正常使用 +- 与循环节点和并行节点的实现保持一致,使用相同的数据提取逻辑 + +## 与其他节点的对比 + +| 特性 | 循环节点 | 并行节点 | 大模型节点 | +|------|---------|---------|-----------| +| 数据提取 | 相同 | 相同 | 相同 | +| 存储位置 | toolInfo 对象 | plugin.value 数组 | tools.value 数组 | +| 显示位置 | 工具卡片右侧 | 折叠面板 header | 工具卡片右侧 | +| 跳转逻辑 | 相同 | 相同 | 相同 | +| 配置节点名 | loopNodeState | parallelNodeState | llmNodeState | +| 额外处理 | - | - | 需要通过 getSkillInfo 获取工具详情 | + +## 使用示例 + +```javascript +// 用户选择工具时,系统会自动提取数据 +{ + uniqueName: "my-tool", + name: "我的工具", + tags: ["WATER_FLOW"], + version: "1.0.0", + runnables: { + APP: { + appId: "app-123" + } + }, + schema: { + parameters: { + properties: { + tenantId: { + default: "tenant-456" + } + } + } + } +} + +// 存储后的数据结构 +{ + id: "uuid", + type: "String", + from: "Input", + value: "my-tool", + appId: "app-123", + tenantId: "tenant-456" +} + +// 点击眼睛图标后跳转到 +// https://your-domain.com/app-develop/tenant-456/add-flow/app-123?type=workFlow +``` + diff --git a/agent-flow/docs/features/loop-tool-detail-view.md b/agent-flow/docs/features/loop-tool-detail-view.md new file mode 100644 index 0000000000..7434d61d2a --- /dev/null +++ b/agent-flow/docs/features/loop-tool-detail-view.md @@ -0,0 +1,108 @@ +# 循环节点工具详情查看功能 + +## 功能概述 + +为循环节点的工具卡片添加了查看详情功能,用户可以点击眼睛图标跳转到对应工具流的详情页面。 + +## 实现细节 + +### 1. 数据存储 + +在选择工具时,系统会自动提取并存储以下信息: +- **appId**: 从 `selectedData.runnables.APP.appId` 提取 +- **tenantId**: 从 `selectedData.schema.parameters.properties.tenantId.default` 提取 + +这些信息会保存在 `toolInfo` 对象中,随节点配置一起持久化。 + +### 2. UI 增强 + +每个选中的工具卡片显示: +- **工具名称** (左侧) +- **眼睛图标** (中间,仅当存在 appId 和 tenantId 时显示) - 用于查看详情 +- **删除图标** (右侧) + +眼睛图标按钮特性: +- **仅在工具包含 appId 和 tenantId 时显示** +- 鼠标悬停时显示"工具详情"提示 +- 在禁用状态下不可点击 +- 点击时不会触发父元素的点击事件 + +### 3. 跳转逻辑 + +点击眼睛图标时: +1. 从工具配置中获取 `appId` 和 `tenantId` +2. 从 graph configs 中获取 `endpoint` 配置(如果未配置,使用当前域名) +3. 构建跳转 URL: `{endpoint}/app-develop/{tenantId}/add-flow/{appId}?type=workFlow` +4. 在新标签页中打开目标页面 +5. 触发 `VIEW_TOOL_DETAIL` 事件(供外部监听) + +### 4. 配置说明 + +如果需要自定义 endpoint,可以在 configs 中配置: + +```javascript +configs.push({ + node: 'loopNodeState', + urls: { + endpoint: 'https://your-domain.com' + } +}); +``` + +如果未配置 endpoint,系统会自动使用 `window.location.origin`。 + +## 修改文件列表 + +1. **SkillForm.jsx** + - 导入 `EyeOutlined` 图标 + - 在 `onSelect` 中提取 `appId` 和 `tenantId` + - 添加 `handleViewDetails` 函数实现跳转逻辑 + - 添加 `renderViewIcon` 函数渲染眼睛图标 + - 在工具卡片上显示眼睛图标 + +2. **LoopWrapper.jsx** + - 更新 `handlePluginChange` 函数签名,接收 `appId` 和 `tenantId` + - 更新 dispatch action,传递 `appId` 和 `tenantId` + - 在组装 plugin 对象时包含 `appId` 和 `tenantId` + +3. **reducers/reducers.js** + - 更新 `ChangePluginByMetaDataReducer` 中的 `updateToolInfo` 函数 + - 将 `appId` 和 `tenantId` 存储到 `toolInfo` 中 + +4. **jadeFlowEntry.jsx** + - 添加 `onViewToolDetail` 方法,允许外部订阅查看详情事件 + +## 事件监听示例 + +外部应用可以监听查看工具详情事件: + +```javascript +jadeFlowAgent.onViewToolDetail((event) => { + const { pluginId, pluginName, uniqueName, appId, tenantId } = event; + + console.log('查看工具详情:', { + pluginId, + pluginName, + uniqueName, + appId, + tenantId + }); + + // 可以在这里实现自定义的详情展示逻辑 +}); +``` + +## 注意事项 + +1. **眼睛图标显示条件**: 只有当工具同时包含 `appId` 和 `tenantId` 信息时,才会显示眼睛图标 +2. **老数据兼容**: 对于不包含 `appId` 和 `tenantId` 的老数据,不显示眼睛图标,只显示工具名称和删除图标 +3. 跳转在新标签页中打开,不影响当前编辑状态 +4. 即使没有配置 endpoint,系统也会使用当前域名作为默认值 +5. 查看详情事件只有在点击眼睛图标时才会被触发 + +## 兼容性 + +- 不会影响现有功能 +- 对于不包含 `appId` 和 `tenantId` 的旧数据,不显示眼睛图标,工具卡片正常显示工具名称和删除功能 +- 所有更改向后兼容,老数据可以正常使用 + diff --git a/agent-flow/docs/features/parallel-tool-detail-view.md b/agent-flow/docs/features/parallel-tool-detail-view.md new file mode 100644 index 0000000000..bae8ab8653 --- /dev/null +++ b/agent-flow/docs/features/parallel-tool-detail-view.md @@ -0,0 +1,118 @@ +# 并行节点工具详情查看功能 + +## 功能概述 + +为并行节点的工具卡片添加了查看详情功能,用户可以点击眼睛图标跳转到对应工具流的详情页面。 + +## 实现细节 + +### 1. 数据存储 + +在选择工具时,系统会自动提取并存储以下信息: +- **appId**: 从 `selectedData.runnables.APP.appId` 提取 +- **tenantId**: 从 `selectedData.schema.parameters.properties.tenantId.default` 提取 + +这些信息会保存在每个工具的 `value` 数组中,随节点配置一起持久化。 + +### 2. UI 增强 + +每个并行任务的工具卡片的 header 显示: +- **工具输出名称** (左侧) +- **眼睛图标** (中间,仅当存在 appId 和 tenantId 时显示) - 用于查看详情 +- **删除图标** (右侧) + +眼睛图标按钮特性: +- **仅在工具包含 appId 和 tenantId 时显示** +- 鼠标悬停时显示"工具详情"提示 +- 在禁用状态下不可点击 +- 点击时不会触发父元素的点击事件 + +### 3. 跳转逻辑 + +点击眼睛图标时: +1. 从工具配置中获取 `appId` 和 `tenantId` +2. 从 graph configs 中获取 `endpoint` 配置(如果未配置,使用当前域名) +3. 构建跳转 URL: `{endpoint}/app-develop/{tenantId}/add-flow/{appId}?type=workFlow` +4. 在新标签页中打开目标页面 + +### 4. 配置说明 + +如果需要自定义 endpoint,可以在 configs 中配置: + +```javascript +configs.push({ + node: 'parallelNodeState', + urls: { + endpoint: 'https://your-domain.com' + } +}); +``` + +如果未配置 endpoint,系统会自动使用 `window.location.origin`。 + +## 修改文件列表 + +1. **ParallelPluginItem.jsx** + - 导入 `EyeOutlined` 图标 + - 添加 `handleViewDetails` 函数实现跳转逻辑 + - 添加 `renderViewIcon` 函数渲染眼睛图标(包含条件判断) + - 在工具卡片 header 上显示眼睛图标 + +2. **ParallelTopBar.jsx** + - 在 `onSelect` 中提取 `appId` 和 `tenantId` + - 更新 `handlePluginAdd` 调用,传递 `appId` 和 `tenantId` + +3. **ParallelWrapper.jsx** + - 更新 `handlePluginAdd` 函数签名,接收 `appId` 和 `tenantId` + - 更新 dispatch action,传递 `appId` 和 `tenantId` + +4. **reducers/reducers.js** + - 更新 `AddPluginByMetaDataReducer` 中的 `PLUGIN_INPUT` 对象 + - 将 `appId` 和 `tenantId` 作为新的字段存储到 plugin value 中 + +## 数据结构 + +并行节点中的每个工具数据结构示例: + +```javascript +{ + id: "uuid", + type: "Object", + from: "Expand", + value: [ + { name: "uniqueName", value: "tool-unique-name" }, + { name: "args", value: [...] }, + { name: "order", value: [...] }, + { name: "outputName", value: "task_1" }, + { name: "tags", value: [...] }, + { name: "appId", value: "app-123" }, // 新增 + { name: "tenantId", value: "tenant-456" } // 新增 + ] +} +``` + +## 注意事项 + +1. **眼睛图标显示条件**: 只有当工具同时包含 `appId` 和 `tenantId` 信息时,才会显示眼睛图标 +2. **老数据兼容**: 对于不包含 `appId` 和 `tenantId` 的老数据,不显示眼睛图标,只显示工具名称和删除功能 +3. 跳转在新标签页中打开,不影响当前编辑状态 +4. 即使没有配置 endpoint,系统也会使用当前域名作为默认值 +5. 每个并行任务独立显示眼睛图标(如果满足条件) + +## 兼容性 + +- 不会影响现有功能 +- 对于不包含 `appId` 和 `tenantId` 的旧数据,不显示眼睛图标,工具卡片正常显示输出名称和删除功能 +- 所有更改向后兼容,老数据可以正常使用 +- 与循环节点的实现保持一致,使用相同的数据提取逻辑 + +## 与循环节点的对比 + +| 特性 | 循环节点 | 并行节点 | +|------|---------|---------| +| 数据提取 | 相同 | 相同 | +| 存储位置 | toolInfo 对象 | plugin.value 数组 | +| 显示位置 | 工具卡片右侧 | 折叠面板 header 中间 | +| 跳转逻辑 | 相同 | 相同 | +| 配置节点名 | loopNodeState | parallelNodeState | + diff --git a/agent-flow/docs/features/shape-data-validation/design-picture.png b/agent-flow/docs/features/shape-data-validation/design-picture.png new file mode 100644 index 0000000000..4991c378d6 Binary files /dev/null and b/agent-flow/docs/features/shape-data-validation/design-picture.png differ diff --git a/agent-flow/docs/features/shape-data-validation/index.md b/agent-flow/docs/features/shape-data-validation/index.md new file mode 100644 index 0000000000..153c6bd0b5 --- /dev/null +++ b/agent-flow/docs/features/shape-data-validation/index.md @@ -0,0 +1,289 @@ +#
agent-flow中节点表单的校验
+ +------ + +## 需求背景 + +应用需要支持导入导出,在导入应用以后需要对应用的数据进行校验。当前覆盖范围有【知识检索-知识库】、【大模型-模型、插件】、【插件节点】、【智能表单节点-表单】、【结束节点-表单】 + +在设计稿中,对于校验失败的节点需要外框变为红色。 + +![img](design-picture.png) + +## 方案设计 + +由于应用导入后所依赖的插件、模型、知识库、表单等信息需要查询接口获取。并且错误信息在进入应用页面,还未进入编排页面时就需要进行展示,此时agent-flow还未被加载。以上信息的存储位置是agent-flow定义,因此提取逻辑也应由agent-flow完成才合适。 + +基于以上背景,设计校验流程如下: + +1.前端获取到graph后调用agent-flow提供对json格式的graph单独提取校验信息的接口,此接口不需要加载流程。 + +2.前端获取到需要校验的信息后,调用后端接口对信息进行校验。 + +3.前端右侧浮窗展示校验结果。 + +4.进入编排页面,对于校验失败项,需要高亮;包含校验失败项的图形需要红框包裹。 + +5.修改校验失败项后,该项高亮去除,右侧浮窗中对应校验结果去除。 + +6.一个图形中所有校验失败项去除后,该图形包裹边框变回蓝色。 + +附加项:在编排页面中点击右侧浮窗中具体图形,画布中心需要切换至该图形位置。 + + + +**过程示意图** + +```plantuml +actor AppDeveloper +participant "frontend" as FR +participant "agent-flow" as ER +participant "backend" as BA + +AppDeveloper -> FR : import App +activate AppDeveloper +activate FR +FR -> ER : Graph(JSON) +ER --> FR : Information to be verified +deactivate ER +activate BA +FR -> BA : Perform verification +BA --> FR : Verification result +deactivate BA +FR -> FR : Display verification result +activate ER +FR -> ER : Verification result displayed on the workflow page +AppDeveloper -> ER : Change invalid data +ER --> FR : Update the verification result. +deactivate ER +deactivate FR +deactivate AppDeveloper +``` + +## 数据结构设计 + +传入json类型的graph后,elsa返回需要校验信息: + +```json +[ + { + "type": "llmNodeState", + "nodeInfos": [ + { + "nodeId": "jadewdnjbq", + "nodeName": "大模型", + "configs": [ + { + "configCheckId": "db5fdafa-4cbf-44ba-9cca-8a98f1f77111", + "configName": "accessInfo", + "serviceName": "Fake Model", + "tag": "INTERNAL" + }, + { + "configCheckId": "ff895dfd-09b8-47b4-a0a0-04b5b83e8cd5", + "configName": "plugin", + "uniqueName": "92d4e409-ea01-45aa-8abc-d7f63ded531d" + }, + { + "configCheckId": "4ab1a267-96c4-4d17-966b-813bfa9142d4", + "configName": "plugin", + "uniqueName": "c04cee50-13e4-499b-a008-077459a2e552" + }, + { + "configCheckId": "ff895dfd-09b8-47b4-a0a0-04b5b83e8cd5", + "configName": "plugin", + "uniqueName": "92d4e409-ea01-45aa-8abc-d7f63ded531e" + }, + { + "configCheckId": "4ab1a267-96c4-4d17-966b-813bfa9142d4", + "configName": "plugin", + "uniqueName": "c04cee50-13e4-499b-a008-077459a2e553" + } + ] + } + ] + } +] +``` + +后端的校验结果: + +```json +[ + { + "nodeId": "jadewdnjbq", + "name": "大模型", + "type": "llmNodeState", + "isValid": false, + "configChecks": [ + { + "configCheckId": "db5fdafa-4cbf-44ba-9cca-8a98f1f77111", + "configName": "accessInfo", + "serviceName": "Fake Model", + "tag": "INTERNAL" + }, + { + "configCheckId": "ff895dfd-09b8-47b4-a0a0-04b5b83e8cd5", + "configName": "plugin", + "uniqueName": "92d4e409-ea01-45aa-8abc-d7f63ded531d" + }, + { + "configCheckId": "ff895dfd-09b8-47b4-a0a0-04b5b83e8cd5", + "configName": "plugin", + "uniqueName": "92d4e409-ea01-45aa-8abc-d7f63ded531e" + }, + { + "configCheckId": "4ab1a267-96c4-4d17-966b-813bfa9142d4", + "configName": "plugin", + "uniqueName": "c04cee50-13e4-499b-a008-077459a2e553" + } + ] + } +] +``` + +为了实现上方的4和5,需要改造原有对外公开的validate接口,改为可以传入validateInfo。 + +1.当validateInfo未传入时与原来逻辑一致,直接进行校验,返回校验结果。 + +2.当传入validateInfo时,更新系统中的validateInfo,需要进行特殊校验的表单项与validateInfo进行对比,不符合特殊校验规则就报错。 + +3.对系统中validateInfo进行监听,一旦validateInfo被修改,需要通知前端。 + +由于用户只会主动修改data,并不会主动修改validateInfo,因此在data进行变化时,需要验证validateInfo是否已经失效,若失效需要主动更新validateInfo。 + +实现上述功能需要修改原有onChangeCallback方法,在原有onChangeCallback直接进行callback之前,先进行validateInfo的校验,若shape的任意一个validateInfo已失效则剔除,若shape已没有validateInfo则整个shape信息从validateInfo中去除。 + +## 关键模块设计 + +### validate方法改造 +改造validate方法,使其可以传入validateInfo,并且传入validateInfo被修改时调用的refresh方法 + +```plantuml +participant "frontend" as FR +participant "agent-flow" as ER + +FR -> ER : Transfer validateInfo and refresh function +activate FR +activate ER +ER -> ER : Listen to the changes of validateInfo +ER -> FR : invoke refresh function +deactivate ER +deactivate FR +``` + + +### onChangeCallback方法改造 +改造onChangeCallback,增加对validateInfo的校验与更新 + +```plantuml +participant "graph" as Graph +participant "shape" as Shape + +activate Graph +activate Shape +Shape -> Graph : Data changed invoke onChangeCallback +Graph -> Graph : Update validateInfo +deactivate Graph +deactivate Shape +``` + + +### 新增ShapeDataValidationProcessor模块 +增加ShapeDataValidationProcessor,每一类需要有特殊校验逻辑的shape自行重写extractValidationInfo和isValidationInfoValid方法 + +```javascript +/** + * 普通节点处理器. + * + * @param shape 节点信息。 + * @return {{}} + */ +const normalDataValidationProcessor = (shape) => { + const self = {}; + self.shape = shape; + + /** + * 提取校验信息. + */ + self.extractValidationInfo = () => { + return null; + }; + + /** + * 验证校验信息是否合法. + */ + self.isValidationInfoValid = () => { + return true; + }; + + return self; +}; +``` + +## 当前shape中form校验现状 + +1.调用unselect的时候,触发整个shape中form的校验 + +2.调用page的validate时,触发整个page中所有shape对应form的校验 + +3.item中数据变化时,触发此item的校验 + + + +## 目标 + +### 红边框出现逻辑: + +红边框在shape失焦时,若shape中有表单项校验未通过,则出现;若shape中所有表单项均校验通过,则消失。 + +触发整个页面的校验时,其中校验不通过的shape出现红框。 + +### 红边框消失逻辑: + +在shape已经为红边框的前提下,该shape中数据有更改时触发shape的校验逻辑,若校验通过,则红边框消失。 + + + +## 设计方案 + +### 红边框出现逻辑: + +```plantuml +actor AppDeveloper +participant "agent-flow" as ER +participant "ShapeBorderColor" as SBC + +AppDeveloper -> ER : shape onblur +activate AppDeveloper +activate ER +activate SBC +ER -> SBC : render red color(if form validation has error) +deactivate ER +deactivate SBC + +AppDeveloper -> ER : page validate +activate ER +activate SBC +ER -> SBC : render red color(if form validation has error) +deactivate AppDeveloper +deactivate ER +deactivate SBC +``` + +### 红边框消失逻辑: + +```plantuml +actor AppDeveloper +participant "agent-flow" as ER +participant "ShapeBorderColor" as SBC + +AppDeveloper -> ER : Change data +activate AppDeveloper +activate ER +activate SBC +ER -> SBC : render blue color(if shape has error before and form verification is successful) +deactivate AppDeveloper +deactivate ER +deactivate SBC +``` diff --git a/agent-flow/index.html b/agent-flow/index.html new file mode 100644 index 0000000000..86fa92c3d3 --- /dev/null +++ b/agent-flow/index.html @@ -0,0 +1,12 @@ + + + + + + Vite + React + + +
+ + + diff --git a/agent-flow/package.json b/agent-flow/package.json new file mode 100644 index 0000000000..25b5f26f14 --- /dev/null +++ b/agent-flow/package.json @@ -0,0 +1,56 @@ +{ + "name": "@fit-elsa/agent-flow", + "private": false, + "version": "1.0.0-alpha.8", + "type": "module", + "main": "./build/agent-flow.js", + "module": "./build/agent-flow.js", + "files": [ + "build" + ], + "exports": { + ".": { + "import": "./build/agent-flow.js", + "require": "./build/agent-flow.umd.cjs" + }, + "./locales/en.json": "./src/i18n/en_US.json", + "./locales/zh.json": "./src/i18n/zh_CN.json" + }, + "scripts": { + "dev": "vite", + "build": "vite build", + "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", + "preview": "vite preview" + }, + "dependencies": { + "@ant-design/icons": "5.3.0", + "@fit-elsa/elsa": "1.0.2", + "@tinymce/tinymce-react": "4.3.0", + "antd": "4.24.13", + "axios": "^1.12.0", + "monaco-editor": "^0.34.1", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "uuid": "^9.0.1" + }, + "devDependencies": { + "@types/react": "^18.2.66", + "@types/react-dom": "^18.2.22", + "@vitejs/plugin-react": "^4.2.1", + "eslint": "^8.57.0", + "eslint-plugin-react": "^7.34.1", + "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-react-refresh": "^0.4.6", + "i18next": "^21.6.0", + "react-i18next": "^11.18.3", + "vite": "^5.2.0", + "vite-plugin-libcss": "^1.1.1", + "vite-plugin-svgr": "^4.2.0" + }, + "overrides": { + "rc-util": "5.43.0", + "rc-motion": "2.9.2", + "rc-resize-observer": "1.4.0", + "rollup": "4.39.0" + } +} diff --git a/agent-flow/src/App.jsx b/agent-flow/src/App.jsx new file mode 100644 index 0000000000..7542406516 --- /dev/null +++ b/agent-flow/src/App.jsx @@ -0,0 +1,339 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import {useEffect, useRef, useState} from 'react'; +import {JadeFlow} from './flow/jadeFlowEntry.jsx'; +import {graphData} from './testFlowData.js'; +import {Button} from 'antd'; +import {CodeDrawer} from '@/components/common/code/CodeDrawer.jsx'; +import 'antd/dist/antd.css'; +import {createGraphOperator} from '@/data/GraphOperator.js'; +import {v4 as uuidv4} from 'uuid'; + +function App({i18n}) { + const [open, setOpen] = useState(false); + const ctlRef = useRef(); + const agentRef = useRef(); + + useEffect(() => { + const stage = document.getElementById('stage'); + const configs = []; + configs.push({ + node: 'startNodeStart', + urls: {customHistoryUrl: ''}, + }); + configs.push({ + node: 'llmNodeState', urls: { + llmModelEndpoint: '', + toolListEndpoint: '', + workflowListEndpoint: '', + }, params: { + tenantId: '', appId: '', + }, + }); + configs.push({ + node: 'knowledgeState', + urls: {knowledgeUrl: ''}, + }); + configs.push({ + node: 'fitInvokeState', urls: { + serviceListEndpoint: '', + fitableMetaInfoUrl: '', + }, + }); + configs.push({ + node: 'manualCheckNodeState', + urls: {runtimeFormUrl: ''}, + }); + configs.push({ + node: 'codeNodeState', urls: { + testCodeUrl: '', + }, + }); + configs.push({ + node: 'replyNodeState', urls: { + testCodeUrl: '', + }, + }); + configs.push({ + node: 'evaluationAlgorithmsNodeState', urls: { + evaluationAlgorithmsUrl: '', + }, + }); + configs.push({ + node: 'evaluationTestSetNodeState', urls: { + datasetUrlPrefix: '', + }, + }); + configs.push({ + node: 'queryOptimizationNodeState', + urls: { + llmModelEndpoint: '', + }, + }); + configs.push({ + node: 'textConcatenateNodeState' + }); + + JadeFlow.edit({ + div: stage, + tenant: '1111', + appId: 'xxx', + flowConfigData: graphData, + configs: configs, + i18n: i18n, + }).then(agent => { + window.agent = agent; + window.operator = createGraphOperator(JSON.stringify(graphData)); + agentRef.current = agent; + agent.onModelSelect((onModelSelectedCallback) => { + onModelSelectedCallback.onSelect({name: 'zy-model'}); + }); + agent.onCreateButtonClick(() => { + }); + agent.onChange(() => { + }); + agent.onKnowledgeSearchArgsSelect(({callback}) => { + callback({ + indexType: { + type: 'enum-string', + name: '语义检索', + description: 'sdfsdfsdfsdfsdf', + }, + similarityThreshold: 0.5, + referenceLimit: { + type: 'TOPK', + value: 4, + }, + rerankParam: { + enableRerank: true, + }, + }); + }); + agent.onKnowledgeBaseSelect((context) => { + context.onSelect([ + { + id: '1', + name: 'ssdfsdf速度快放假水电费', + description: '', + type: 'VECTOR', + createdAt: '2024-10-22 09:47:31', + checked: true, + }, + ]); + }); + agent.onAddInputParam(({ onAdd, existParam }) => { + const mockParam = { + id: 'input_' + uuidv4(), + name: 'userAge', + type: 'Boolean', + value: '', + displayName: 'test', + isRequired: true, + isVisible: true, + disableModifiable: false, + appearance: {}, + }; + + // 直接调用回调,模拟弹窗“确认”后的行为 + onAdd(mockParam); + console.log(existParam); + }); + + agent.onEditInputParam(({id, onEdit, selectedParam }) => { + const mockParam = { + id: id, + name: 'userAge', + type: 'String', + value: '', + displayName: 'test', + isRequired: true, + isVisible: true, + disableModifiable: false, + appearance: {}, + }; + + // 直接调用回调,模拟弹窗“确认”后的行为 + onEdit(mockParam); + + console.log(selectedParam); + }); + agent.listen('GENERATE_AI_PROMPT', (event) => { + event.applyPrompt('123'); + }); + agent.listen('SELECT_KNOWLEDGE_BASE_GROUP', (event) => { + event.onSelect('groupIdTest'); + }); + }); + }, []); + + const runTest = () => { + ctlRef.current = agentRef.current.run(); + setTimeout(() => { + ctlRef.current.refresh([ + { + parameters: [ + { + input: '{\"useMemory\":false,\"chatId\":\"9f1c069ec8224b7d93e72fa402217ebd\",\"Question\":\"11111\"}', + output: '{\"useMemory\":false,\"chatId\":\"9f1c069ec8224b7d93e72fa402217ebd\",\"Question\":\"11111\"}', + }, + ], + nodeId: 'jade6qm5eg', + nodeType: 'START', + startTime: 1724772633987, + runCost: 0, + status: 'ARCHIVED', + errorMsg: '', + }, + { + parameters: [ + { + input: '{\"query\":\"11111\",\"maximum\":3,\"knowledge\":[{}]}', + output: '{\"output\":{\"retrievalOutput\":\"\"}}', + }, + ], + nodeId: 'jade0pg2ag', + nodeType: 'STATE', + startTime: 1724772634000, + runCost: 47, + status: 'ARCHIVED', + errorMsg: '', + }, + ]); + setTimeout(() => { + ctlRef.current.stop([ + { + parameters: [ + { + input: '{\"chatId\":\"2ad84f7cbc2445798dc62d219f0fca42\",\"Question\":\"11111\"}', + output: '{\"chatId\":\"2ad84f7cbc2445798dc62d219f0fca42\",\"Question\":\"11111\"}', + }, + ], + nodeId: 'jade6qm5eg', + nodeType: 'START', + startTime: 1725290745940, + runCost: 0, + status: 'ARCHIVED', + errorMsg: '', + }, + { + parameters: [ + { + input: '{\"query\":\"11111\",\"maximum\":3,\"knowledge\":[{}]}', + output: '{\"output\":{\"retrievalOutput\":\"\"}}', + }, + ], + nodeId: 'jade0pg2ag', + nodeType: 'STATE', + startTime: 1725290745958, + runCost: 27, + status: 'ARCHIVED', + errorMsg: '', + }, + { + parameters: [ + { + input: '{\"branches\":[{\"conditionRelation\":\"and\",\"conditions\":[{\"condition\":\"equal\",\"left\":{\"type\":\"String\",\"value\":\"\",\"key\":\"jade0pg2ag.output.retrievalOutput\"},\"right\":{\"type\":\"String\",\"value\":\"123123\"}},{\"condition\":\"equal\",\"left\":{\"type\":\"String\",\"value\":\"11111\",\"key\":\"jade6qm5eg.Question\"},\"right\":{\"type\":\"String\",\"value\":\"2222\"}}]},{\"conditionRelation\":\"and\",\"conditions\":[{\"condition\":\"true\"}]}]}', + output: null, + }, + ], + errorMsg: 'Condition rule parse error. Condition Rule: (businessData.get(\"_internal\").get(\"outputScope\").get(\"jadeih9c4h\").get(\"output\").isEmpty())', + nodeId: 'jade62q33k', + nodeType: 'CONDITION', + startTime: 1725290746003, + runCost: 39, + status: 'ERROR', + }, + { + parameters: [ + { + input: '{\"finalOutput\":\"008a734011be4473ac87c268342b4630\"}', + output: null, + }, + ], + nodeId: 'jadesoux5i', + nodeType: 'END', + startTime: 1725290746284, + runCost: 63, + status: 'ARCHIVED', + errorMsg: '', + }, + ]); + }, 2000); + }, 500); + }; + + const runReset = () => { + ctlRef.current && ctlRef.current.reset(); + }; + + return (<> +
+
+ + + + + + + + + +
+
+ Output:\n return ret', + suggestions: [{label: 'zyyyyyyyyyyyy', insertText: 'zyyyyyyyyyyyy'}], + }} + onClose={() => setOpen(false)} + onConfirm={(v) => { + // 这里对编辑后的代码进行处理 + }} + executeFunc={(args, language, callback) => { + // 这里调用执行代码的接口 + + // 接口返回的output通过callback传递给组件,展示output + }}/> +
+ ); +} + +export default App; diff --git a/agent-flow/src/common/Consts.js b/agent-flow/src/common/Consts.js new file mode 100644 index 0000000000..d929a08d3a --- /dev/null +++ b/agent-flow/src/common/Consts.js @@ -0,0 +1,290 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import {v4 as uuidv4} from 'uuid'; + +export const NODE_STATUS = { + RUNNING: 'RUNNING', + ERROR: 'ERROR', + SUCCESS: 'ARCHIVED', + DEFAULT: 'DEFAULT', + UN_RUNNING: 'UN_RUNNING', + TERMINATED: 'TERMINATED', +}; + +export const SECTION_TYPE = { + CONDITION: 'condition', + DEFAULT: 'default', +}; + +export const UNARY_OPERATOR = { + IS_EMPTY: 'is empty', + IS_NOT_EMPTY: 'is not empty', + IS_EMPTY_STRING: 'is empty string', + IS_NOT_EMPTY_STRING: 'is not empty string', + IS_TRUE: 'is true', + IS_FALSE: 'is false', + IS_NULL: 'is null', + IS_NOT_NULL: 'is not null', +}; + +export const BINARY_OPERATOR = { + LONGER_THAN: 'longer than', + LONGER_THAN_OR_EQUAL: 'longer than or equal', + SHORTER_THAN: 'shorter than', + SHORTER_THAN_OR_EQUAL: 'shorter than or equal', + STARTS_WITH: 'starts with', + ENDS_WITH: 'ends with', + EQUAL: 'equal', + NOT_EQUAL: 'not equal', + CONTAINS: 'contains', + DOES_NOT_CONTAIN: 'does not contain', + GREATER_THAN: 'greater than', + GREATER_THAN_OR_EQUAL: 'greater than or equal', + LESS_THAN: 'less than', + LESS_THAN_OR_EQUAL: 'less than or equal', +}; + +export const VIRTUAL_CONTEXT_NODE = { + id: '_systemEnv', + name: 'systemEnv', +}; + +export const VIRTUAL_CONTEXT_NODE_VARIABLES = { + INSTANCE_ID: 'instanceId', + APP_ID: 'appId', + MEMORIES: 'memories', + USE_MEMORY: 'useMemory', + USER_ID: 'userId', + FILE_URLS: 'fileUrls', + CHAT_ID: 'chatId', + IS_GUEST: 'isGuest', + APP_CREATE_BY: 'appCreateBy', +}; + +export const CONNECTOR = { + RADIUS: 6, + CONDITION_RADIUS: 4, +}; + +export const SOURCE_PLATFORM = { + OFFICIAL: 'official', + HUGGING_FACE: 'huggingface', + LLAMA_INDEX: 'llamaindex', + LANG_CHAIN: 'langchain', +}; + +export const DATA_TYPES = { + STRING: 'String', + INTEGER: 'Integer', + BOOLEAN: 'Boolean', + NUMBER: 'Number', + OBJECT: 'Object', + ARRAY: 'Array', +}; + +export const OBSERVER_STATUS = { + ENABLE: 'enable', + DISABLE: 'disable', +}; + +export const EVALUATION_ALGORITHM_NODE_CONST = { + ALGORITHM: 'algorithm', + UNIQUE_NAME: 'uniqueName', + PASS_SCORE: 'passScore', + IS_PASS: 'isPass', + SCORE: 'score', +}; + +export const FLOW_TYPE = { + APP: 'app', + WORK_FLOW: 'workflow', +}; + +export const FROM_TYPE = { + EXPAND: 'Expand', + INPUT: 'Input', + REFERENCE: 'Reference', +}; + +export const DEFAULT_FLOW_META = '{"triggerMode":"auto","jober":{"type":"STORE_JOBER","name":"","fitables":[],"converter":{"type":"mapping_converter"},"entity":{"uniqueName":"","params":[],"return":{"type":""}}},"joberFilter":{"type":"MINIMUM_SIZE_FILTER","threshold":1}}'; + +export const TOOL_TYPE = { + WATER_FLOW: 'WATERFLOW', + TOOL: 'TOOL', +}; + +export const HTTP_METHOD_TYPE = { + GET: 'get', + POST: 'post', + PUT: 'put', + DELETE: 'delete', + PATCH: 'patch', +}; + +export const HTTP_BODY_TYPE = { + X_WWW_FORM_URLENCODED: 'x-www-form-urlencoded', + JSON: 'json', + TEXT: 'text', +}; + +export const END_NODE_TYPE = { + VARIABLES: 'variables', + MANUAL_CHECK: 'manualCheck', +}; + +export const EVENT_NAME = { + NODE_NAME_CHANGED: 'NODE_NAME_CHANGED', +}; + +export const DEFAULT_KNOWLEDGE_REPO_GROUP = 'default'; + +export const DEFAULT_AP_PROMPT_MODEL_CONFIG = { + MODEL: 'Qwen1.5-32B-Chat', + TEMPERATURE: '0.3', +}; +export const SYSTEM_ACTION = { + JADE_NODE_CONFIG_CHANGE: 'jade_node_config_change', + PAGE_DATA_CHANGED: 'page_data_changed', +}; + +export const DEFAULT_MAX_MEMORY_ROUNDS = { + id: uuidv4(), + name: 'maxMemoryRounds', + type: DATA_TYPES.INTEGER, + from: FROM_TYPE.INPUT, + value: '3', +}; + +export const DEFAULT_LLM_KNOWLEDGE_BASES = { + id: uuidv4(), + from: FROM_TYPE.EXPAND, + name: 'knowledgeBases', + type: DATA_TYPES.ARRAY, + value: [], +}; + +export const DEFAULT_LLM_REFERENCE_OUTPUT = { + id: uuidv4(), + from: FROM_TYPE.INPUT, + name: 'reference', + description: '', + type: DATA_TYPES.ARRAY, + value: [], +}; + +export const DEFAULT_KNOWLEDGE_REPO_GROUP_STRUCT = { + id: uuidv4(), + name: 'groupId', + type: DATA_TYPES.STRING, + from: FROM_TYPE.INPUT, + value: DEFAULT_KNOWLEDGE_REPO_GROUP, +}; + +export const RENDER_TYPE = { + SELECT: 'Select', + RADIO: 'Radio', + INPUT: 'Input', + SWITCH: 'Switch', + CHECK_BOX: 'CheckBox', + LABEL: 'Label', +}; + +export const DEFAULT_ADD_TOOL_NODE_CONTEXT = { + id: uuidv4(), + name: 'context', + type: DATA_TYPES.OBJECT, + from: FROM_TYPE.EXPAND, + value: [{ + id: uuidv4(), + name: VIRTUAL_CONTEXT_NODE_VARIABLES.INSTANCE_ID, + type: DATA_TYPES.STRING, + from: FROM_TYPE.REFERENCE, + referenceId: VIRTUAL_CONTEXT_NODE_VARIABLES.INSTANCE_ID, + referenceKey: VIRTUAL_CONTEXT_NODE_VARIABLES.INSTANCE_ID, + referenceNode: VIRTUAL_CONTEXT_NODE.id, + value: [VIRTUAL_CONTEXT_NODE_VARIABLES.INSTANCE_ID], + }], +}; + +export const DEFAULT_KNOWLEDGE_RETRIEVAL_NODE_KNOWLEDGE_CONFIG_ID = { + id: `knowledgeConfigId_${uuidv4()}`, + name: 'knowledgeConfigId', + type: DATA_TYPES.STRING, + from: FROM_TYPE.INPUT, + value: '', +}; + +export const DEFAULT_MCP_SERVERS = { + id: uuidv4(), + name: "mcpServers", + type: DATA_TYPES.OBJECT, + from: FROM_TYPE.INPUT, + value: {} +}; + +export const DEFAULT_KNOWLEDGE_NODE_ACCESS_INFO = { + id: uuidv4(), + name: 'accessInfo', + type: DATA_TYPES.OBJECT, + from: FROM_TYPE.EXPAND, + value: [{ + id: uuidv4(), + name: 'serviceName', + type: DATA_TYPES.STRING, + from: FROM_TYPE.INPUT, + value: '', + }, { + id: uuidv4(), + name: 'tag', + type: DATA_TYPES.STRING, + from: FROM_TYPE.INPUT, + value: '', + }], +}; + +export const DEFAULT_KNOWLEDGE_NODE_RERANK_TOP_N = { + id: uuidv4(), + name: 'topN', + type: DATA_TYPES.INTEGER, + from: FROM_TYPE.INPUT, + value: 3, +}; + +export const DEFAULT_KNOWLEDGE_RETRIEVAL_NODE_EXTENSIONS = { + id: `knowledgeExtensions_${uuidv4()}`, + name: 'extensions', + type: DATA_TYPES.OBJECT, + from: FROM_TYPE.EXPAND, + value: [{ + id: uuidv4(), + name: VIRTUAL_CONTEXT_NODE_VARIABLES.USER_ID, + type: DATA_TYPES.STRING, + from: FROM_TYPE.REFERENCE, + referenceId: VIRTUAL_CONTEXT_NODE_VARIABLES.USER_ID, + referenceKey: VIRTUAL_CONTEXT_NODE_VARIABLES.USER_ID, + referenceNode: VIRTUAL_CONTEXT_NODE.id, + value: [VIRTUAL_CONTEXT_NODE_VARIABLES.USER_ID], + }, { + id: uuidv4(), + name: VIRTUAL_CONTEXT_NODE_VARIABLES.IS_GUEST, + type: DATA_TYPES.BOOLEAN, + from: FROM_TYPE.REFERENCE, + referenceId: VIRTUAL_CONTEXT_NODE_VARIABLES.IS_GUEST, + referenceKey: VIRTUAL_CONTEXT_NODE_VARIABLES.IS_GUEST, + referenceNode: VIRTUAL_CONTEXT_NODE.id, + value: [VIRTUAL_CONTEXT_NODE_VARIABLES.IS_GUEST], + }, { + id: uuidv4(), + name: VIRTUAL_CONTEXT_NODE_VARIABLES.APP_CREATE_BY, + type: DATA_TYPES.STRING, + from: FROM_TYPE.REFERENCE, + referenceId: VIRTUAL_CONTEXT_NODE_VARIABLES.APP_CREATE_BY, + referenceKey: VIRTUAL_CONTEXT_NODE_VARIABLES.APP_CREATE_BY, + referenceNode: VIRTUAL_CONTEXT_NODE.id, + value: [VIRTUAL_CONTEXT_NODE_VARIABLES.APP_CREATE_BY], + }], +}; \ No newline at end of file diff --git a/agent-flow/src/components/AdvancedConfiguration.jsx b/agent-flow/src/components/AdvancedConfiguration.jsx new file mode 100644 index 0000000000..0f5ae8ed2d --- /dev/null +++ b/agent-flow/src/components/AdvancedConfiguration.jsx @@ -0,0 +1,109 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import {useState} from 'react'; +import {Button, Input, Modal, Popover, Switch} from 'antd'; +import {QuestionCircleOutlined} from '@ant-design/icons'; +import PropTypes from 'prop-types'; + +const {TextArea} = Input; + +/** + * 高级配置. + * + * @param data 数据. + * @param disabled 是否禁用. + * @param onConfirm 当点击确认时的回调. + * @param isAdvancedConfigurationOpen 组件是否需要打开. + * @param setAdvancedConfigurationOpen 组件打开设置修改方法. + * @return {JSX.Element} + * @constructor + */ +export const AdvancedConfiguration = ({data, + disabled, + onConfirm, + isAdvancedConfigurationOpen, + setAdvancedConfigurationOpen}) => { + const [stageData, setStageData] = + useState({enableStageDesc: data.enableStageDesc, stageDesc: data.stageDesc}); + + const onModalOk = () => { + onConfirm(stageData); + setAdvancedConfigurationOpen(false); + }; + + const onModalCancel = () => { + setAdvancedConfigurationOpen(false); + }; + + const advancedConfigurationContent = ( + <> +
+

说明:是否在节点运行前,打印过

+

程信息,告知用户当前在做什么操

+

作。如:数据库查询中...

+
+ ); + + const handleProcessInfoSwitchChange = (e) => { + if (e === false) { + setStageData({...stageData, enableStageDesc: e, stageDesc: ''}); + } else { + setStageData({...stageData, enableStageDesc: e}); + } + }; + + const handleProcessInfoChange = (e) => { + setStageData({...stageData, stageDesc: e.target.value}); + }; + + return (<> + + + + , + ]}> +
+ 是否打印过程信息 + + + + handleProcessInfoSwitchChange(e)} + checked={stageData?.enableStageDesc ?? false}> +
+ {stageData.enableStageDesc && ( + <> + 请输入过程信息 +