mirror of
https://github.com/labring/FastGPT.git
synced 2025-07-22 12:20:34 +00:00
feat: add tool params node & tool params add array type (#2824)
* add tool params node * add tool params enum * node response * tool add array type params * fix tool params * fix * fix * fix
This commit is contained in:
@@ -118,6 +118,7 @@ export enum FlowNodeTypeEnum {
|
||||
queryExtension = 'cfr',
|
||||
tools = 'tools',
|
||||
stopTool = 'stopTool',
|
||||
toolParams = 'toolParams',
|
||||
lafModule = 'lafModule',
|
||||
ifElseNode = 'ifElseNode',
|
||||
variableUpdate = 'variableUpdate',
|
||||
|
@@ -186,6 +186,9 @@ export type DispatchNodeResponseType = {
|
||||
|
||||
// form input
|
||||
formInputResult?: string;
|
||||
|
||||
// tool params
|
||||
toolParamsResult?: Record<string, any>;
|
||||
};
|
||||
|
||||
export type DispatchNodeResultType<T = {}> = {
|
||||
|
@@ -234,7 +234,8 @@ export const getReferenceVariableValue = ({
|
||||
nodes: RuntimeNodeItemType[];
|
||||
variables: Record<string, any>;
|
||||
}) => {
|
||||
if (!isReferenceValue(value)) {
|
||||
const nodeIds = nodes.map((node) => node.nodeId);
|
||||
if (!isReferenceValue(value, nodeIds)) {
|
||||
return value;
|
||||
}
|
||||
const sourceNodeId = value[0];
|
||||
|
@@ -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,
|
||||
|
20
packages/global/core/workflow/template/system/toolParams.ts
Normal file
20
packages/global/core/workflow/template/system/toolParams.ts
Normal file
@@ -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: []
|
||||
};
|
1
packages/global/core/workflow/type/io.d.ts
vendored
1
packages/global/core/workflow/type/io.d.ts
vendored
@@ -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
|
||||
|
@@ -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) => {
|
||||
|
@@ -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) || []
|
||||
};
|
||||
});
|
||||
|
||||
|
@@ -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) || []
|
||||
};
|
||||
});
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
}
|
||||
};
|
||||
};
|
@@ -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, Function> = {
|
||||
[FlowNodeTypeEnum.workflowStart]: dispatchWorkflowStart,
|
||||
@@ -87,6 +88,7 @@ const callbackMap: Record<FlowNodeTypeEnum, Function> = {
|
||||
[FlowNodeTypeEnum.queryExtension]: dispatchQueryExtension,
|
||||
[FlowNodeTypeEnum.tools]: dispatchRunTools,
|
||||
[FlowNodeTypeEnum.stopTool]: dispatchStopToolCall,
|
||||
[FlowNodeTypeEnum.toolParams]: dispatchToolParams,
|
||||
[FlowNodeTypeEnum.lafModule]: dispatchLafRequest,
|
||||
[FlowNodeTypeEnum.ifElseNode]: dispatchIfElse,
|
||||
[FlowNodeTypeEnum.variableUpdate]: dispatchUpdateVariable,
|
||||
|
@@ -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'),
|
||||
|
@@ -0,0 +1,10 @@
|
||||
<svg viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="36" height="36" fill="url(#paint0_linear_10998_23397)"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.9999 8C9.79074 8 7.99988 9.79086 7.99988 12V24C7.99988 26.2091 9.79074 28 11.9999 28H23.9999C26.209 28 27.9999 26.2091 27.9999 24V12C27.9999 9.79086 26.209 8 23.9999 8H11.9999ZM17.3939 13.6H24.1231C24.6202 13.6 25.0231 14.0029 25.0231 14.5C25.0231 14.997 24.6202 15.4 24.1231 15.4H17.3939C17.0436 16.223 16.2275 16.8 15.2767 16.8C14.3258 16.8 13.5097 16.223 13.1594 15.4H11.8767C11.3796 15.4 10.9767 14.997 10.9767 14.5C10.9767 14.0029 11.3796 13.6 11.8767 13.6H13.1594C13.5097 12.777 14.3258 12.2 15.2767 12.2C16.2275 12.2 17.0436 12.777 17.3939 13.6ZM18.6059 20.6H11.8767C11.3796 20.6 10.9767 21.0029 10.9767 21.5C10.9767 21.997 11.3796 22.4 11.8767 22.4H18.6058C18.9561 23.223 19.7722 23.8 20.7231 23.8C21.674 23.8 22.4901 23.223 22.8403 22.4H24.1231C24.6202 22.4 25.0231 21.997 25.0231 21.5C25.0231 21.0029 24.6202 20.6 24.1231 20.6H22.8403C22.4901 19.777 21.674 19.2 20.7231 19.2C19.7722 19.2 18.9561 19.777 18.6059 20.6Z" fill="white"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_10998_23397" x1="18" y1="0" x2="5.5" y2="33" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#91A9FE"/>
|
||||
<stop offset="1" stop-color="#BA84FF"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 1.3 KiB |
@@ -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",
|
||||
|
@@ -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",
|
||||
|
@@ -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": "分钟",
|
||||
|
@@ -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": "可以更新指定节点的输出值或更新全局变量",
|
||||
|
@@ -355,6 +355,12 @@ export const WholeResponseContent = ({
|
||||
|
||||
{/* form input */}
|
||||
<Row label={t('workflow:form_input_result')} value={activeModule?.formInputResult} />
|
||||
|
||||
{/* tool params */}
|
||||
<Row
|
||||
label={t('workflow:tool_params.tool_params_result')}
|
||||
value={activeModule?.toolParamsResult}
|
||||
/>
|
||||
</Box>
|
||||
) : null;
|
||||
};
|
||||
|
@@ -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;
|
||||
|
@@ -48,6 +48,7 @@ const nodeTypes: Record<FlowNodeTypeEnum, any> = {
|
||||
[FlowNodeTypeEnum.stopTool]: (data: NodeProps<FlowNodeItemType>) => (
|
||||
<NodeSimple {...data} minW={'100px'} maxW={'300px'} />
|
||||
),
|
||||
[FlowNodeTypeEnum.toolParams]: dynamic(() => import('./nodes/NodeToolParams')),
|
||||
[FlowNodeTypeEnum.lafModule]: dynamic(() => import('./nodes/NodeLaf')),
|
||||
[FlowNodeTypeEnum.ifElseNode]: dynamic(() => import('./nodes/NodeIfElse')),
|
||||
[FlowNodeTypeEnum.variableUpdate]: dynamic(() => import('./nodes/NodeVariableUpdate')),
|
||||
|
@@ -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<FlowNodeInputItemType>({
|
||||
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 (
|
||||
<MyModal isOpen iconSrc="modal/edit" title={t('workflow:tool_field')} onClose={onClose}>
|
||||
<ModalBody>
|
||||
<Flex alignItems={'center'} mb={5}>
|
||||
<FormLabel flex={'0 0 80px'}>{t('common:core.module.Data Type')}</FormLabel>
|
||||
<Box flex={'1 0 0'}>
|
||||
<MySelect
|
||||
list={fnValueTypeSelect}
|
||||
value={valueType}
|
||||
onchange={(e: any) => {
|
||||
setValue('valueType', e);
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</Flex>
|
||||
<Flex alignItems={'center'} mb={5}>
|
||||
<FormLabel flex={'0 0 80px'}>{t('workflow:tool_params.params_name')}</FormLabel>
|
||||
<Input
|
||||
bg={'myGray.50'}
|
||||
{...register('key', {
|
||||
required: true,
|
||||
pattern: {
|
||||
value: /^[a-zA-Z]+[0-9]*$/,
|
||||
message: t('common:info.felid_message')
|
||||
}
|
||||
})}
|
||||
placeholder={t('workflow:tool_params.params_name_placeholder')}
|
||||
/>
|
||||
</Flex>
|
||||
<Flex alignItems={'center'} mb={5}>
|
||||
<FormLabel flex={'0 0 80px'}>{t('workflow:tool_params.params_description')}</FormLabel>
|
||||
<Input
|
||||
bg={'myGray.50'}
|
||||
{...register('toolDescription', {
|
||||
required: true
|
||||
})}
|
||||
placeholder={t('workflow:tool_params.params_description_placeholder')}
|
||||
/>
|
||||
</Flex>
|
||||
<Box>
|
||||
<Flex alignItems={'center'} mb={2}>
|
||||
<FormLabel>{t('workflow:tool_params.enum_values')}</FormLabel>
|
||||
<QuestionTip label={t('workflow:tool_params.enum_values_tip')} />
|
||||
</Flex>
|
||||
<Textarea
|
||||
bg={'myGray.50'}
|
||||
{...register('enum')}
|
||||
placeholder={t('workflow:tool_params.enum_placeholder')}
|
||||
/>
|
||||
</Box>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button variant={'whiteBase'} mr={2} onClick={onClose}>
|
||||
{t('common:common.Close')}
|
||||
</Button>
|
||||
<Button onClick={handleSubmit((data) => onClickSubmit(data), onClickSubmitError)}>
|
||||
{t('common:common.Confirm')}
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</MyModal>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(ToolParamsEditModal);
|
@@ -0,0 +1,117 @@
|
||||
import { FlowNodeItemType } from '@fastgpt/global/core/workflow/type/node';
|
||||
import { NodeProps } from 'reactflow';
|
||||
import NodeCard from '../render/NodeCard';
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import Container from '../../components/Container';
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Flex,
|
||||
FormLabel,
|
||||
Table,
|
||||
TableContainer,
|
||||
Tbody,
|
||||
Td,
|
||||
Th,
|
||||
Thead,
|
||||
Tr
|
||||
} from '@chakra-ui/react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { SmallAddIcon } from '@chakra-ui/icons';
|
||||
import { FlowNodeInputItemType } from '@fastgpt/global/core/workflow/type/io';
|
||||
import { defaultEditFormData } from '../render/RenderToolInput/EditFieldModal';
|
||||
import ToolParamsEditModal from './ToolParamsEditModal';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
import { WorkflowContext } from '../../../context';
|
||||
|
||||
const NodeToolParams = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
||||
const { t } = useTranslation();
|
||||
const [editField, setEditField] = useState<FlowNodeInputItemType>();
|
||||
const { nodeId, inputs } = data;
|
||||
const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode);
|
||||
|
||||
const Render = useMemo(() => {
|
||||
return (
|
||||
<NodeCard selected={selected} {...data}>
|
||||
<Container>
|
||||
<Flex alignItems={'center'} justifyContent={'space-between'} mb={1.5}>
|
||||
<FormLabel>{t('workflow:custom_tool_input')}</FormLabel>
|
||||
<Button
|
||||
variant={'whiteBase'}
|
||||
leftIcon={<SmallAddIcon />}
|
||||
iconSpacing={1}
|
||||
size={'sm'}
|
||||
onClick={() => setEditField(defaultEditFormData)}
|
||||
>
|
||||
{t('common:add_new_param')}
|
||||
</Button>
|
||||
{!!editField && (
|
||||
<ToolParamsEditModal
|
||||
defaultValue={editField}
|
||||
nodeId={nodeId}
|
||||
onClose={() => setEditField(undefined)}
|
||||
/>
|
||||
)}
|
||||
</Flex>
|
||||
<Box borderRadius={'md'} overflow={'hidden'} border={'base'}>
|
||||
<TableContainer>
|
||||
<Table bg={'white'}>
|
||||
<Thead>
|
||||
<Tr>
|
||||
<Th>{t('workflow:tool_params.params_name')}</Th>
|
||||
<Th>{t('workflow:tool_params.params_description')}</Th>
|
||||
<Th>{t('common:common.Operation')}</Th>
|
||||
</Tr>
|
||||
</Thead>
|
||||
<Tbody>
|
||||
{inputs.map((item, index) => (
|
||||
<Tr
|
||||
key={index}
|
||||
position={'relative'}
|
||||
whiteSpace={'pre-wrap'}
|
||||
wordBreak={'break-all'}
|
||||
>
|
||||
<Td>{item.key}</Td>
|
||||
<Td>{item.toolDescription}</Td>
|
||||
<Td whiteSpace={'nowrap'}>
|
||||
<MyIcon
|
||||
mr={3}
|
||||
name={'common/settingLight'}
|
||||
w={'16px'}
|
||||
cursor={'pointer'}
|
||||
onClick={() => setEditField(item)}
|
||||
/>
|
||||
<MyIcon
|
||||
name={'delete'}
|
||||
w={'16px'}
|
||||
cursor={'pointer'}
|
||||
onClick={() => {
|
||||
onChangeNode({
|
||||
nodeId,
|
||||
type: 'delInput',
|
||||
key: item.key
|
||||
});
|
||||
onChangeNode({
|
||||
nodeId,
|
||||
type: 'delOutput',
|
||||
key: item.key
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</Td>
|
||||
</Tr>
|
||||
))}
|
||||
</Tbody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
</Box>
|
||||
</Container>
|
||||
</NodeCard>
|
||||
);
|
||||
}, [selected, data, t, editField, inputs, onChangeNode, nodeId]);
|
||||
|
||||
return Render;
|
||||
};
|
||||
|
||||
export default React.memo(NodeToolParams);
|
@@ -103,8 +103,9 @@ const NodeVariableUpdate = ({ data, selected }: NodeProps<FlowNodeItemType>) =>
|
||||
(item) => item.renderType === updateItem.renderType
|
||||
);
|
||||
|
||||
const nodeIds = nodeList.map((node) => node.nodeId);
|
||||
const handleUpdate = (newValue: ReferenceValueProps | string) => {
|
||||
if (isReferenceValue(newValue)) {
|
||||
if (isReferenceValue(newValue, nodeIds)) {
|
||||
onUpdateList(
|
||||
updateList.map((update, i) =>
|
||||
i === index ? { ...update, value: newValue as ReferenceValueProps } : update
|
||||
|
@@ -85,7 +85,7 @@ const EditFieldModal = ({
|
||||
);
|
||||
|
||||
return (
|
||||
<MyModal isOpen iconSrc="modal/edit" title={t('common:tool_field')} onClose={onClose}>
|
||||
<MyModal isOpen iconSrc="modal/edit" title={t('workflow:tool_field')} onClose={onClose}>
|
||||
<ModalBody>
|
||||
<Flex alignItems={'center'} mb={5}>
|
||||
<Box flex={'0 0 80px'}>{t('common:common.Require Input')}</Box>
|
||||
|
@@ -12,5 +12,17 @@ export const fnValueTypeSelect = [
|
||||
{
|
||||
label: WorkflowIOValueTypeEnum.boolean,
|
||||
value: WorkflowIOValueTypeEnum.boolean
|
||||
},
|
||||
{
|
||||
label: WorkflowIOValueTypeEnum.arrayString,
|
||||
value: WorkflowIOValueTypeEnum.arrayString
|
||||
},
|
||||
{
|
||||
label: WorkflowIOValueTypeEnum.arrayNumber,
|
||||
value: WorkflowIOValueTypeEnum.arrayNumber
|
||||
},
|
||||
{
|
||||
label: WorkflowIOValueTypeEnum.arrayBoolean,
|
||||
value: WorkflowIOValueTypeEnum.arrayBoolean
|
||||
}
|
||||
];
|
||||
|
Reference in New Issue
Block a user