diff --git a/web/app/components/workflow/block-selector/data-sources.tsx b/web/app/components/workflow/block-selector/data-sources.tsx index 0dffe9df99..6b2293afa5 100644 --- a/web/app/components/workflow/block-selector/data-sources.tsx +++ b/web/app/components/workflow/block-selector/data-sources.tsx @@ -44,7 +44,6 @@ const DataSources = ({ datasource_name: toolDefaultValue?.tool_name, datasource_label: toolDefaultValue?.tool_label, title: toolDefaultValue?.title, - output_schema: toolDefaultValue?.output_schema, }) }, [onSelect]) const { enable_marketplace } = useGlobalPublicStore(s => s.systemFeatures) diff --git a/web/app/components/workflow/block-selector/tool/action-item.tsx b/web/app/components/workflow/block-selector/tool/action-item.tsx index 4a2ed4a87b..1005758d43 100644 --- a/web/app/components/workflow/block-selector/tool/action-item.tsx +++ b/web/app/components/workflow/block-selector/tool/action-item.tsx @@ -69,7 +69,6 @@ const ToolItem: FC = ({ tool_description: payload.description[language], title: payload.label[language], is_team_authorization: provider.is_team_authorization, - output_schema: payload.output_schema, paramSchemas: payload.parameters, params, meta: provider.meta, diff --git a/web/app/components/workflow/block-selector/tool/tool.tsx b/web/app/components/workflow/block-selector/tool/tool.tsx index 83ae062737..6004bb119a 100644 --- a/web/app/components/workflow/block-selector/tool/tool.tsx +++ b/web/app/components/workflow/block-selector/tool/tool.tsx @@ -90,7 +90,6 @@ const Tool: FC = ({ tool_description: tool.description[language], title: tool.label[language], is_team_authorization: payload.is_team_authorization, - output_schema: tool.output_schema, paramSchemas: tool.parameters, params, } @@ -122,7 +121,6 @@ const Tool: FC = ({ } if (!hasSearchText && !isFold) setFold(true) - // eslint-disable-next-line react-hooks/exhaustive-deps }, [hasSearchText]) const FoldIcon = isFold ? RiArrowRightSLine : RiArrowDownSLine @@ -171,7 +169,6 @@ const Tool: FC = ({ tool_description: tool.description[language], title: tool.label[language], is_team_authorization: payload.is_team_authorization, - output_schema: tool.output_schema, paramSchemas: tool.parameters, params, }) diff --git a/web/app/components/workflow/block-selector/types.ts b/web/app/components/workflow/block-selector/types.ts index e152a54b7f..60aa8b2420 100644 --- a/web/app/components/workflow/block-selector/types.ts +++ b/web/app/components/workflow/block-selector/types.ts @@ -35,7 +35,6 @@ export type ToolDefaultValue = { is_team_authorization: boolean params: Record paramSchemas: Record[] - output_schema: Record credential_id?: string meta?: PluginMeta } @@ -46,7 +45,6 @@ export type DataSourceDefaultValue = { provider_name: string datasource_name: string datasource_label: string - output_schema?: Record } export type ToolValue = { diff --git a/web/app/components/workflow/block-selector/utils.ts b/web/app/components/workflow/block-selector/utils.ts index c1eee2202c..9b7a5fc076 100644 --- a/web/app/components/workflow/block-selector/utils.ts +++ b/web/app/components/workflow/block-selector/utils.ts @@ -25,9 +25,12 @@ export const transformDataSourceToTool = (dataSourceItem: DataSourceItem) => { description: datasource.description, parameters: datasource.parameters, labels: [], - output_schema: datasource.output_schema || {}, + output_schema: datasource.output_schema, } as Tool }), credentialsSchema: dataSourceItem.declaration.credentials_schema || [], + meta: { + version: '', + }, } } diff --git a/web/app/components/workflow/hooks/use-workflow-variables.ts b/web/app/components/workflow/hooks/use-workflow-variables.ts index cb1078efd7..429aaef73e 100644 --- a/web/app/components/workflow/hooks/use-workflow-variables.ts +++ b/web/app/components/workflow/hooks/use-workflow-variables.ts @@ -35,6 +35,11 @@ export const useWorkflowVariables = () => { conversationVariables, environmentVariables, ragPipelineVariables, + buildInTools, + customTools, + workflowTools, + mcpTools, + dataSourceList, } = workflowStore.getState() return toNodeAvailableVars({ parentNode, @@ -45,6 +50,13 @@ export const useWorkflowVariables = () => { conversationVariables: (isChatMode && !hideChatVar) ? conversationVariables : [], ragVariables: ragPipelineVariables, filterVar, + allPluginInfoList: { + buildInTools, + customTools, + workflowTools, + mcpTools, + dataSourceList: dataSourceList ?? [], + }, }) }, [t, workflowStore]) @@ -69,6 +81,11 @@ export const useWorkflowVariables = () => { conversationVariables, environmentVariables, ragPipelineVariables, + buildInTools, + customTools, + workflowTools, + mcpTools, + dataSourceList, } = workflowStore.getState() return getVarType({ parentNode, @@ -81,6 +98,13 @@ export const useWorkflowVariables = () => { environmentVariables, conversationVariables, ragVariables: ragPipelineVariables, + allPluginInfoList: { + buildInTools, + customTools, + workflowTools, + mcpTools, + dataSourceList: dataSourceList ?? [], + }, }) }, [workflowStore]) diff --git a/web/app/components/workflow/nodes/_base/components/variable/utils.ts b/web/app/components/workflow/nodes/_base/components/variable/utils.ts index 0181c08227..5270dbe7b6 100644 --- a/web/app/components/workflow/nodes/_base/components/variable/utils.ts +++ b/web/app/components/workflow/nodes/_base/components/variable/utils.ts @@ -19,7 +19,7 @@ import { OUTPUT_FILE_SUB_VARIABLES } from '../../../constants' import type { DocExtractorNodeType } from '../../../document-extractor/types' import { BlockEnum, InputVarType, VarType } from '@/app/components/workflow/types' import type { StartNodeType } from '@/app/components/workflow/nodes/start/types' -import type { ConversationVariable, EnvironmentVariable, Node, NodeOutPutVar, ValueSelector, Var } from '@/app/components/workflow/types' +import type { ConversationVariable, EnvironmentVariable, Node, NodeOutPutVar, ToolWithProvider, ValueSelector, Var } from '@/app/components/workflow/types' import type { VariableAssignerNodeType } from '@/app/components/workflow/nodes/variable-assigner/types' import type { Field as StructField } from '@/app/components/workflow/nodes/llm/types' import type { RAGPipelineVariable } from '@/models/pipeline' @@ -35,12 +35,12 @@ import { TEMPLATE_TRANSFORM_OUTPUT_STRUCT, TOOL_OUTPUT_STRUCT, } from '@/app/components/workflow/constants' +import ToolNodeDefault from '@/app/components/workflow/nodes/tool/default' import DataSourceNodeDefault from '@/app/components/workflow/nodes/data-source/default' import type { DataSourceNodeType } from '@/app/components/workflow/nodes/data-source/types' import type { PromptItem } from '@/models/debug' import { VAR_REGEX } from '@/config' import type { AgentNodeType } from '../../../agent/types' -import { getOutputVariableAlias } from '@/app/components/workflow/utils/tool' export const isSystemVar = (valueSelector: ValueSelector) => { return valueSelector[0] === 'sys' || valueSelector[1] === 'sys' @@ -229,6 +229,7 @@ const formatItem = ( item: any, isChatMode: boolean, filterVar: (payload: Var, selector: ValueSelector) => boolean, + allPluginInfoList: Record, ragVars?: Var[], ): NodeOutPutVar => { const { id, data } = item @@ -398,43 +399,8 @@ const formatItem = ( } case BlockEnum.Tool: { - const { - output_schema, - } = data as ToolNodeType - if (!output_schema) { - res.vars = TOOL_OUTPUT_STRUCT - } - else { - const outputSchema: any[] = [] - Object.keys(output_schema.properties).forEach((outputKey) => { - const output = output_schema.properties[outputKey] - const dataType = output.type - const alias = getOutputVariableAlias(output.properties) - let type = dataType === 'array' - ? `array[${output.items?.type.slice(0, 1).toLocaleLowerCase()}${output.items?.type.slice(1)}]` - : `${output.type.slice(0, 1).toLocaleLowerCase()}${output.type.slice(1)}` - - if (type === VarType.object && alias === 'file') - type = VarType.file - - outputSchema.push({ - variable: outputKey, - type, - description: output.description, - alias, - children: output.type === 'object' ? { - schema: { - type: 'object', - properties: output.properties, - }, - } : undefined, - }) - }) - res.vars = [ - ...TOOL_OUTPUT_STRUCT, - ...outputSchema, - ] - } + const toolOutputVars = ToolNodeDefault.getOutputVars?.(data as ToolNodeType, allPluginInfoList) || [] + res.vars = toolOutputVars break } @@ -529,7 +495,7 @@ const formatItem = ( case BlockEnum.DataSource: { const payload = data as DataSourceNodeType - const dataSourceVars = DataSourceNodeDefault.getOutputVars?.(payload, ragVars) || [] + const dataSourceVars = DataSourceNodeDefault.getOutputVars?.(payload, allPluginInfoList, ragVars) || [] res.vars = dataSourceVars break } @@ -658,6 +624,7 @@ export const toNodeOutputVars = ( environmentVariables: EnvironmentVariable[] = [], conversationVariables: ConversationVariable[] = [], ragVariables: RAGPipelineVariable[] = [], + allPluginInfoList: Record, ): NodeOutPutVar[] => { // ENV_NODE data format const ENV_NODE = { @@ -708,7 +675,7 @@ export const toNodeOutputVars = ( if (node.data.type === BlockEnum.DataSource) ragVariablesInDataSource = ragVariables.filter(ragVariable => ragVariable.belong_to_node_id === node.id) return { - ...formatItem(node, isChatMode, filterVar, ragVariablesInDataSource.map( + ...formatItem(node, isChatMode, filterVar, allPluginInfoList, ragVariablesInDataSource.map( (ragVariable: RAGPipelineVariable) => ({ variable: `rag.${node.id}.${ragVariable.variable}`, type: inputVarTypeToVarType(ragVariable.type as any), @@ -835,6 +802,7 @@ export const getVarType = ({ environmentVariables = [], conversationVariables = [], ragVariables = [], + allPluginInfoList, }: { valueSelector: ValueSelector parentNode?: Node | null @@ -846,6 +814,7 @@ export const getVarType = ({ environmentVariables?: EnvironmentVariable[] conversationVariables?: ConversationVariable[] ragVariables?: RAGPipelineVariable[] + allPluginInfoList: Record }): VarType => { if (isConstant) return VarType.string @@ -857,6 +826,7 @@ export const getVarType = ({ environmentVariables, conversationVariables, ragVariables, + allPluginInfoList, ) const isIterationInnerVar = parentNode?.data.type === BlockEnum.Iteration @@ -982,6 +952,7 @@ export const toNodeAvailableVars = ({ conversationVariables, ragVariables, filterVar, + allPluginInfoList, }: { parentNode?: Node | null t?: any @@ -995,6 +966,7 @@ export const toNodeAvailableVars = ({ // rag variables ragVariables?: RAGPipelineVariable[] filterVar: (payload: Var, selector: ValueSelector) => boolean + allPluginInfoList: Record }): NodeOutPutVar[] => { const beforeNodesOutputVars = toNodeOutputVars( beforeNodes, @@ -1003,6 +975,7 @@ export const toNodeAvailableVars = ({ environmentVariables, conversationVariables, ragVariables, + allPluginInfoList, ) const isInIteration = parentNode?.data.type === BlockEnum.Iteration if (isInIteration) { @@ -1015,6 +988,7 @@ export const toNodeAvailableVars = ({ isChatMode, environmentVariables, conversationVariables, + allPluginInfoList, }) const itemChildren = itemType === VarType.file ? { diff --git a/web/app/components/workflow/nodes/_base/hooks/use-one-step-run.ts b/web/app/components/workflow/nodes/_base/hooks/use-one-step-run.ts index 9f77e66d12..f682dfe13f 100644 --- a/web/app/components/workflow/nodes/_base/hooks/use-one-step-run.ts +++ b/web/app/components/workflow/nodes/_base/hooks/use-one-step-run.ts @@ -128,7 +128,19 @@ const useOneStepRun = ({ const availableNodes = getBeforeNodesInSameBranch(id) const availableNodesIncludeParent = getBeforeNodesInSameBranchIncludeParent(id) - const allOutputVars = toNodeOutputVars(availableNodes, isChatMode, undefined, undefined, conversationVariables) + const buildInTools = useStore(s => s.buildInTools) + const customTools = useStore(s => s.customTools) + const workflowTools = useStore(s => s.workflowTools) + const mcpTools = useStore(s => s.mcpTools) + const dataSourceList = useStore(s => s.dataSourceList) + const allPluginInfoList = { + buildInTools, + customTools, + workflowTools, + mcpTools, + dataSourceList: dataSourceList ?? [], + } + const allOutputVars = toNodeOutputVars(availableNodes, isChatMode, undefined, undefined, conversationVariables, [], allPluginInfoList) const getVar = (valueSelector: ValueSelector): Var | undefined => { const isSystem = valueSelector[0] === 'sys' const targetVar = allOutputVars.find(item => isSystem ? !!item.isStartNode : item.nodeId === valueSelector[0]) @@ -188,11 +200,11 @@ const useOneStepRun = ({ const isPaused = isPausedRef.current // The backend don't support pause the single run, so the frontend handle the pause state. - if(isPaused) + if (isPaused) return const canRunLastRun = !isRunAfterSingleRun || runningStatus === NodeRunningStatus.Succeeded - if(!canRunLastRun) { + if (!canRunLastRun) { doSetRunResult(data) return } @@ -202,9 +214,9 @@ const useOneStepRun = ({ const { getNodes } = store.getState() const nodes = getNodes() appendNodeInspectVars(id, vars, nodes) - if(data?.status === NodeRunningStatus.Succeeded) { + if (data?.status === NodeRunningStatus.Succeeded) { invalidLastRun() - if(isStartNode) + if (isStartNode) invalidateSysVarValues() invalidateConversationVarValues() // loop, iteration, variable assigner node can update the conversation variables, but to simple the logic(some nodes may also can update in the future), all nodes refresh. } @@ -221,21 +233,21 @@ const useOneStepRun = ({ }) } const checkValidWrap = () => { - if(!checkValid) + if (!checkValid) return { isValid: true, errorMessage: '' } const res = checkValid(data, t, moreDataForCheckValid) - if(!res.isValid) { - handleNodeDataUpdate({ - id, - data: { - ...data, - _isSingleRun: false, - }, - }) - Toast.notify({ - type: 'error', - message: res.errorMessage, - }) + if (!res.isValid) { + handleNodeDataUpdate({ + id, + data: { + ...data, + _isSingleRun: false, + }, + }) + Toast.notify({ + type: 'error', + message: res.errorMessage, + }) } return res } @@ -254,7 +266,6 @@ const useOneStepRun = ({ const { isValid } = checkValidWrap() setCanShowSingleRun(isValid) } - // eslint-disable-next-line react-hooks/exhaustive-deps }, [data._isSingleRun]) useEffect(() => { @@ -296,9 +307,9 @@ const useOneStepRun = ({ if (!isIteration && !isLoop) { const isStartNode = data.type === BlockEnum.Start const postData: Record = {} - if(isStartNode) { + if (isStartNode) { const { '#sys.query#': query, '#sys.files#': files, ...inputs } = submitData - if(isChatMode) + if (isChatMode) postData.conversation_id = '' postData.inputs = inputs @@ -320,7 +331,7 @@ const useOneStepRun = ({ { onWorkflowStarted: noop, onWorkflowFinished: (params) => { - if(isPausedRef.current) + if (isPausedRef.current) return handleNodeDataUpdate({ id, @@ -399,7 +410,7 @@ const useOneStepRun = ({ setIterationRunResult(newIterationRunResult) }, onError: () => { - if(isPausedRef.current) + if (isPausedRef.current) return handleNodeDataUpdate({ id, @@ -423,7 +434,7 @@ const useOneStepRun = ({ { onWorkflowStarted: noop, onWorkflowFinished: (params) => { - if(isPausedRef.current) + if (isPausedRef.current) return handleNodeDataUpdate({ id, @@ -503,7 +514,7 @@ const useOneStepRun = ({ setLoopRunResult(newLoopRunResult) }, onError: () => { - if(isPausedRef.current) + if (isPausedRef.current) return handleNodeDataUpdate({ id, @@ -525,7 +536,7 @@ const useOneStepRun = ({ hasError = true invalidLastRun() if (!isIteration && !isLoop) { - if(isPausedRef.current) + if (isPausedRef.current) return handleNodeDataUpdate({ id, @@ -547,11 +558,11 @@ const useOneStepRun = ({ }) } } - if(isPausedRef.current) + if (isPausedRef.current) return if (!isIteration && !isLoop && !hasError) { - if(isPausedRef.current) + if (isPausedRef.current) return handleNodeDataUpdate({ id, diff --git a/web/app/components/workflow/nodes/data-source/default.ts b/web/app/components/workflow/nodes/data-source/default.ts index 579b3e6304..6e7d4cd2de 100644 --- a/web/app/components/workflow/nodes/data-source/default.ts +++ b/web/app/components/workflow/nodes/data-source/default.ts @@ -54,15 +54,21 @@ const nodeDefault: NodeDefault = { errorMessage, } }, - getOutputVars(payload, ragVars = []) { + getOutputVars(payload, allPluginInfoList, ragVars = []) { const { + plugin_id, + datasource_name, provider_type, } = payload const isLocalFile = provider_type === DataSourceClassification.localFile + const currentDataSource = allPluginInfoList.dataSourceList?.find((ds: any) => ds.plugin_id === plugin_id) + const currentDataSourceItem = currentDataSource?.tools?.find((tool: any) => tool.name === datasource_name) + const output_schema = currentDataSourceItem?.output_schema const dynamicOutputSchema: any[] = [] - if (payload.output_schema?.properties) { - Object.keys(payload.output_schema.properties).forEach((outputKey) => { - const output = payload.output_schema!.properties[outputKey] + + if (output_schema?.properties) { + Object.keys(output_schema.properties).forEach((outputKey) => { + const output = output_schema.properties[outputKey] const dataType = output.type const alias = getOutputVariableAlias(output.properties) let type = dataType === 'array' diff --git a/web/app/components/workflow/nodes/data-source/hooks/use-config.ts b/web/app/components/workflow/nodes/data-source/hooks/use-config.ts index 10153f622d..8624bb98f5 100644 --- a/web/app/components/workflow/nodes/data-source/hooks/use-config.ts +++ b/web/app/components/workflow/nodes/data-source/hooks/use-config.ts @@ -71,7 +71,7 @@ export const useConfig = (id: string, dataSourceList?: any[]) => { const res: any[] = [] if (!output_schema || !output_schema.properties) - return [] + return res Object.keys(output_schema.properties).forEach((outputKey) => { const output = output_schema.properties[outputKey] diff --git a/web/app/components/workflow/nodes/data-source/types.ts b/web/app/components/workflow/nodes/data-source/types.ts index 60bcfd58ee..3e6e8e6c64 100644 --- a/web/app/components/workflow/nodes/data-source/types.ts +++ b/web/app/components/workflow/nodes/data-source/types.ts @@ -27,10 +27,6 @@ export type DataSourceNodeType = CommonNodeType & { datasource_label: string datasource_parameters: ToolVarInputs datasource_configurations: Record - output_schema?: { - type: string - properties: Record - } } export type CustomRunFormProps = { diff --git a/web/app/components/workflow/nodes/if-else/components/condition-list/condition-item.tsx b/web/app/components/workflow/nodes/if-else/components/condition-list/condition-item.tsx index d20bd31125..44c29a9bcb 100644 --- a/web/app/components/workflow/nodes/if-else/components/condition-list/condition-item.tsx +++ b/web/app/components/workflow/nodes/if-else/components/condition-list/condition-item.tsx @@ -92,6 +92,11 @@ const ConditionItem = ({ const workflowStore = useWorkflowStore() const { setControlPromptEditorRerenderKey, + buildInTools, + customTools, + mcpTools, + workflowTools, + dataSourceList, } = workflowStore.getState() const doUpdateCondition = useCallback((newCondition: Condition) => { @@ -207,6 +212,13 @@ const ConditionItem = ({ valueSelector, availableNodes, isChatMode, + allPluginInfoList: { + buildInTools, + customTools, + mcpTools, + workflowTools, + dataSourceList: dataSourceList ?? [], + }, }) const newCondition = produce(condition, (draft) => { diff --git a/web/app/components/workflow/nodes/iteration/use-config.ts b/web/app/components/workflow/nodes/iteration/use-config.ts index c8656aa937..0b48193569 100644 --- a/web/app/components/workflow/nodes/iteration/use-config.ts +++ b/web/app/components/workflow/nodes/iteration/use-config.ts @@ -14,6 +14,7 @@ import type { VarType as VarKindType } from '@/app/components/workflow/nodes/too import type { Item } from '@/app/components/base/select' import useInspectVarsCrud from '../../hooks/use-inspect-vars-crud' import { isEqual } from 'lodash-es' +import { useStore } from '../../store' const useConfig = (id: string, payload: IterationNodeType) => { const { @@ -39,7 +40,19 @@ const useConfig = (id: string, payload: IterationNodeType) => { // output const { getIterationNodeChildren } = useWorkflow() const iterationChildrenNodes = getIterationNodeChildren(id) - const childrenNodeVars = toNodeOutputVars(iterationChildrenNodes, isChatMode) + const buildInTools = useStore(s => s.buildInTools) + const customTools = useStore(s => s.customTools) + const workflowTools = useStore(s => s.workflowTools) + const mcpTools = useStore(s => s.mcpTools) + const dataSourceList = useStore(s => s.dataSourceList) + const allPluginInfoList = { + buildInTools, + customTools, + workflowTools, + mcpTools, + dataSourceList: dataSourceList ?? [], + } + const childrenNodeVars = toNodeOutputVars(iterationChildrenNodes, isChatMode, undefined, [], [], [], allPluginInfoList) const handleOutputVarChange = useCallback((output: ValueSelector | string, _varKindType: VarKindType, varInfo?: Var) => { if (isEqual(inputs.output_selector, output as ValueSelector)) diff --git a/web/app/components/workflow/nodes/loop/use-config.ts b/web/app/components/workflow/nodes/loop/use-config.ts index 4c6e07c9c0..552c94cc3b 100644 --- a/web/app/components/workflow/nodes/loop/use-config.ts +++ b/web/app/components/workflow/nodes/loop/use-config.ts @@ -38,7 +38,19 @@ const useConfig = (id: string, payload: LoopNodeType) => { // output const { getLoopNodeChildren } = useWorkflow() const loopChildrenNodes = [{ id, data: payload } as any, ...getLoopNodeChildren(id)] - const childrenNodeVars = toNodeOutputVars(loopChildrenNodes, isChatMode, undefined, [], conversationVariables) + const buildInTools = useStore(s => s.buildInTools) + const customTools = useStore(s => s.customTools) + const workflowTools = useStore(s => s.workflowTools) + const mcpTools = useStore(s => s.mcpTools) + const dataSourceList = useStore(s => s.dataSourceList) + const allPluginInfoList = { + buildInTools, + customTools, + workflowTools, + mcpTools, + dataSourceList: dataSourceList ?? [], + } + const childrenNodeVars = toNodeOutputVars(loopChildrenNodes, isChatMode, undefined, [], conversationVariables, [], allPluginInfoList) const { getIsVarFileAttribute, diff --git a/web/app/components/workflow/nodes/tool/default.ts b/web/app/components/workflow/nodes/tool/default.ts index ca015d35d9..bfe7d8fdd8 100644 --- a/web/app/components/workflow/nodes/tool/default.ts +++ b/web/app/components/workflow/nodes/tool/default.ts @@ -1,8 +1,11 @@ -import { genNodeMetaData } from '@/app/components/workflow/utils' -import { BlockEnum } from '@/app/components/workflow/types' -import type { NodeDefault } from '../../types' +import { genNodeMetaData, getOutputVariableAlias } from '@/app/components/workflow/utils' +import { BlockEnum, VarType } from '@/app/components/workflow/types' +import type { NodeDefault, ToolWithProvider } from '../../types' import type { ToolNodeType } from './types' import { VarType as VarKindType } from '@/app/components/workflow/nodes/tool/types' +import { TOOL_OUTPUT_STRUCT } from '../../constants' +import { CollectionType } from '@/app/components/tools/types' +import { canFindTool } from '@/utils' const i18nPrefix = 'workflow.errorMsg' @@ -62,6 +65,65 @@ const nodeDefault: NodeDefault = { errorMessage: errorMessages, } }, + getOutputVars(payload: ToolNodeType, allPluginInfoList: Record) { + const { provider_id, provider_type } = payload + let currentTools: ToolWithProvider[] = [] + switch (provider_type) { + case CollectionType.builtIn: + currentTools = allPluginInfoList.buildInTools ?? [] + break + case CollectionType.custom: + currentTools = allPluginInfoList.customTools ?? [] + break + case CollectionType.workflow: + currentTools = allPluginInfoList.workflowTools ?? [] + break + case CollectionType.mcp: + currentTools = allPluginInfoList.mcpTools ?? [] + break + default: + currentTools = [] + } + const currCollection = currentTools.find(item => canFindTool(item.id, provider_id)) + const currTool = currCollection?.tools.find(tool => tool.name === payload.tool_name) + const output_schema = currTool?.output_schema + let res: any[] = [] + if (!output_schema) { + res = TOOL_OUTPUT_STRUCT + } + else { + const outputSchema: any[] = [] + Object.keys(output_schema.properties).forEach((outputKey) => { + const output = output_schema.properties[outputKey] + const dataType = output.type + const alias = getOutputVariableAlias(output.properties) + let type = dataType === 'array' + ? `array[${output.items?.type.slice(0, 1).toLocaleLowerCase()}${output.items?.type.slice(1)}]` + : `${output.type.slice(0, 1).toLocaleLowerCase()}${output.type.slice(1)}` + + if (type === VarType.object && alias === 'file') + type = VarType.file + + outputSchema.push({ + variable: outputKey, + type, + description: output.description, + alias, + children: output.type === 'object' ? { + schema: { + type: 'object', + properties: output.properties, + }, + } : undefined, + }) + }) + res = [ + ...TOOL_OUTPUT_STRUCT, + ...outputSchema, + ] + } + return res + }, } export default nodeDefault diff --git a/web/app/components/workflow/nodes/tool/types.ts b/web/app/components/workflow/nodes/tool/types.ts index c7a900a4f0..4b60d562cc 100644 --- a/web/app/components/workflow/nodes/tool/types.ts +++ b/web/app/components/workflow/nodes/tool/types.ts @@ -20,7 +20,6 @@ export type ToolNodeType = CommonNodeType & { tool_label: string tool_parameters: ToolVarInputs tool_configurations: Record - output_schema: Record paramSchemas?: Record[] version?: string tool_node_version?: string diff --git a/web/app/components/workflow/nodes/tool/use-config.ts b/web/app/components/workflow/nodes/tool/use-config.ts index d6affe5e3b..6dad1d6363 100644 --- a/web/app/components/workflow/nodes/tool/use-config.ts +++ b/web/app/components/workflow/nodes/tool/use-config.ts @@ -30,9 +30,8 @@ const useConfig = (id: string, payload: ToolNodeType) => { /* * tool_configurations: tool setting, not dynamic setting (form type = form) * tool_parameters: tool dynamic setting(form type = llm) - * output_schema: tool dynamic output */ - const { provider_id, provider_type, tool_name, tool_configurations, output_schema, tool_parameters } = inputs + const { provider_id, provider_type, tool_name, tool_configurations, tool_parameters } = inputs const isBuiltIn = provider_type === CollectionType.builtIn const buildInTools = useStore(s => s.buildInTools) const customTools = useStore(s => s.customTools) @@ -53,7 +52,9 @@ const useConfig = (id: string, payload: ToolNodeType) => { return [] } }, [buildInTools, customTools, mcpTools, provider_type, workflowTools]) - const currCollection = currentTools.find(item => canFindTool(item.id, provider_id)) + const currCollection = useMemo(() => { + return currentTools.find(item => canFindTool(item.id, provider_id)) + }, [currentTools, provider_id]) // Auth const needAuth = !!currCollection?.allow_delete @@ -75,13 +76,19 @@ const useConfig = (id: string, payload: ToolNodeType) => { hideSetAuthModal() }, [currCollection?.name, hideSetAuthModal, t, handleFetchAllTools, provider_type]) - const currTool = currCollection?.tools.find(tool => tool.name === tool_name) + const currTool = useMemo(() => { + return currCollection?.tools.find(tool => tool.name === tool_name) + }, [currCollection, tool_name]) const formSchemas = useMemo(() => { return currTool ? toolParametersToFormSchemas(currTool.parameters) : [] }, [currTool]) - const toolInputVarSchema = formSchemas.filter((item: any) => item.form === 'llm') + const toolInputVarSchema = useMemo(() => { + return formSchemas.filter((item: any) => item.form === 'llm') + }, [formSchemas]) // use setting - const toolSettingSchema = formSchemas.filter((item: any) => item.form !== 'llm') + const toolSettingSchema = useMemo(() => { + return formSchemas.filter((item: any) => item.form !== 'llm') + }, [formSchemas]) const hasShouldTransferTypeSettingInput = toolSettingSchema.some(item => item.type === 'boolean' || item.type === 'number-input') const setInputs = useCallback((value: ToolNodeType) => { @@ -174,8 +181,10 @@ const useConfig = (id: string, payload: ToolNodeType) => { const outputSchema = useMemo(() => { const res: any[] = [] - if (!output_schema) - return [] + const output_schema = currTool?.output_schema + if (!output_schema || !output_schema.properties) + return res + Object.keys(output_schema.properties).forEach((outputKey) => { const output = output_schema.properties[outputKey] const type = output.type @@ -196,14 +205,15 @@ const useConfig = (id: string, payload: ToolNodeType) => { } }) return res - }, [output_schema]) + }, [currTool]) const hasObjectOutput = useMemo(() => { - if (!output_schema) + const output_schema = currTool?.output_schema + if (!output_schema || !output_schema.properties) return false const properties = output_schema.properties return Object.keys(properties).some(key => properties[key].type === 'object') - }, [output_schema]) + }, [currTool]) return { readOnly, diff --git a/web/app/components/workflow/nodes/tool/use-initial.ts b/web/app/components/workflow/nodes/tool/use-initial.ts index 3006f56eb7..6f0f03e7ea 100644 --- a/web/app/components/workflow/nodes/tool/use-initial.ts +++ b/web/app/components/workflow/nodes/tool/use-initial.ts @@ -54,7 +54,6 @@ export const useInitial = (id: string) => { tool_description: currTool.description[language], title: currTool.label[language], is_team_authorization: currCollection.is_team_authorization, - output_schema: currTool.output_schema, paramSchemas: currTool.parameters, params, _notInitialized: false, diff --git a/web/app/components/workflow/types.ts b/web/app/components/workflow/types.ts index de2eb8bfe8..2c8b859392 100644 --- a/web/app/components/workflow/types.ts +++ b/web/app/components/workflow/types.ts @@ -330,7 +330,7 @@ export type NodeDefault = { defaultValue: Partial defaultRunInputData?: Record checkValid: (payload: T, t: any, moreDataForCheckValid?: any) => { isValid: boolean; errorMessage?: string } - getOutputVars?: (payload: T, ragVariables?: Var[]) => Var[] + getOutputVars?: (payload: T, allPluginInfoList: Record, ragVariables?: Var[]) => Var[] } export type OnSelectBlock = (type: BlockEnum, toolDefaultValue?: ToolDefaultValue | DataSourceDefaultValue) => void