diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 3a2b66d5bb9a..feb4f852b565 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,5 +1,5 @@ blank_issues_enabled: false contact_links: - name: 飞书话题群 - url: https://oss.laf.run/otnvvf-imgs/1719505774252.jpg + url: https://oss.laf.run/otnvvf-imgs/feishu3.png about: FastGPT 全是问题群 diff --git a/docSite/content/zh-cn/docs/development/upgrading/4811.md b/docSite/content/zh-cn/docs/development/upgrading/4811.md index 32f9ca4dbfad..26bb9f3f7354 100644 --- a/docSite/content/zh-cn/docs/development/upgrading/4811.md +++ b/docSite/content/zh-cn/docs/development/upgrading/4811.md @@ -92,15 +92,16 @@ weight: 813 6. 新增 - 支持 Openai o1 模型,需增加模型的 `defaultConfig` 配置,覆盖 `temperature`、`max_tokens` 和 `stream`配置,o1 不支持 stream 模式, 详细可重新拉取 `config.json` 配置文件查看。 7. 新增 - AI 对话节点知识库引用,支持配置 role=system 和 role=user,已配置的过自定义提示词的节点将会保持 user 模式,其余用户将转成 system 模式。 8. 新增 - 插件支持上传系统文件。 -9. 新增 - 支持工作流嵌套子应用时,可以设置`非流模式`,同时简易模式也可以选择工作流作为插件了,简易模式调用子应用时,都将强制使用非流模式。 -10. 新增 - 调试模式下,子应用调用,支持返回详细运行数据。 -11. 新增 - 保留所有模式下子应用嵌套调用的日志。 -12. 优化 - 工作流嵌套层级限制 20 层,避免因编排不合理导致的无限死循环。 -13. 优化 - 工作流 handler 性能优化。 -14. 优化 - 工作流快捷键,避免调试测试时也会触发。 -15. 优化 - 流输出,切换 tab 时仍可以继续输出。 -16. 优化 - 完善外部文件知识库相关 API -17. 修复 - 知识库选择权限问题。 -18. 修复 - 空 chatId 发起对话,首轮携带用户选择时会异常。 -19. 修复 - createDataset 接口,intro 为赋值。 -20. 修复 - 对话框渲染性能问题。 +9. 新增 - 插件输出,支持指定字段作为工具响应。 +10. 新增 - 支持工作流嵌套子应用时,可以设置`非流模式`,同时简易模式也可以选择工作流作为插件了,简易模式调用子应用时,都将强制使用非流模式。 +11. 新增 - 调试模式下,子应用调用,支持返回详细运行数据。 +12. 新增 - 保留所有模式下子应用嵌套调用的日志。 +13. 优化 - 工作流嵌套层级限制 20 层,避免因编排不合理导致的无限死循环。 +14. 优化 - 工作流 handler 性能优化。 +15. 优化 - 工作流快捷键,避免调试测试时也会触发。 +16. 优化 - 流输出,切换 tab 时仍可以继续输出。 +17. 优化 - 完善外部文件知识库相关 API +18. 修复 - 知识库选择权限问题。 +19. 修复 - 空 chatId 发起对话,首轮携带用户选择时会异常。 +20. 修复 - createDataset 接口,intro 为赋值。 +21. 修复 - 对话框渲染性能问题。 diff --git a/packages/global/core/workflow/template/system/interactive/formInput.ts b/packages/global/core/workflow/template/system/interactive/formInput.ts index 5c2a60bf8270..5750a50aa011 100644 --- a/packages/global/core/workflow/template/system/interactive/formInput.ts +++ b/packages/global/core/workflow/template/system/interactive/formInput.ts @@ -46,8 +46,8 @@ export const FormInputNode: FlowNodeTemplateType = { id: NodeOutputKeyEnum.formInputResult, key: NodeOutputKeyEnum.formInputResult, required: true, - label: i18nT('app:workflow.form_input_result'), - description: i18nT('app:workflow.form_input_result_tip'), + label: i18nT('workflow:form_input_result'), + description: i18nT('workflow:form_input_result_tip'), valueType: WorkflowIOValueTypeEnum.object, type: FlowNodeOutputTypeEnum.static } diff --git a/packages/service/core/workflow/dispatch/interactive/formInput.ts b/packages/service/core/workflow/dispatch/interactive/formInput.ts index 532fc67bbc86..2235603e3c5b 100644 --- a/packages/service/core/workflow/dispatch/interactive/formInput.ts +++ b/packages/service/core/workflow/dispatch/interactive/formInput.ts @@ -9,6 +9,7 @@ import { UserInputFormItemType, UserInputInteractive } from '@fastgpt/global/core/workflow/template/system/interactive/type'; +import { addLog } from '../../../../common/system/log'; type Props = ModuleDispatchProps<{ [NodeInputKeyEnum.description]: string; @@ -48,7 +49,8 @@ export const dispatchFormInput = async (props: Props): Promise node.flowNodeType === FlowNodeTypeEnum.pluginOutput)! - .inputs.reduce( - (acc, cur) => { + const outputFilterMap = + plugin.nodes + .find((node) => node.flowNodeType === FlowNodeTypeEnum.pluginOutput) + ?.inputs.reduce>((acc, cur) => { acc[cur.key] = cur.isToolOutput === false ? false : true; return acc; - }, - {} as Record - ); + }, {}) ?? {}; const runtimeNodes = storeNodes2RuntimeNodes( plugin.nodes, @@ -125,13 +123,12 @@ export const dispatchRunPlugin = async (props: RunPluginProps): Promise { - const filterArr = [FlowNodeTypeEnum.pluginOutput]; - return !filterArr.includes(item.moduleType as any); - }) - : undefined + pluginDetail: pluginData?.permission?.hasWritePer // Not system plugin + ? flowResponses.filter((item) => { + const filterArr = [FlowNodeTypeEnum.pluginOutput]; + return !filterArr.includes(item.moduleType as any); + }) + : undefined }, [DispatchNodeResponseKeyEnum.nodeDispatchUsages]: [ { @@ -143,14 +140,11 @@ export const dispatchRunPlugin = async (props: RunPluginProps): Promise outputFilterMap[key]) - .reduce( - (acc, key) => { - acc[key] = output.pluginOutput![key]; - return acc; - }, - {} as Record - ) - : {}, + .reduce>((acc, key) => { + acc[key] = output.pluginOutput![key]; + return acc; + }, {}) + : null, ...(output ? output.pluginOutput : {}) }; }; diff --git a/packages/web/i18n/en/app.json b/packages/web/i18n/en/app.json index 8a5e0d0a8c13..6462b2997bb0 100644 --- a/packages/web/i18n/en/app.json +++ b/packages/web/i18n/en/app.json @@ -145,8 +145,6 @@ "workflow.file_url": "Document Link", "workflow.form_input": "Form input", "workflow.form_input_description_placeholder": "For example: \nAdd your information", - "workflow.form_input_result": "Full input result", - "workflow.form_input_result_tip": "An object of full result", "workflow.form_input_tip": " This module can configure multiple inputs to guide users in entering specific content.", "workflow.input_description_tip": "You can add a description to explain to users what they need to input", "workflow.read_files": "Document Parsing", diff --git a/packages/web/i18n/en/common.json b/packages/web/i18n/en/common.json index 20463aa6606a..b82a549c8df7 100644 --- a/packages/web/i18n/en/common.json +++ b/packages/web/i18n/en/common.json @@ -430,7 +430,6 @@ "core.chat.response.Read complete response tips": "Click to View Detailed Process", "core.chat.response.Tool call tokens": "Tool Call Tokens Consumption", "core.chat.response.context total length": "Total Context Length", - "core.chat.response.form_input_result": "Form input result", "core.chat.response.loop_input": "Loop Input Array", "core.chat.response.loop_input_element": "Loop Input Element", "core.chat.response.loop_output": "Loop Output Array", diff --git a/packages/web/i18n/en/workflow.json b/packages/web/i18n/en/workflow.json index 74991ae5068a..30934e4441af 100644 --- a/packages/web/i18n/en/workflow.json +++ b/packages/web/i18n/en/workflow.json @@ -50,6 +50,8 @@ "field_required": "Required", "field_used_as_tool_input": "Used as Tool Call Parameter", "filter_description": "Currently supports filtering by tags and creation time. Fill in the format as follows:\n{\n \"tags\": {\n \"$and\": [\"Tag 1\",\"Tag 2\"],\n \"$or\": [\"When there are $and tags, and is effective, or is not effective\"]\n },\n \"createTime\": {\n \"$gte\": \"YYYY-MM-DD HH:mm format, collection creation time greater than this time\",\n \"$lte\": \"YYYY-MM-DD HH:mm format, collection creation time less than this time, can be used with $gte\"\n }\n}", + "form_input_result": "User complete input result", + "form_input_result_tip": "an object containing the complete result", "full_field_extraction": "Full Field Extraction", "full_field_extraction_description": "Returns true when all fields are fully extracted (success includes model extraction or using default values)", "full_response_data": "Full Response Data", @@ -79,7 +81,7 @@ "is_equal_to": "Is Equal To", "is_not_empty": "Is Not Empty", "is_not_equal": "Is Not Equal", - "is_tool_output": "Whether to use this field as tool output when the tool is called", + "is_tool_output_label": "as tool response", "judgment_result": "Judgment Result", "knowledge_base_reference": "Dataset Reference", "knowledge_base_search_merge": "Dataset Search Merge", @@ -112,6 +114,7 @@ "plugin.Instruction_Tip": "You can configure an instruction to explain the purpose of the plugin. This instruction will be displayed each time the plugin is used. Supports standard Markdown syntax.", "plugin.Instructions": "Instructions", "plugin_input": "Plugin Input", + "plugin_output_tool": "When the plug-in is executed as a tool, whether this field responds as a result of the tool", "question_classification": "Question Classification", "question_optimization": "Question Optimization", "quote_content_placeholder": "The structure of the reference content can be customized to better suit different scenarios. \nSome variables can be used for template configuration\n\n{{q}} - main content\n\n{{a}} - auxiliary data\n\n{{source}} - source name\n\n{{sourceId}} - source ID\n\n{{index}} - nth reference", @@ -157,6 +160,9 @@ "update_link_error": "Error updating link", "update_specified_node_output_or_global_variable": "Can update the output value of a specified node or update global variables", "use_user_id": "User ID", + "user_form_input_config": "Form configuration", + "user_form_input_description": "describe", + "user_form_input_name": "Name", "user_question": "User Question", "user_question_tool_desc": "User input questions (questions need to be improved)", "value_type": "Value type", diff --git a/packages/web/i18n/zh/app.json b/packages/web/i18n/zh/app.json index 76c88401da26..5b56f42f5137 100644 --- a/packages/web/i18n/zh/app.json +++ b/packages/web/i18n/zh/app.json @@ -145,8 +145,6 @@ "workflow.file_url": "文档链接", "workflow.form_input": "表单输入", "workflow.form_input_description_placeholder": "例如:\n补充您的信息", - "workflow.form_input_result": "完整输入结果", - "workflow.form_input_result_tip": "一个包含完整结果的对象", "workflow.form_input_tip": "该模块可以配置多种输入,引导用户输入特定内容。", "workflow.input_description_tip": "你可以添加一段说明文字,用以向用户说明需要输入的内容", "workflow.read_files": "文档解析", @@ -156,7 +154,7 @@ "workflow.select_description": "说明文字", "workflow.select_description_placeholder": "例如: \n冰箱里是否有西红柿?", "workflow.select_description_tip": "你可以添加一段说明文字,用以向用户说明每个选项代表的含义。", - "workflow.select_result": "选择的结果", + "workflow.select_result": "选择结果", "workflow.template.communication": "通信", "workflow.user_file_input": "文件链接", "workflow.user_file_input_desc": "用户上传的文档和图片链接", diff --git a/packages/web/i18n/zh/common.json b/packages/web/i18n/zh/common.json index 0a050994e160..c695da6cd3eb 100644 --- a/packages/web/i18n/zh/common.json +++ b/packages/web/i18n/zh/common.json @@ -429,7 +429,6 @@ "core.chat.response.Read complete response tips": "点击查看详细流程", "core.chat.response.Tool call tokens": "工具调用 tokens 消耗", "core.chat.response.context total length": "上下文总长度", - "core.chat.response.form_input_result": "表单输入结果", "core.chat.response.loop_input": "输入数组", "core.chat.response.loop_input_element": "输入数组元素", "core.chat.response.loop_output": "输出数组", diff --git a/packages/web/i18n/zh/workflow.json b/packages/web/i18n/zh/workflow.json index 45a623401672..bcaa9546656f 100644 --- a/packages/web/i18n/zh/workflow.json +++ b/packages/web/i18n/zh/workflow.json @@ -50,6 +50,8 @@ "field_required": "必填", "field_used_as_tool_input": "作为工具调用参数", "filter_description": "目前支持标签和创建时间过滤,需按照以下格式填写:\n{\n \"tags\": {\n \"$and\": [\"标签 1\",\"标签 2\"],\n \"$or\": [\"有 $and 标签时,and 生效,or 不生效\"]\n },\n \"createTime\": {\n \"$gte\": \"YYYY-MM-DD HH:mm 格式即可,集合的创建时间大于该时间\",\n \"$lte\": \"YYYY-MM-DD HH:mm 格式即可,集合的创建时间小于该时间,可和 $gte 共同使用\"\n }\n}", + "form_input_result": "用户完整输入结果", + "form_input_result_tip": "一个包含完整结果的对象", "full_field_extraction": "字段完全提取", "full_field_extraction_description": "提取字段全部填充时返回 true (模型提取或使用默认值均属于成功)", "full_response_data": "完整响应数据", @@ -79,7 +81,7 @@ "is_equal_to": "等于", "is_not_empty": "不为空", "is_not_equal": "不等于", - "is_tool_output": "是否在工具调用时,将该字段作为工具输出", + "is_tool_output_label": "作为工具响应", "judgment_result": "判断结果", "knowledge_base_reference": "知识库引用", "knowledge_base_search_merge": "知识库搜索引用合并", @@ -112,6 +114,7 @@ "plugin.Instruction_Tip": "可以配置一段说明,以解释该插件的用途。每次使用插件前,会显示该段说明。支持标准 Markdown 语法。", "plugin.Instructions": "使用说明", "plugin_input": "插件输入", + "plugin_output_tool": "插件作为工具执行时,该字段是否作为工具响应结果", "question_classification": "问题分类", "question_optimization": "问题优化", "quote_content_placeholder": "可以自定义引用内容的结构,以更好的适配不同场景。可以使用一些变量来进行模板配置\n{{q}} - 主要内容\n{{a}} - 辅助数据\n{{source}} - 来源名\n{{sourceId}} - 来源ID\n{{index}} - 第 n 个引用", @@ -157,6 +160,9 @@ "update_link_error": "更新链接异常", "update_specified_node_output_or_global_variable": "可以更新指定节点的输出值或更新全局变量", "use_user_id": "使用者 ID", + "user_form_input_config": "表单配置", + "user_form_input_description": "描述", + "user_form_input_name": "标题", "user_question": "用户问题", "user_question_tool_desc": "用户输入的问题(问题需要完善)", "value_type": "数据类型", diff --git a/projects/app/src/components/core/chat/components/AIResponseBox.tsx b/projects/app/src/components/core/chat/components/AIResponseBox.tsx index a7eca5d95422..9f1dec9f6a44 100644 --- a/projects/app/src/components/core/chat/components/AIResponseBox.tsx +++ b/projects/app/src/components/core/chat/components/AIResponseBox.tsx @@ -299,18 +299,12 @@ const AIResponseBox = ({ value, isLastResponseValue, isChatting }: props) => { ); if (value.type === ChatItemValueTypeEnum.tool && value.tools) return ; - if ( - value.type === ChatItemValueTypeEnum.interactive && - value.interactive && - value.interactive.type === 'userSelect' - ) - return ; - if ( - value.type === ChatItemValueTypeEnum.interactive && - value.interactive && - value.interactive?.type === 'userInput' - ) - return ; + if (value.type === ChatItemValueTypeEnum.interactive && value.interactive) { + if (value.interactive.type === 'userSelect') + return ; + if (value.interactive?.type === 'userInput') + return ; + } }; export default React.memo(AIResponseBox); diff --git a/projects/app/src/components/core/chat/components/WholeResponseModal.tsx b/projects/app/src/components/core/chat/components/WholeResponseModal.tsx index 77154a1f39fa..81199f3d3e61 100644 --- a/projects/app/src/components/core/chat/components/WholeResponseModal.tsx +++ b/projects/app/src/components/core/chat/components/WholeResponseModal.tsx @@ -354,10 +354,7 @@ export const WholeResponseContent = ({ /> {/* form input */} - + ) : null; }; diff --git a/projects/app/src/pages/_app.tsx b/projects/app/src/pages/_app.tsx index 5539d5b9eba8..7889bb8672dc 100644 --- a/projects/app/src/pages/_app.tsx +++ b/projects/app/src/pages/_app.tsx @@ -11,9 +11,17 @@ import { useInitApp } from '@/web/context/useInitApp'; import { useTranslation } from 'next-i18next'; import '@/web/styles/reset.scss'; import NextHead from '@/components/common/NextHead'; -import { useEffect } from 'react'; +import { ReactElement, useEffect } from 'react'; +import { NextPage } from 'next'; -function App({ Component, pageProps }: AppProps) { +type NextPageWithLayout = NextPage & { + setLayout?: (page: ReactElement) => JSX.Element; +}; +type AppPropsWithLayout = AppProps & { + Component: NextPageWithLayout; +}; + +function App({ Component, pageProps }: AppPropsWithLayout) { const { feConfigs, scripts, title } = useInitApp(); const { t } = useTranslation(); @@ -30,6 +38,8 @@ function App({ Component, pageProps }: AppProps) { ); }, []); + const setLayout = Component.setLayout || ((page) => <>{page}); + return ( <> - - - + {setLayout()} diff --git a/projects/app/src/pages/app/detail/components/WorkflowComponents/Flow/components/ButtonEdge.tsx b/projects/app/src/pages/app/detail/components/WorkflowComponents/Flow/components/ButtonEdge.tsx index bf4032dbddd1..46e3765c4eb4 100644 --- a/projects/app/src/pages/app/detail/components/WorkflowComponents/Flow/components/ButtonEdge.tsx +++ b/projects/app/src/pages/app/detail/components/WorkflowComponents/Flow/components/ButtonEdge.tsx @@ -1,10 +1,11 @@ -import React, { useCallback, useMemo } from 'react'; +import React, { useCallback, useMemo, useState } from 'react'; import { BezierEdge, getBezierPath, EdgeLabelRenderer, EdgeProps } from 'reactflow'; import { Box, Flex } from '@chakra-ui/react'; import MyIcon from '@fastgpt/web/components/common/Icon'; import { NodeOutputKeyEnum, RuntimeEdgeStatusEnum } from '@fastgpt/global/core/workflow/constants'; import { useContextSelector } from 'use-context-selector'; import { WorkflowContext } from '../../context'; +import { useThrottleEffect } from 'ahooks'; const ButtonEdge = (props: EdgeProps) => { const { nodes, nodeList, setEdges, workflowDebugData, hoverEdgeId } = useContextSelector( @@ -28,13 +29,11 @@ const ButtonEdge = (props: EdgeProps) => { style } = props; + // If parentNode is folded, the edge will not be displayed const parentNode = useMemo(() => { - for (const node of nodeList) { - if ((node.nodeId === source || node.nodeId === target) && node.parentNodeId) { - return nodeList.find((parent) => parent.nodeId === node.parentNodeId); - } - } - return undefined; + return nodeList.find( + (node) => (node.nodeId === source || node.nodeId === target) && node.parentNodeId + ); }, [nodeList, source, target]); const defaultZIndex = useMemo( @@ -52,12 +51,20 @@ const ButtonEdge = (props: EdgeProps) => { [setEdges] ); - const highlightEdge = useMemo(() => { - const connectNode = nodes.find((node) => { - return node.selected && (node.id === props.source || node.id === props.target); - }); - return !!(connectNode || selected); - }, [nodes, props.source, props.target, selected]); + // Selected edge or source/target node selected + const [highlightEdge, setHighlightEdge] = useState(false); + useThrottleEffect( + () => { + const connectNode = nodes.find((node) => { + return node.selected && (node.id === props.source || node.id === props.target); + }); + setHighlightEdge(!!connectNode || !!selected); + }, + [nodes, selected, props.source, props.target], + { + wait: 100 + } + ); const [, labelX, labelY] = getBezierPath({ sourceX, diff --git a/projects/app/src/pages/app/detail/components/WorkflowComponents/Flow/components/FlowController.tsx b/projects/app/src/pages/app/detail/components/WorkflowComponents/Flow/components/FlowController.tsx index d46f9dd3f23c..8fae7c1ace10 100644 --- a/projects/app/src/pages/app/detail/components/WorkflowComponents/Flow/components/FlowController.tsx +++ b/projects/app/src/pages/app/detail/components/WorkflowComponents/Flow/components/FlowController.tsx @@ -17,6 +17,7 @@ import { useTranslation } from 'next-i18next'; import styles from './index.module.scss'; import { maxZoom, minZoom } from '../index'; import { useKeyPress } from 'ahooks'; +import { FlowNodeItemType } from '@fastgpt/global/core/workflow/type/node'; const buttonStyle = { border: 'none', @@ -61,11 +62,16 @@ const FlowController = React.memo(function FlowController() { zoomOut(); }); + /* + id: Render node id + */ const MiniMapNode = useCallback( ({ x, y, width, height, color, id }: MiniMapNodeProps) => { + // If the node parentNode is folded, the child node will not be displayed const node = nodeList.find((node) => node.nodeId === id); - const parentNode = nodeList.find((n) => n.nodeId === node?.parentNodeId); - + const parentNode = node?.parentNodeId + ? nodeList.find((n) => n.nodeId === node?.parentNodeId) + : undefined; if (parentNode?.isFolded) { return null; } diff --git a/projects/app/src/pages/app/detail/components/WorkflowComponents/Flow/hooks/useWorkflow.tsx b/projects/app/src/pages/app/detail/components/WorkflowComponents/Flow/hooks/useWorkflow.tsx index 69818409c7cb..a5fe5c21340c 100644 --- a/projects/app/src/pages/app/detail/components/WorkflowComponents/Flow/hooks/useWorkflow.tsx +++ b/projects/app/src/pages/app/detail/components/WorkflowComponents/Flow/hooks/useWorkflow.tsx @@ -277,6 +277,7 @@ export const useWorkflow = () => { const { setConnectingEdge, nodes, + nodeList, onNodesChange, setEdges, onChangeNode, @@ -370,10 +371,12 @@ export const useWorkflow = () => { const checkNodeOverLoopNode = useMemoizedFn((node: Node) => { if (!node) return; - // 获取所有与当前节点相交的节点,不包含折叠的节点 - const intersections = getIntersectingNodes(node).filter((node) => !node.data.isFolded); - // 获取所有与当前节点相交的节点中,类型为 loop 的节点 - const parentNode = intersections.find((item) => item.type === FlowNodeTypeEnum.loop); + // 获取所有与当前节点相交的节点 + const intersections = getIntersectingNodes(node); + // 获取所有与当前节点相交的节点中,类型为 loop 的节点且它不能是折叠状态 + const parentNode = intersections.find( + (item) => !item.data.isFolded && item.type === FlowNodeTypeEnum.loop + ); const unSupportedTypes = [ FlowNodeTypeEnum.workflowStart, @@ -548,8 +551,10 @@ export const useWorkflow = () => { const onConnectStart = useCallback( (event: any, params: OnConnectStartParams) => { if (!params.nodeId) return; - const sourceNode = nodes.find((node) => node.id === params.nodeId); - if (sourceNode?.data.isFolded) { + + // If node is folded, unfold it when connecting + const sourceNode = nodeList.find((node) => node.nodeId === params.nodeId); + if (sourceNode?.isFolded) { return onChangeNode({ nodeId: params.nodeId, type: 'attr', @@ -559,7 +564,7 @@ export const useWorkflow = () => { } setConnectingEdge(params); }, - [nodes, setConnectingEdge, onChangeNode] + [nodeList, setConnectingEdge, onChangeNode] ); const onConnectEnd = useCallback(() => { setConnectingEdge(undefined); diff --git a/projects/app/src/pages/app/detail/components/WorkflowComponents/Flow/nodes/NodeFormInput/index.tsx b/projects/app/src/pages/app/detail/components/WorkflowComponents/Flow/nodes/NodeFormInput/index.tsx index 1541d0c2c1a3..9f61f9f8edb1 100644 --- a/projects/app/src/pages/app/detail/components/WorkflowComponents/Flow/nodes/NodeFormInput/index.tsx +++ b/projects/app/src/pages/app/detail/components/WorkflowComponents/Flow/nodes/NodeFormInput/index.tsx @@ -120,7 +120,7 @@ const NodeFormInput = ({ data, selected }: NodeProps) => { return ( - {t('common:core.module.input_form')} + {t('workflow:user_form_input_config')}