diff --git a/packages/global/core/workflow/node/constant.ts b/packages/global/core/workflow/node/constant.ts index dd2872ee4..e734d15a0 100644 --- a/packages/global/core/workflow/node/constant.ts +++ b/packages/global/core/workflow/node/constant.ts @@ -118,6 +118,7 @@ export enum FlowNodeTypeEnum { queryExtension = 'cfr', tools = 'tools', stopTool = 'stopTool', + toolParams = 'toolParams', lafModule = 'lafModule', ifElseNode = 'ifElseNode', variableUpdate = 'variableUpdate', diff --git a/packages/global/core/workflow/runtime/type.d.ts b/packages/global/core/workflow/runtime/type.d.ts index bf0e40cc7..d03cdefed 100644 --- a/packages/global/core/workflow/runtime/type.d.ts +++ b/packages/global/core/workflow/runtime/type.d.ts @@ -186,6 +186,9 @@ export type DispatchNodeResponseType = { // form input formInputResult?: string; + + // tool params + toolParamsResult?: Record; }; export type DispatchNodeResultType = { diff --git a/packages/global/core/workflow/runtime/utils.ts b/packages/global/core/workflow/runtime/utils.ts index 967e71e10..b29614bd0 100644 --- a/packages/global/core/workflow/runtime/utils.ts +++ b/packages/global/core/workflow/runtime/utils.ts @@ -234,7 +234,8 @@ export const getReferenceVariableValue = ({ nodes: RuntimeNodeItemType[]; variables: Record; }) => { - if (!isReferenceValue(value)) { + const nodeIds = nodes.map((node) => node.nodeId); + if (!isReferenceValue(value, nodeIds)) { return value; } const sourceNodeId = value[0]; diff --git a/packages/global/core/workflow/template/constants.ts b/packages/global/core/workflow/template/constants.ts index ba2d94edc..bc2d9fada 100644 --- a/packages/global/core/workflow/template/constants.ts +++ b/packages/global/core/workflow/template/constants.ts @@ -33,17 +33,19 @@ import { LoopNode } from './system/loop/loop'; import { LoopStartNode } from './system/loop/loopStart'; import { LoopEndNode } from './system/loop/loopEnd'; import { FormInputNode } from './system/interactive/formInput'; +import { ToolParamsNode } from './system/toolParams'; const systemNodes: FlowNodeTemplateType[] = [ AiChatModule, TextEditorNode, AssignedAnswerModule, DatasetSearchModule, - DatasetConcatModule, - ToolModule, - StopToolNode, ClassifyQuestionModule, ContextExtractModule, + DatasetConcatModule, + ToolModule, + ToolParamsNode, + StopToolNode, ReadFilesNode, HttpNode468, AiQueryExtension, diff --git a/packages/global/core/workflow/template/system/toolParams.ts b/packages/global/core/workflow/template/system/toolParams.ts new file mode 100644 index 000000000..edfc0aa36 --- /dev/null +++ b/packages/global/core/workflow/template/system/toolParams.ts @@ -0,0 +1,20 @@ +import { FlowNodeTypeEnum } from '../../node/constant'; +import { FlowNodeTemplateType } from '../../type/node'; +import { FlowNodeTemplateTypeEnum } from '../../constants'; +import { getHandleConfig } from '../utils'; +import { i18nT } from '../../../../../web/i18n/utils'; + +export const ToolParamsNode: FlowNodeTemplateType = { + id: FlowNodeTypeEnum.toolParams, + templateType: FlowNodeTemplateTypeEnum.ai, + flowNodeType: FlowNodeTypeEnum.toolParams, + sourceHandle: getHandleConfig(true, true, true, true), + targetHandle: getHandleConfig(true, true, true, true), + avatar: 'core/workflow/template/toolParams', + name: i18nT('workflow:tool_params_config'), + intro: i18nT('workflow:intro_tool_params_config'), + version: '4811', + isTool: true, + inputs: [], + outputs: [] +}; diff --git a/packages/global/core/workflow/type/io.d.ts b/packages/global/core/workflow/type/io.d.ts index a19fa8eaa..eb46e6dda 100644 --- a/packages/global/core/workflow/type/io.d.ts +++ b/packages/global/core/workflow/type/io.d.ts @@ -50,6 +50,7 @@ export type FlowNodeInputItemType = InputComponentPropsType & { description?: string; // field desc required?: boolean; toolDescription?: string; // If this field is not empty, it is entered as a tool + enum?: string; // render components params canEdit?: boolean; // dynamic inputs diff --git a/packages/global/core/workflow/utils.ts b/packages/global/core/workflow/utils.ts index 7aff601b7..de3f1e5e6 100644 --- a/packages/global/core/workflow/utils.ts +++ b/packages/global/core/workflow/utils.ts @@ -297,8 +297,8 @@ export const formatEditorVariablePickerIcon = ( })); }; -export const isReferenceValue = (value: any): boolean => { - return Array.isArray(value) && value.length === 2 && typeof value[0] === 'string'; +export const isReferenceValue = (value: any, nodeIds: string[]): boolean => { + return Array.isArray(value) && value.length === 2 && nodeIds.includes(value[0]); }; export const getElseIFLabel = (i: number) => { diff --git a/packages/service/core/workflow/dispatch/agent/runTool/functionCall.ts b/packages/service/core/workflow/dispatch/agent/runTool/functionCall.ts index 8e18fb018..46f8a789f 100644 --- a/packages/service/core/workflow/dispatch/agent/runTool/functionCall.ts +++ b/packages/service/core/workflow/dispatch/agent/runTool/functionCall.ts @@ -60,12 +60,16 @@ export const runToolWithFunctionCall = async ( type: string; description: string; required?: boolean; + enum?: string[]; } > = {}; item.toolParams.forEach((item) => { + const isArray = item.valueType?.startsWith('array'); properties[item.key] = { - type: item.valueType || 'string', - description: item.toolDescription || '' + type: isArray ? 'array' : item.valueType || 'string', + ...(isArray && { items: { type: item.valueType?.slice(5).toLowerCase() || 'string' } }), + description: item.toolDescription || '', + enum: item.enum?.split('\n').filter(Boolean) || [] }; }); diff --git a/packages/service/core/workflow/dispatch/agent/runTool/promptCall.ts b/packages/service/core/workflow/dispatch/agent/runTool/promptCall.ts index c2826f5e5..c84a79098 100644 --- a/packages/service/core/workflow/dispatch/agent/runTool/promptCall.ts +++ b/packages/service/core/workflow/dispatch/agent/runTool/promptCall.ts @@ -68,12 +68,16 @@ export const runToolWithPromptCall = async ( type: string; description: string; required?: boolean; + enum?: string[]; } > = {}; item.toolParams.forEach((item) => { + const isArray = item.valueType?.startsWith('array'); properties[item.key] = { - type: 'string', - description: item.toolDescription || '' + type: isArray ? 'array' : item.valueType || 'string', + ...(isArray && { items: { type: item.valueType?.slice(5).toLowerCase() || 'string' } }), + description: item.toolDescription || '', + enum: item.enum?.split('\n').filter(Boolean) || [] }; }); diff --git a/packages/service/core/workflow/dispatch/agent/runTool/toolChoice.ts b/packages/service/core/workflow/dispatch/agent/runTool/toolChoice.ts index 2bf607337..20e5db282 100644 --- a/packages/service/core/workflow/dispatch/agent/runTool/toolChoice.ts +++ b/packages/service/core/workflow/dispatch/agent/runTool/toolChoice.ts @@ -70,13 +70,20 @@ export const runToolWithToolChoice = async ( { type: string; description: string; + enum?: string[]; required?: boolean; + items?: { + type: string; + }; } > = {}; item.toolParams.forEach((item) => { + const isArray = item.valueType?.startsWith('array'); properties[item.key] = { - type: item.valueType || 'string', - description: item.toolDescription || '' + type: isArray ? 'array' : item.valueType || 'string', + ...(isArray && { items: { type: item.valueType?.slice(5).toLowerCase() || 'string' } }), + description: item.toolDescription || '', + enum: item.enum?.split('\n').filter(Boolean) || [] }; }); @@ -138,7 +145,6 @@ export const runToolWithToolChoice = async ( toolModel ); - // console.log(JSON.stringify(requestBody, null, 2)); /* Run llm */ const ai = getAIApi({ timeout: 480000 diff --git a/packages/service/core/workflow/dispatch/agent/runTool/toolParams.ts b/packages/service/core/workflow/dispatch/agent/runTool/toolParams.ts new file mode 100644 index 000000000..3a9591d86 --- /dev/null +++ b/packages/service/core/workflow/dispatch/agent/runTool/toolParams.ts @@ -0,0 +1,17 @@ +import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants'; +import type { ModuleDispatchProps } from '@fastgpt/global/core/workflow/runtime/type'; +import { DispatchNodeResultType } from '@fastgpt/global/core/workflow/runtime/type'; + +export type Props = ModuleDispatchProps<{}>; +export type Response = DispatchNodeResultType<{}>; + +export const dispatchToolParams = (props: Props): Response => { + const { params } = props; + + return { + ...params, + [DispatchNodeResponseKeyEnum.nodeResponse]: { + toolParamsResult: params + } + }; +}; diff --git a/packages/service/core/workflow/dispatch/index.ts b/packages/service/core/workflow/dispatch/index.ts index 8bdf05373..b03a1e2fa 100644 --- a/packages/service/core/workflow/dispatch/index.ts +++ b/packages/service/core/workflow/dispatch/index.ts @@ -70,6 +70,7 @@ import { dispatchLoop } from './loop/runLoop'; import { dispatchLoopEnd } from './loop/runLoopEnd'; import { dispatchLoopStart } from './loop/runLoopStart'; import { dispatchFormInput } from './interactive/formInput'; +import { dispatchToolParams } from './agent/runTool/toolParams'; const callbackMap: Record = { [FlowNodeTypeEnum.workflowStart]: dispatchWorkflowStart, @@ -87,6 +88,7 @@ const callbackMap: Record = { [FlowNodeTypeEnum.queryExtension]: dispatchQueryExtension, [FlowNodeTypeEnum.tools]: dispatchRunTools, [FlowNodeTypeEnum.stopTool]: dispatchStopToolCall, + [FlowNodeTypeEnum.toolParams]: dispatchToolParams, [FlowNodeTypeEnum.lafModule]: dispatchLafRequest, [FlowNodeTypeEnum.ifElseNode]: dispatchIfElse, [FlowNodeTypeEnum.variableUpdate]: dispatchUpdateVariable, diff --git a/packages/web/components/common/Icon/constants.ts b/packages/web/components/common/Icon/constants.ts index 44c8d0682..5aebe7d2f 100644 --- a/packages/web/components/common/Icon/constants.ts +++ b/packages/web/components/common/Icon/constants.ts @@ -37,10 +37,10 @@ export const iconPaths = { 'common/importLight': () => import('./icons/common/importLight.svg'), 'common/info': () => import('./icons/common/info.svg'), 'common/inviteLight': () => import('./icons/common/inviteLight.svg'), + 'common/language/America': () => import('./icons/common/language/America.svg'), + 'common/language/China': () => import('./icons/common/language/China.svg'), 'common/language/en': () => import('./icons/common/language/en.svg'), 'common/language/zh': () => import('./icons/common/language/zh.svg'), - 'common/language/China': () => import('./icons/common/language/China.svg'), - 'common/language/America': () => import('./icons/common/language/America.svg'), 'common/leftArrowLight': () => import('./icons/common/leftArrowLight.svg'), 'common/line': () => import('./icons/common/line.svg'), 'common/lineChange': () => import('./icons/common/lineChange.svg'), @@ -212,8 +212,10 @@ export const iconPaths = { 'core/workflow/runSkip': () => import('./icons/core/workflow/runSkip.svg'), 'core/workflow/runSuccess': () => import('./icons/core/workflow/runSuccess.svg'), 'core/workflow/running': () => import('./icons/core/workflow/running.svg'), + 'core/workflow/template/BI': () => import('./icons/core/workflow/template/BI.svg'), 'core/workflow/template/FileRead': () => import('./icons/core/workflow/template/FileRead.svg'), 'core/workflow/template/aiChat': () => import('./icons/core/workflow/template/aiChat.svg'), + 'core/workflow/template/baseChart': () => import('./icons/core/workflow/template/baseChart.svg'), 'core/workflow/template/codeRun': () => import('./icons/core/workflow/template/codeRun.svg'), 'core/workflow/template/customFeedback': () => import('./icons/core/workflow/template/customFeedback.svg'), @@ -247,15 +249,17 @@ export const iconPaths = { 'core/workflow/template/reply': () => import('./icons/core/workflow/template/reply.svg'), 'core/workflow/template/runApp': () => import('./icons/core/workflow/template/runApp.svg'), 'core/workflow/template/stopTool': () => import('./icons/core/workflow/template/stopTool.svg'), - 'core/workflow/template/toolkitActive': () => - import('./icons/core/workflow/template/toolkitActive.svg'), - 'core/workflow/template/toolkitInactive': () => - import('./icons/core/workflow/template/toolkitInactive.svg'), 'core/workflow/template/systemConfig': () => import('./icons/core/workflow/template/systemConfig.svg'), 'core/workflow/template/textConcat': () => import('./icons/core/workflow/template/textConcat.svg'), 'core/workflow/template/toolCall': () => import('./icons/core/workflow/template/toolCall.svg'), + 'core/workflow/template/toolParams': () => + import('./icons/core/workflow/template/toolParams.svg'), + 'core/workflow/template/toolkitActive': () => + import('./icons/core/workflow/template/toolkitActive.svg'), + 'core/workflow/template/toolkitInactive': () => + import('./icons/core/workflow/template/toolkitInactive.svg'), 'core/workflow/template/userSelect': () => import('./icons/core/workflow/template/userSelect.svg'), 'core/workflow/template/variable': () => import('./icons/core/workflow/template/variable.svg'), @@ -267,8 +271,6 @@ export const iconPaths = { 'core/workflow/undo': () => import('./icons/core/workflow/undo.svg'), 'core/workflow/upload': () => import('./icons/core/workflow/upload.svg'), 'core/workflow/versionHistories': () => import('./icons/core/workflow/versionHistories.svg'), - 'core/workflow/template/baseChart': () => import('./icons/core/workflow/template/baseChart.svg'), - 'core/workflow/template/BI': () => import('./icons/core/workflow/template/BI.svg'), date: () => import('./icons/date.svg'), delete: () => import('./icons/delete.svg'), edit: () => import('./icons/edit.svg'), diff --git a/packages/web/components/common/Icon/icons/core/workflow/template/toolParams.svg b/packages/web/components/common/Icon/icons/core/workflow/template/toolParams.svg new file mode 100644 index 000000000..e67862b5f --- /dev/null +++ b/packages/web/components/common/Icon/icons/core/workflow/template/toolParams.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/packages/web/i18n/en/common.json b/packages/web/i18n/en/common.json index 36f4dba7d..8effd585e 100644 --- a/packages/web/i18n/en/common.json +++ b/packages/web/i18n/en/common.json @@ -28,6 +28,7 @@ "UnKnow": "Unknown", "Warning": "Warning", "add_new": "Add New", + "add_new_param": "Add new param", "back": "Back", "chose_condition": "Choose Condition", "chosen": "Chosen", @@ -1110,7 +1111,6 @@ "tag_list": "Tag List", "team_tag": "Team Tag", "textarea_variable_picker_tip": "Enter \"/\" to select a variable", - "tool_field": "Tool Field Parameter Configuration", "undefined_var": "Referenced an undefined variable, add it automatically?", "unit.character": "Character", "unit.minute": "Minute", diff --git a/packages/web/i18n/en/workflow.json b/packages/web/i18n/en/workflow.json index 315ab92ad..4b270115b 100644 --- a/packages/web/i18n/en/workflow.json +++ b/packages/web/i18n/en/workflow.json @@ -27,6 +27,7 @@ "create_link_error": "Error creating link", "custom_feedback": "Custom Feedback", "custom_input": "Custom Input", + "custom_tool_input": "Custom tool input", "dataset_quote_role": "Role", "dataset_quote_role_system_option_desc": "Historical records should be consistent first (recommended)", "dataset_quote_role_tip": "When set to System, the knowledge base reference content will be placed in the system message, which can ensure the continuity of the history record, but the constraint effect may not be good.\n\nWhen set to User, the knowledge base reference content will be placed in the user message, and the {{question}} variable location needs to be specified. \nIt will have a certain impact on the consistency of historical records, but usually the constraint effect is better.", @@ -78,6 +79,7 @@ "intro_text_concatenation": "Can process and output fixed or incoming text. Non-string type data will be converted to string type.", "intro_text_content_extraction": "Can extract specified data from text, such as SQL statements, search keywords, code, etc.", "intro_tool_call_termination": "This module needs to be configured for tool calls. When this module is executed, the current tool call will be forcibly terminated, and AI will no longer answer questions based on the tool call results.", + "intro_tool_params_config": " This module works with tool calls. It creates required tool parameters. Tool calls automatically generate parameter content and pass it to corresponding function blocks.", "is_empty": "Is Empty", "is_equal_to": "Is Equal To", "is_not_empty": "Is Not Empty", @@ -156,7 +158,17 @@ "text_to_extract": "Text to Extract", "these_variables_will_be_input_parameters_for_code_execution": "These variables will be input parameters for code execution", "tool_call_termination": "Tool Call Termination", + "tool_field": " Tool Field Parameter Configuration", "tool_input": "Tool Input", + "tool_params.enum_placeholder": "apple \npeach \nwatermelon", + "tool_params.enum_values": "Enum values", + "tool_params.enum_values_tip": "List the possible values for this field, one per line", + "tool_params.params_description": "Description", + "tool_params.params_description_placeholder": "Name/Age/SQL statement..", + "tool_params.params_name": "Name", + "tool_params.params_name_placeholder": "name/age/sql", + "tool_params.tool_params_result": "Tool params result", + "tool_params_config": "Tool params config", "trigger_after_application_completion": "Will be triggered after the application is fully completed", "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", diff --git a/packages/web/i18n/zh/common.json b/packages/web/i18n/zh/common.json index 38be438ce..518710adf 100644 --- a/packages/web/i18n/zh/common.json +++ b/packages/web/i18n/zh/common.json @@ -28,6 +28,7 @@ "UnKnow": "未知", "Warning": "提示", "add_new": "新增", + "add_new_param": "新增参数", "back": "返回", "chose_condition": "选择条件", "chosen": "已选", @@ -732,8 +733,8 @@ "core.module.template.empty_plugin": "空白插件", "core.module.template.empty_workflow": "空白工作流", "core.module.template.http body placeholder": "与 Apifox 相同的语法", - "core.module.template.self_output": "插件输出", "core.module.template.self_input": "插件输入", + "core.module.template.self_output": "插件输出", "core.module.template.system_config": "系统配置", "core.module.template.system_config_info": "可以配置应用的系统参数", "core.module.template.work_start": "流程开始", @@ -1109,7 +1110,6 @@ "tag_list": "标签列表", "team_tag": "团队标签", "textarea_variable_picker_tip": "输入\"/\"可选择变量", - "tool_field": "工具字段参数配置", "undefined_var": "引用了未定义的变量,是否自动添加?", "unit.character": "字符", "unit.minute": "分钟", diff --git a/packages/web/i18n/zh/workflow.json b/packages/web/i18n/zh/workflow.json index cc878ae31..7f308ee93 100644 --- a/packages/web/i18n/zh/workflow.json +++ b/packages/web/i18n/zh/workflow.json @@ -27,6 +27,7 @@ "create_link_error": "创建链接异常", "custom_feedback": "自定义反馈", "custom_input": "自定义输入", + "custom_tool_input": "自定义工具参数", "dataset_quote_role": "角色", "dataset_quote_role_system_option_desc": "历史记录连贯优先(推荐)", "dataset_quote_role_tip": "设置为 System 时,将会把知识库引用内容放置到 system 消息中,可以确保历史记录的连贯性,但约束效果可能不佳,需要多调试。\n设置为 User 时,将会把知识库引用内容放置到 user 消息中,并且需要指定 {{question}} 变量位置。会对历史记录连贯性有一定影响,但通常约束效果更优。", @@ -78,6 +79,7 @@ "intro_text_concatenation": "可对固定或传入的文本进行加工后输出,非字符串类型数据最终会转成字符串类型。", "intro_text_content_extraction": "可从文本中提取指定的数据,例如:sql语句、搜索关键词、代码等", "intro_tool_call_termination": "该模块需配置工具调用使用。当该模块被执行时,本次工具调用将会强制结束,并且不再调用AI针对工具调用结果回答问题。", + "intro_tool_params_config": "该模块需要配合工具调用使用。可以创建所需的工具参数,工具调用将自动生成参数内容并传给对应的功能块。", "is_empty": "为空", "is_equal_to": "等于", "is_not_empty": "不为空", @@ -156,7 +158,17 @@ "text_to_extract": "需要提取的文本", "these_variables_will_be_input_parameters_for_code_execution": "这些变量会作为代码的运行的输入参数", "tool_call_termination": "工具调用终止", + "tool_field": "工具参数配置", "tool_input": "工具参数", + "tool_params.enum_placeholder": "apple \npeach \nwatermelon", + "tool_params.enum_values": "枚举值(可选)", + "tool_params.enum_values_tip": "列举出该字段可能的值,每行一个", + "tool_params.params_description": "参数描述", + "tool_params.params_description_placeholder": "姓名/年龄/SQL 语句...", + "tool_params.params_name": "参数名", + "tool_params.params_name_placeholder": "name/age/sql", + "tool_params.tool_params_result": "参数配置结果", + "tool_params_config": "工具参数配置", "trigger_after_application_completion": "将在应用完全结束后触发", "update_link_error": "更新链接异常", "update_specified_node_output_or_global_variable": "可以更新指定节点的输出值或更新全局变量", diff --git a/projects/app/src/components/core/chat/components/WholeResponseModal.tsx b/projects/app/src/components/core/chat/components/WholeResponseModal.tsx index 81199f3d3..de9e3df6d 100644 --- a/projects/app/src/components/core/chat/components/WholeResponseModal.tsx +++ b/projects/app/src/components/core/chat/components/WholeResponseModal.tsx @@ -355,6 +355,12 @@ export const WholeResponseContent = ({ {/* form input */} + + {/* tool params */} + ) : null; }; diff --git a/projects/app/src/pages/app/detail/components/WorkflowComponents/Flow/NodeTemplatesModal.tsx b/projects/app/src/pages/app/detail/components/WorkflowComponents/Flow/NodeTemplatesModal.tsx index bab31efb0..e7c279eab 100644 --- a/projects/app/src/pages/app/detail/components/WorkflowComponents/Flow/NodeTemplatesModal.tsx +++ b/projects/app/src/pages/app/detail/components/WorkflowComponents/Flow/NodeTemplatesModal.tsx @@ -106,8 +106,12 @@ const NodeTemplatesModal = ({ isOpen, onClose }: ModuleTemplateListProps) => { if (item.flowNodeType === FlowNodeTypeEnum.lafModule && !feConfigs.lafEnv) { return false; } - // tool stop - if (!hasToolNode && item.flowNodeType === FlowNodeTypeEnum.stopTool) { + // tool stop or tool params + if ( + !hasToolNode && + (item.flowNodeType === FlowNodeTypeEnum.stopTool || + item.flowNodeType === FlowNodeTypeEnum.toolParams) + ) { return false; } return true; diff --git a/projects/app/src/pages/app/detail/components/WorkflowComponents/Flow/index.tsx b/projects/app/src/pages/app/detail/components/WorkflowComponents/Flow/index.tsx index cd5e28eca..eed9beee4 100644 --- a/projects/app/src/pages/app/detail/components/WorkflowComponents/Flow/index.tsx +++ b/projects/app/src/pages/app/detail/components/WorkflowComponents/Flow/index.tsx @@ -48,6 +48,7 @@ const nodeTypes: Record = { [FlowNodeTypeEnum.stopTool]: (data: NodeProps) => ( ), + [FlowNodeTypeEnum.toolParams]: dynamic(() => import('./nodes/NodeToolParams')), [FlowNodeTypeEnum.lafModule]: dynamic(() => import('./nodes/NodeLaf')), [FlowNodeTypeEnum.ifElseNode]: dynamic(() => import('./nodes/NodeIfElse')), [FlowNodeTypeEnum.variableUpdate]: dynamic(() => import('./nodes/NodeVariableUpdate')), diff --git a/projects/app/src/pages/app/detail/components/WorkflowComponents/Flow/nodes/NodeToolParams/ToolParamsEditModal.tsx b/projects/app/src/pages/app/detail/components/WorkflowComponents/Flow/nodes/NodeToolParams/ToolParamsEditModal.tsx new file mode 100644 index 000000000..801d13d5c --- /dev/null +++ b/projects/app/src/pages/app/detail/components/WorkflowComponents/Flow/nodes/NodeToolParams/ToolParamsEditModal.tsx @@ -0,0 +1,172 @@ +import { fnValueTypeSelect } from '@/web/core/workflow/constants/dataType'; +import { Box, Button, Flex, Input, ModalBody, ModalFooter, Textarea } from '@chakra-ui/react'; +import { FlowNodeInputItemType } from '@fastgpt/global/core/workflow/type/io'; +import MyModal from '@fastgpt/web/components/common/MyModal'; +import MySelect from '@fastgpt/web/components/common/MySelect'; +import React, { useCallback } from 'react'; +import { useForm } from 'react-hook-form'; +import { useTranslation } from 'react-i18next'; +import { defaultEditFormData } from '../render/RenderToolInput/EditFieldModal'; +import { useRequest2 } from '@fastgpt/web/hooks/useRequest'; +import { useContextSelector } from 'use-context-selector'; +import { WorkflowContext } from '../../../context'; +import { useToast } from '@fastgpt/web/hooks/useToast'; +import { FlowNodeOutputTypeEnum } from '@fastgpt/global/core/workflow/node/constant'; +import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel'; +import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip'; + +const ToolParamsEditModal = ({ + defaultValue = defaultEditFormData, + nodeId, + onClose +}: { + defaultValue: FlowNodeInputItemType; + nodeId: string; + onClose: () => void; +}) => { + const { t } = useTranslation(); + const { toast } = useToast(); + const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode); + + const { register, setValue, handleSubmit, watch } = useForm({ + defaultValues: defaultValue + }); + const valueType = watch('valueType'); + + const { runAsync: onClickSubmit } = useRequest2( + async (e: FlowNodeInputItemType) => { + e.key = e.key.trim(); + + const inputConfig: FlowNodeInputItemType = { + ...e, + description: e.toolDescription, + label: e.key + }; + if (defaultValue.key) { + // edit + onChangeNode({ + nodeId, + type: 'replaceInput', + key: defaultValue.key, + value: inputConfig + }); + onChangeNode({ + nodeId, + type: 'replaceOutput', + key: defaultValue.key, + value: { + ...e, + id: e.key, + label: e.key, + type: FlowNodeOutputTypeEnum.static + } + }); + } else { + // create + onChangeNode({ + nodeId, + type: 'addInput', + value: { + ...e, + label: e.key + } + }); + onChangeNode({ + nodeId, + type: 'addOutput', + value: { + ...e, + id: e.key, + label: e.key, + type: FlowNodeOutputTypeEnum.static + } + }); + } + onClose(); + }, + { + onSuccess: () => { + onClose(); + } + } + ); + + const onClickSubmitError = useCallback( + (e: Object) => { + for (const item of Object.values(e)) { + if (item.message) { + toast({ + status: 'warning', + title: item.message + }); + break; + } + } + }, + [toast] + ); + + return ( + + + + {t('common:core.module.Data Type')} + + { + setValue('valueType', e); + }} + /> + + + + {t('workflow:tool_params.params_name')} + + + + {t('workflow:tool_params.params_description')} + + + + + {t('workflow:tool_params.enum_values')} + + +