From e1cb85cee171eeeffc9ab5158368317e39634043 Mon Sep 17 00:00:00 2001 From: JzoNg Date: Mon, 30 Dec 2024 15:43:24 +0800 Subject: [PATCH 01/19] fix label of tool picker --- web/app/components/workflow/block-selector/tool/action-item.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 46c1917e39..f05d7a1c81 100644 --- a/web/app/components/workflow/block-selector/tool/action-item.tsx +++ b/web/app/components/workflow/block-selector/tool/action-item.tsx @@ -64,7 +64,7 @@ const ToolItem: FC = ({ }) }} > -
{payload.name}
+
{payload.label[language]}
) From afb3548e456b4a37962772de6551b1295e87013c Mon Sep 17 00:00:00 2001 From: AkaraChen Date: Mon, 30 Dec 2024 17:40:56 +0800 Subject: [PATCH 02/19] refactor: agent parameters --- web/app/components/plugins/types.ts | 6 ++ .../components/agent-strategy-selector.tsx | 2 - .../nodes/_base/components/agent-strategy.tsx | 98 ++++--------------- .../components/workflow/nodes/agent/node.tsx | 8 +- .../components/workflow/nodes/agent/panel.tsx | 13 +-- .../components/workflow/nodes/agent/types.ts | 3 +- .../workflow/nodes/agent/use-config.ts | 28 ++++++ 7 files changed, 63 insertions(+), 95 deletions(-) diff --git a/web/app/components/plugins/types.ts b/web/app/components/plugins/types.ts index 18282b730d..92ac9dad28 100644 --- a/web/app/components/plugins/types.ts +++ b/web/app/components/plugins/types.ts @@ -392,6 +392,12 @@ export type StrategyParamItem = { required: boolean default: any options: any[] + template: { + enabled: boolean + }, + auto_generate: { + type: string + } } export type StrategyDetail = { diff --git a/web/app/components/workflow/nodes/_base/components/agent-strategy-selector.tsx b/web/app/components/workflow/nodes/_base/components/agent-strategy-selector.tsx index 638f5b7259..c4250c0307 100644 --- a/web/app/components/workflow/nodes/_base/components/agent-strategy-selector.tsx +++ b/web/app/components/workflow/nodes/_base/components/agent-strategy-selector.tsx @@ -126,8 +126,6 @@ export const AgentStrategySelector = (props: AgentStrategySelectorProps) => { agent_strategy_provider_name: tool!.provider_name, agent_strategy_label: tool!.tool_label, agent_output_schema: tool!.output_schema, - agent_configurations: {}, - agent_parameters: {}, }) setOpen(false) }} diff --git a/web/app/components/workflow/nodes/_base/components/agent-strategy.tsx b/web/app/components/workflow/nodes/_base/components/agent-strategy.tsx index 8f10d91f9e..e964e2d98b 100644 --- a/web/app/components/workflow/nodes/_base/components/agent-strategy.tsx +++ b/web/app/components/workflow/nodes/_base/components/agent-strategy.tsx @@ -14,13 +14,12 @@ import MultipleToolSelector from '@/app/components/plugins/plugin-detail-panel/m import Field from './field' import type { ComponentProps } from 'react' import { useLanguage } from '@/app/components/header/account-setting/model-provider-page/hooks' +import Editor from './prompt/editor' export type Strategy = { agent_strategy_provider_name: string agent_strategy_name: string agent_strategy_label: string - agent_configurations?: Record - agent_parameters?: Record agent_output_schema: Record } @@ -36,80 +35,16 @@ type CustomSchema = Omit & { typ type ToolSelectorSchema = CustomSchema<'tool-selector'> type MultipleToolSelectorSchema = CustomSchema<'array[tools]'> - -type CustomField = ToolSelectorSchema | MultipleToolSelectorSchema - -const devMockForm = [{ - name: 'instruction', - label: { - en_US: 'Instruction', - zh_Hans: '指令', - pt_BR: 'Instruction', - ja_JP: 'Instruction', - }, - placeholder: null, - scope: null, - auto_generate: { - type: 'prompt_instruction', - }, +type StringSchema = CustomSchema<'string', { template: { - enabled: true, + enabled: boolean }, - required: true, - default: null, - min: null, - max: null, - options: [], - type: 'string', -}, -{ - name: 'query', - label: { - en_US: 'Query', - zh_Hans: '查询', - pt_BR: 'Query', - ja_JP: 'Query', - }, - placeholder: null, - scope: null, - auto_generate: null, - template: null, - required: true, - default: null, - min: null, - max: null, - options: [], - type: 'string', -}, -{ - name: 'max iterations', - label: { - en_US: 'Max Iterations', - zh_Hans: '最大迭代次数', - pt_BR: 'Max Iterations', - ja_JP: 'Max Iterations', - }, - placeholder: null, - scope: null, - auto_generate: null, - template: null, - required: true, - default: '1', - min: 1, - max: 10, - type: FormTypeEnum.textNumber, - tooltip: { - en_US: 'The maximum number of iterations to run', - zh_Hans: '运行的最大迭代次数', - pt_BR: 'The maximum number of iterations to run', - ja_JP: 'The maximum number of iterations to run', - }, -}].map((item) => { - return { - ...item, - variable: item.name, + auto_generate: { + type: string } -}) +}> + +type CustomField = ToolSelectorSchema | MultipleToolSelectorSchema | StringSchema export const AgentStrategy = (props: AgentStrategyProps) => { const { strategy, onStrategyChange, formSchema, formValue, onFormValueChange } = props @@ -188,6 +123,18 @@ export const AgentStrategy = (props: AgentStrategyProps) => { /> ) } + case 'string': { + const value = props.value[schema.variable] + const onChange = (value: any) => { + props.onChange({ ...props.value, [schema.variable]: value }) + } + return + } } } return
@@ -196,10 +143,7 @@ export const AgentStrategy = (props: AgentStrategyProps) => { strategy ?
- formSchemas={[ - ...formSchema, - ...devMockForm as any, - ]} + formSchemas={formSchema} value={formValue} onChange={onFormValueChange} validating={false} diff --git a/web/app/components/workflow/nodes/agent/node.tsx b/web/app/components/workflow/nodes/agent/node.tsx index fa05132295..948a9ef0d2 100644 --- a/web/app/components/workflow/nodes/agent/node.tsx +++ b/web/app/components/workflow/nodes/agent/node.tsx @@ -21,7 +21,7 @@ const AgentNode: FC> = (props) => { const models = currentStrategy?.parameters .filter(param => param.type === FormTypeEnum.modelSelector) .reduce((acc, param) => { - const item = inputs.agent_configurations?.[param.name] + const item = inputs.agent_parameters?.[param.name]?.value if (!item) { if (param.required) { acc.push({ param: param.name }) @@ -40,7 +40,7 @@ const AgentNode: FC> = (props) => { currentStrategy?.parameters.forEach((param) => { if (param.type === FormTypeEnum.toolSelector) { const field = param.name - const value = inputs.agent_configurations?.[field] + const value = inputs.agent_parameters?.[field]?.value if (value) { tools.push({ providerName: value.provider_name as any, @@ -49,7 +49,7 @@ const AgentNode: FC> = (props) => { } if (param.type === FormTypeEnum.multiToolSelector) { const field = param.name - const value = inputs.agent_configurations?.[field] + const value = inputs.agent_parameters?.[field]?.value if (value) { (value as unknown as any[]).forEach((item) => { tools.push({ @@ -60,7 +60,7 @@ const AgentNode: FC> = (props) => { } }) return tools - }, [currentStrategy?.parameters, inputs.agent_configurations]) + }, [currentStrategy?.parameters, inputs.agent_parameters]) return
{inputs.agent_strategy_name ? > = (props) => { - const { inputs, setInputs, currentStrategy } = useConfig(props.id, props.data) + const { inputs, setInputs, currentStrategy, formData, onFormChange } = useConfig(props.id, props.data) const { t } = useTranslation() return
@@ -28,28 +28,21 @@ const AgentPanel: FC> = (props) => { strategy={inputs.agent_strategy_name ? { agent_strategy_provider_name: inputs.agent_strategy_provider_name!, agent_strategy_name: inputs.agent_strategy_name!, - agent_configurations: inputs.agent_configurations, agent_strategy_label: inputs.agent_strategy_label!, agent_output_schema: inputs.output_schema, - agent_parameters: inputs.agent_parameters, } : undefined} onStrategyChange={(strategy) => { setInputs({ ...inputs, agent_strategy_provider_name: strategy?.agent_strategy_provider_name, agent_strategy_name: strategy?.agent_strategy_name, - agent_configurations: strategy?.agent_configurations, - agent_parameters: strategy?.agent_parameters, agent_strategy_label: strategy?.agent_strategy_label, output_schema: strategy!.agent_output_schema, }) }} formSchema={currentStrategy?.parameters?.map(strategyParamToCredientialForm) || []} - formValue={inputs.agent_configurations || {}} - onFormValueChange={value => setInputs({ - ...inputs, - agent_configurations: value, - })} + formValue={formData} + onFormValueChange={onFormChange} />
diff --git a/web/app/components/workflow/nodes/agent/types.ts b/web/app/components/workflow/nodes/agent/types.ts index 66e576cb9c..e75079ae8c 100644 --- a/web/app/components/workflow/nodes/agent/types.ts +++ b/web/app/components/workflow/nodes/agent/types.ts @@ -5,7 +5,6 @@ export type AgentNodeType = CommonNodeType & { agent_strategy_provider_name?: string agent_strategy_name?: string agent_strategy_label?: string - agent_parameters?: Record - agent_configurations?: Record + agent_parameters?: ToolVarInputs output_schema: Record } diff --git a/web/app/components/workflow/nodes/agent/use-config.ts b/web/app/components/workflow/nodes/agent/use-config.ts index e3fa786480..04020da497 100644 --- a/web/app/components/workflow/nodes/agent/use-config.ts +++ b/web/app/components/workflow/nodes/agent/use-config.ts @@ -5,6 +5,8 @@ import type { AgentNodeType } from './types' import { useNodesReadOnly, } from '@/app/components/workflow/hooks' +import { useMemo } from 'react' +import { type ToolVarInputs, VarType } from '../tool/types' const useConfig = (id: string, payload: AgentNodeType) => { const { nodesReadOnly: readOnly } = useNodesReadOnly() @@ -20,6 +22,30 @@ const useConfig = (id: string, payload: AgentNodeType) => { const currentStrategy = strategies.data?.declaration.strategies.find( str => str.identity.name === inputs.agent_strategy_name, ) + const formData = useMemo(() => { + return Object.fromEntries( + Object.entries(inputs.agent_parameters || {}).map(([key, value]) => { + return [key, value.value] + }), + ) + }, [inputs.agent_parameters]) + const onFormChange = (value: Record) => { + const res: ToolVarInputs = {} + const params = currentStrategy!.parameters + Object.entries(value).forEach(([key, val]) => { + const param = params.find(p => p.name === key) + const isMixed = param?.type === 'string' + res[key] = { + type: isMixed ? VarType.mixed : VarType.constant, + value: val, + } + }) + setInputs({ + ...inputs, + agent_parameters: res, + }) + console.log(res) + } return { readOnly, inputs, @@ -27,6 +53,8 @@ const useConfig = (id: string, payload: AgentNodeType) => { handleVarListChange, handleAddVariable, currentStrategy, + formData, + onFormChange, } } From 9b6f5803654ea788f860132725ce74ea5fe93b0e Mon Sep 17 00:00:00 2001 From: Yi Date: Mon, 30 Dec 2024 17:55:46 +0800 Subject: [PATCH 03/19] add model install in model configuration for the agent node --- .../components/base/install-button/index.tsx | 2 +- .../agent-model-trigger.tsx | 52 ++++++++++++++----- .../status-indicators.tsx | 12 ++++- .../deprecated-model-trigger.tsx | 4 +- .../model-selector/empty-trigger.tsx | 2 +- .../components/agent-strategy-selector.tsx | 6 +-- web/i18n/en-US/workflow.ts | 2 +- web/i18n/zh-Hans/workflow.ts | 2 +- 8 files changed, 60 insertions(+), 22 deletions(-) diff --git a/web/app/components/base/install-button/index.tsx b/web/app/components/base/install-button/index.tsx index f9ad238fb2..983e9b343e 100644 --- a/web/app/components/base/install-button/index.tsx +++ b/web/app/components/base/install-button/index.tsx @@ -3,7 +3,7 @@ import { RiInstallLine, RiLoader2Line } from '@remixicon/react' type InstallButtonProps = { loading: boolean - onInstall: () => void + onInstall: (e: React.MouseEvent) => void t: any } diff --git a/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/agent-model-trigger.tsx b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/agent-model-trigger.tsx index 68cdf2230e..e1a8873cde 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/agent-model-trigger.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/agent-model-trigger.tsx @@ -1,5 +1,5 @@ import type { FC } from 'react' -import { useEffect, useState } from 'react' +import { useEffect, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import type { CustomConfigurationModelFixedFields, @@ -9,6 +9,7 @@ import type { import { ConfigurationMethodEnum, CustomConfigurationStatusEnum, + ModelTypeEnum, } from '../declarations' import { UPDATE_MODEL_PROVIDER_CUSTOM_MODEL_LIST } from '../provider-added-card' import type { PluginInfoFromMarketPlace } from '@/app/components/plugins/types' @@ -54,13 +55,19 @@ const AgentModelTrigger: FC = ({ const updateModelProviders = useUpdateModelProviders() const updateModelList = useUpdateModelList() const { eventEmitter } = useEventEmitterContextContext() - const modelProvider = modelProviders.find(item => item.provider === providerName) - const needsConfiguration = modelProvider?.custom_configuration.status === CustomConfigurationStatusEnum.noConfigure && !( - modelProvider.system_configuration.enabled === true - && modelProvider.system_configuration.quota_configurations.find( - item => item.quota_type === modelProvider.system_configuration.current_quota_type, + const { modelProvider, needsConfiguration } = useMemo(() => { + const modelProvider = modelProviders.find(item => item.provider === providerName) + const needsConfiguration = modelProvider?.custom_configuration.status === CustomConfigurationStatusEnum.noConfigure && !( + modelProvider.system_configuration.enabled === true + && modelProvider.system_configuration.quota_configurations.find( + item => item.quota_type === modelProvider.system_configuration.current_quota_type, + ) ) - ) + return { + modelProvider, + needsConfiguration, + } + }, [modelProviders, providerName]) const [pluginInfo, setPluginInfo] = useState(null) const [isPluginChecked, setIsPluginChecked] = useState(false) const [loading, setLoading] = useState(false) @@ -124,6 +131,24 @@ const AgentModelTrigger: FC = ({ }) } + const handleInstall = async (pluginInfo: PluginInfoFromMarketPlace) => { + setLoading(true) + try { + const { all_installed } = await installPackageFromMarketPlace(pluginInfo.latest_package_identifier) + if (all_installed) { + setInstalled(true) + updateModelProviders() + updateModelList(ModelTypeEnum.textGeneration) + } + } + catch (error) { + console.error('Installation failed:', error) + } + finally { + setLoading(false) + } + } + return (
= ({ {!installed && !modelProvider && pluginInfo && ( { - setLoading(true) - const { all_installed } = await installPackageFromMarketPlace(pluginInfo.latest_package_identifier) - if (all_installed) - setInstalled(true) + onInstall={(e) => { + e.stopPropagation() + handleInstall(pluginInfo) }} t={t} /> )} + {modelProvider && !disabled && ( +
+ +
+ )} ) : ( <> diff --git a/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/status-indicators.tsx b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/status-indicators.tsx index 2f7ee0e5f7..94724b43eb 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/status-indicators.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/status-indicators.tsx @@ -1,4 +1,5 @@ import Tooltip from '@/app/components/base/tooltip' +import Link from 'next/link' import { RiErrorWarningFill } from '@remixicon/react' type StatusIndicatorsProps = { @@ -28,7 +29,16 @@ const StatusIndicators = ({ needsConfiguration, modelProvider, disabled, pluginI
{t('workflow.nodes.agent.modelNotInMarketplace.desc')}
-
{t('workflow.nodes.agent.modelNotInMarketplace.manageInPlugins')}
+
+ { + e.stopPropagation() + }} + > + {t('workflow.nodes.agent.linkToPlugin')} + +
} asChild={false} diff --git a/web/app/components/header/account-setting/model-provider-page/model-selector/deprecated-model-trigger.tsx b/web/app/components/header/account-setting/model-provider-page/model-selector/deprecated-model-trigger.tsx index e47afa1c4d..6d42561774 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-selector/deprecated-model-trigger.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-selector/deprecated-model-trigger.tsx @@ -22,14 +22,14 @@ const ModelTrigger: FC = ({ return (
-
+
{modelName}
diff --git a/web/app/components/header/account-setting/model-provider-page/model-selector/empty-trigger.tsx b/web/app/components/header/account-setting/model-provider-page/model-selector/empty-trigger.tsx index ccbf2d8c22..980e4e3784 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-selector/empty-trigger.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-selector/empty-trigger.tsx @@ -14,7 +14,7 @@ const ModelTrigger: FC = ({ return (
diff --git a/web/app/components/workflow/nodes/_base/components/agent-strategy-selector.tsx b/web/app/components/workflow/nodes/_base/components/agent-strategy-selector.tsx index c4250c0307..f82beaab93 100644 --- a/web/app/components/workflow/nodes/_base/components/agent-strategy-selector.tsx +++ b/web/app/components/workflow/nodes/_base/components/agent-strategy-selector.tsx @@ -89,15 +89,15 @@ export const AgentStrategySelector = (props: AgentStrategySelectorProps) => { const { t } = useTranslation() return -
setOpen(o => !o)}> +
setOpen(o => !o)}> {/* eslint-disable-next-line @next/next/no-img-element */} - {icon && icon} + />
}

diff --git a/web/i18n/en-US/workflow.ts b/web/i18n/en-US/workflow.ts index 64ea5b9317..200e4d9ca7 100644 --- a/web/i18n/en-US/workflow.ts +++ b/web/i18n/en-US/workflow.ts @@ -716,7 +716,7 @@ const translation = { }, modelNotInMarketplace: { title: 'Model not installed', - desc: 'This model is not installed from the marketplace. Please go to Plugins to reinstall.', + desc: 'This model is installed from Local or GitHub repository. Please use after installation.', manageInPlugins: 'Manage in Plugins', }, configureModel: 'Configure Model', diff --git a/web/i18n/zh-Hans/workflow.ts b/web/i18n/zh-Hans/workflow.ts index fce1fd74b2..3d970d3e9f 100644 --- a/web/i18n/zh-Hans/workflow.ts +++ b/web/i18n/zh-Hans/workflow.ts @@ -716,7 +716,7 @@ const translation = { }, modelNotInMarketplace: { title: '模型未安装', - desc: '此模型未从市场安装。请转到插件重新安装。', + desc: '此模型安装自本地或 GitHub 仓库。请安装后使用。', manageInPlugins: '在插件中管理', }, model: '模型', From 128410902c07d2e4a8198a23871c68f20edcdd08 Mon Sep 17 00:00:00 2001 From: Joel Date: Mon, 30 Dec 2024 15:47:24 +0800 Subject: [PATCH 04/19] temp --- .../run/utils/format-log/agent/data.ts | 36 +++++++++++++++++++ .../run/utils/format-log/agent/index.ts | 5 +++ 2 files changed, 41 insertions(+) diff --git a/web/app/components/workflow/run/utils/format-log/agent/data.ts b/web/app/components/workflow/run/utils/format-log/agent/data.ts index 2e0c23b5fb..828a482e9d 100644 --- a/web/app/components/workflow/run/utils/format-log/agent/data.ts +++ b/web/app/components/workflow/run/utils/format-log/agent/data.ts @@ -89,3 +89,39 @@ export const agentNodeData = (() => { }], } })() + +export const oneStepCircle = (() => { + const node = { + node_type: BlockEnum.Agent, + execution_metadata: { + agent_log: [ + { id: '1', label: 'Node 1' }, + { id: '1', parent_id: '1', label: 'Node 1' }, + { id: '1', parent_id: '1', label: 'Node 1' }, + { id: '1', parent_id: '1', label: 'Node 1' }, + { id: '1', parent_id: '1', label: 'Node 1' }, + { id: '1', parent_id: '1', label: 'Node 1' }, + ], + }, + } + + return { + in: [node], + expect: [{ + ...node, + agentLog: [ + { + id: '1', + label: 'Node 1', + hasCircle: true, + }, + ], + }], + } +})() + +export const multiStepsCircle = () => { +} + +export const CircleNestCircle = (() => { +})() diff --git a/web/app/components/workflow/run/utils/format-log/agent/index.ts b/web/app/components/workflow/run/utils/format-log/agent/index.ts index 255dc16c5c..2efa44114c 100644 --- a/web/app/components/workflow/run/utils/format-log/agent/index.ts +++ b/web/app/components/workflow/run/utils/format-log/agent/index.ts @@ -3,6 +3,10 @@ import type { AgentLogItem, AgentLogItemWithChildren, NodeTracing } from '@/type const supportedAgentLogNodes = [BlockEnum.Agent, BlockEnum.Tool] +const removeCircle = (node: NodeTracing) => { + +} + const listToTree = (logs: AgentLogItem[]) => { if (!logs || logs.length === 0) return [] @@ -24,6 +28,7 @@ const listToTree = (logs: AgentLogItem[]) => { }) return tree } + const format = (list: NodeTracing[]): NodeTracing[] => { const result: NodeTracing[] = list.map((item) => { if (supportedAgentLogNodes.includes(item.node_type) && item.execution_metadata?.agent_log && item.execution_metadata?.agent_log.length > 0) From 0a300183306a7479b401406f8a9f021145fcba12 Mon Sep 17 00:00:00 2001 From: Joel Date: Mon, 30 Dec 2024 18:27:49 +0800 Subject: [PATCH 05/19] chore: one step circle check --- .../run/utils/format-log/agent/data.ts | 63 ++++++++++++++++++- .../run/utils/format-log/agent/index.spec.ts | 10 ++- .../run/utils/format-log/agent/index.ts | 41 +++++++++++- web/types/workflow.ts | 1 + 4 files changed, 109 insertions(+), 6 deletions(-) diff --git a/web/app/components/workflow/run/utils/format-log/agent/data.ts b/web/app/components/workflow/run/utils/format-log/agent/data.ts index 828a482e9d..a159fc2832 100644 --- a/web/app/components/workflow/run/utils/format-log/agent/data.ts +++ b/web/app/components/workflow/run/utils/format-log/agent/data.ts @@ -1,4 +1,5 @@ import { BlockEnum } from '@/app/components/workflow/types' +import { has } from 'immer/dist/internal' export const agentNodeData = (() => { const node = { @@ -114,14 +115,70 @@ export const oneStepCircle = (() => { id: '1', label: 'Node 1', hasCircle: true, + children: [], + }, + ], + }], + } + +})() + +export const multiStepsCircle = (() => { + const node = { + node_type: BlockEnum.Agent, + execution_metadata: { + agent_log: [ + // 1 -> [2 -> 4 -> 1, 3] + { id: '1', label: 'Node 1' }, + { id: '2', parent_id: '1', label: 'Node 2' }, + { id: '3', parent_id: '1', label: 'Node 3' }, + { id: '4', parent_id: '2', label: 'Node 4' }, + // Loop + { id: '1', parent_id: '1', label: 'Node 1' }, + { id: '2', parent_id: '1', label: 'Node 2' }, + { id: '4', parent_id: '2', label: 'Node 4' }, + // { id: '1', parent_id: '1', label: 'Node 1' }, + // { id: '2', parent_id: '1', label: 'Node 2' }, + // { id: '4', parent_id: '2', label: 'Node 4' }, + + ], + }, + } + + return { + in: [node], + expect: [{ + ...node, + agentLog: [ + { + id: '1', + label: 'Node 1', + children: [ + { + id: '2', + parent_id: '1', + label: 'Node 2', + children: [ + { + id: '4', + parent_id: '2', + label: 'Node 4', + children: [], + hasCircle: true, + } + ], + }, + { + id: '3', + parent_id: '1', + label: 'Node 3', + }, + ], }, ], }], } })() -export const multiStepsCircle = () => { -} - export const CircleNestCircle = (() => { })() diff --git a/web/app/components/workflow/run/utils/format-log/agent/index.spec.ts b/web/app/components/workflow/run/utils/format-log/agent/index.spec.ts index 0fd871c41d..e211744bbe 100644 --- a/web/app/components/workflow/run/utils/format-log/agent/index.spec.ts +++ b/web/app/components/workflow/run/utils/format-log/agent/index.spec.ts @@ -1,9 +1,17 @@ +import exp from 'constants' import format from '.' -import { agentNodeData } from './data' +import { agentNodeData, oneStepCircle, multiStepsCircle } from './data' describe('agent', () => { test('list should transform to tree', () => { // console.log(format(agentNodeData.in as any)) expect(format(agentNodeData.in as any)).toEqual(agentNodeData.expect) }) + + test('list should remove circle log item', () => { + // format(oneStepCircle.in as any) + console.log(JSON.stringify(format(multiStepsCircle.in as any)[0].agentLog)) + // expect(format(oneStepCircle.in as any)).toEqual(oneStepCircle.expect) + // expect(format(multiStepsCircle.in as any)).toEqual(multiStepsCircle.expect) + }) }) diff --git a/web/app/components/workflow/run/utils/format-log/agent/index.ts b/web/app/components/workflow/run/utils/format-log/agent/index.ts index 2efa44114c..8d95c76f24 100644 --- a/web/app/components/workflow/run/utils/format-log/agent/index.ts +++ b/web/app/components/workflow/run/utils/format-log/agent/index.ts @@ -1,10 +1,43 @@ import { BlockEnum } from '@/app/components/workflow/types' import type { AgentLogItem, AgentLogItemWithChildren, NodeTracing } from '@/types/workflow' +import { cloneDeep } from 'lodash-es' const supportedAgentLogNodes = [BlockEnum.Agent, BlockEnum.Tool] -const removeCircle = (node: NodeTracing) => { +const remove = (node: AgentLogItemWithChildren, removeId: string) => { + const { children } = node + if (!children || children.length === 0) { + return + } + children.forEach((child, index) => { + if (child.id === removeId) { + node.hasCircle = true + children.splice(index, 1) + return + } + remove(child, removeId) + }) +} +const removeCircleLogItem = (log: AgentLogItemWithChildren) => { + let newLog = cloneDeep(log) + let { id, children } = newLog + if (!children || children.length === 0) { + return log + } + // check one step circle + const hasOneStepCircle = !!children.find(c => c.id === id) + if (hasOneStepCircle) { + newLog.hasCircle = true + newLog.children = newLog.children.filter(c => c.id !== id) + children = newLog.children + } + + children.forEach((child, index) => { + remove(child, id) // check multi steps circle + children[index] = removeCircleLogItem(child) + }) + return newLog } const listToTree = (logs: AgentLogItem[]) => { @@ -31,8 +64,12 @@ const listToTree = (logs: AgentLogItem[]) => { const format = (list: NodeTracing[]): NodeTracing[] => { const result: NodeTracing[] = list.map((item) => { + let treeList: AgentLogItemWithChildren[] = [] + let removedCircleTree: AgentLogItemWithChildren[] = [] if (supportedAgentLogNodes.includes(item.node_type) && item.execution_metadata?.agent_log && item.execution_metadata?.agent_log.length > 0) - item.agentLog = listToTree(item.execution_metadata.agent_log) + treeList = listToTree(item.execution_metadata.agent_log) + removedCircleTree = treeList.length > 0 ? treeList.map(t => removeCircleLogItem(t)) : [] + item.agentLog = removedCircleTree return item }) diff --git a/web/types/workflow.ts b/web/types/workflow.ts index 1efde98b3d..3ac1db9715 100644 --- a/web/types/workflow.ts +++ b/web/types/workflow.ts @@ -20,6 +20,7 @@ export type AgentLogItem = { } export type AgentLogItemWithChildren = AgentLogItem & { + hasCircle?: boolean children: AgentLogItemWithChildren[] } From d9d42b2d8c46209cbd14459c69449c0d27ac1142 Mon Sep 17 00:00:00 2001 From: Joel Date: Mon, 30 Dec 2024 18:38:07 +0800 Subject: [PATCH 06/19] feat: multi step circle --- .../workflow/run/utils/format-log/agent/data.ts | 10 +++++----- .../run/utils/format-log/agent/index.spec.ts | 5 ++--- .../run/utils/format-log/agent/index.ts | 17 +++++++++++++++++ 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/web/app/components/workflow/run/utils/format-log/agent/data.ts b/web/app/components/workflow/run/utils/format-log/agent/data.ts index a159fc2832..9ffac8ec45 100644 --- a/web/app/components/workflow/run/utils/format-log/agent/data.ts +++ b/web/app/components/workflow/run/utils/format-log/agent/data.ts @@ -133,14 +133,14 @@ export const multiStepsCircle = (() => { { id: '2', parent_id: '1', label: 'Node 2' }, { id: '3', parent_id: '1', label: 'Node 3' }, { id: '4', parent_id: '2', label: 'Node 4' }, + // Loop - { id: '1', parent_id: '1', label: 'Node 1' }, + { id: '1', parent_id: '4', label: 'Node 1' }, + { id: '2', parent_id: '1', label: 'Node 2' }, + { id: '4', parent_id: '2', label: 'Node 4' }, + { id: '1', parent_id: '4', label: 'Node 1' }, { id: '2', parent_id: '1', label: 'Node 2' }, { id: '4', parent_id: '2', label: 'Node 4' }, - // { id: '1', parent_id: '1', label: 'Node 1' }, - // { id: '2', parent_id: '1', label: 'Node 2' }, - // { id: '4', parent_id: '2', label: 'Node 4' }, - ], }, } diff --git a/web/app/components/workflow/run/utils/format-log/agent/index.spec.ts b/web/app/components/workflow/run/utils/format-log/agent/index.spec.ts index e211744bbe..2cd61e99e4 100644 --- a/web/app/components/workflow/run/utils/format-log/agent/index.spec.ts +++ b/web/app/components/workflow/run/utils/format-log/agent/index.spec.ts @@ -10,8 +10,7 @@ describe('agent', () => { test('list should remove circle log item', () => { // format(oneStepCircle.in as any) - console.log(JSON.stringify(format(multiStepsCircle.in as any)[0].agentLog)) - // expect(format(oneStepCircle.in as any)).toEqual(oneStepCircle.expect) - // expect(format(multiStepsCircle.in as any)).toEqual(multiStepsCircle.expect) + expect(format(oneStepCircle.in as any)).toEqual(oneStepCircle.expect) + expect(format(multiStepsCircle.in as any)).toEqual(multiStepsCircle.expect) }) }) diff --git a/web/app/components/workflow/run/utils/format-log/agent/index.ts b/web/app/components/workflow/run/utils/format-log/agent/index.ts index 8d95c76f24..65c7f6d36e 100644 --- a/web/app/components/workflow/run/utils/format-log/agent/index.ts +++ b/web/app/components/workflow/run/utils/format-log/agent/index.ts @@ -19,8 +19,24 @@ const remove = (node: AgentLogItemWithChildren, removeId: string) => { }) } +const removeRepeatedSiblings = (list: AgentLogItemWithChildren[]) => { + if (!list || list.length === 0) { + return [] + } + const result: AgentLogItemWithChildren[] = [] + const addedItemIds: string[] = [] + list.forEach((item) => { + if (!addedItemIds.includes(item.id)) { + result.push(item) + addedItemIds.push(item.id) + } + }) + return result +} + const removeCircleLogItem = (log: AgentLogItemWithChildren) => { let newLog = cloneDeep(log) + newLog.children = removeRepeatedSiblings(newLog.children) let { id, children } = newLog if (!children || children.length === 0) { return log @@ -31,6 +47,7 @@ const removeCircleLogItem = (log: AgentLogItemWithChildren) => { newLog.hasCircle = true newLog.children = newLog.children.filter(c => c.id !== id) children = newLog.children + } children.forEach((child, index) => { From a3f736f6e54beadda443dc2072a8e729fbefeb87 Mon Sep 17 00:00:00 2001 From: Joel Date: Mon, 30 Dec 2024 18:39:33 +0800 Subject: [PATCH 07/19] feat: multi steps circle --- .../components/workflow/run/utils/format-log/agent/data.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/web/app/components/workflow/run/utils/format-log/agent/data.ts b/web/app/components/workflow/run/utils/format-log/agent/data.ts index 9ffac8ec45..d64670a015 100644 --- a/web/app/components/workflow/run/utils/format-log/agent/data.ts +++ b/web/app/components/workflow/run/utils/format-log/agent/data.ts @@ -138,9 +138,9 @@ export const multiStepsCircle = (() => { { id: '1', parent_id: '4', label: 'Node 1' }, { id: '2', parent_id: '1', label: 'Node 2' }, { id: '4', parent_id: '2', label: 'Node 4' }, - { id: '1', parent_id: '4', label: 'Node 1' }, - { id: '2', parent_id: '1', label: 'Node 2' }, - { id: '4', parent_id: '2', label: 'Node 4' }, + // { id: '1', parent_id: '4', label: 'Node 1' }, + // { id: '2', parent_id: '1', label: 'Node 2' }, + // { id: '4', parent_id: '2', label: 'Node 4' }, ], }, } From 232fb66edd2f13abe31d0e454353725e120e49c0 Mon Sep 17 00:00:00 2001 From: Yi Date: Tue, 31 Dec 2024 11:08:53 +0800 Subject: [PATCH 08/19] ui fixes in model selector --- .../model-provider-page/model-icon/index.tsx | 12 +++++------ .../deprecated-model-trigger.tsx | 20 ++++++++++--------- .../model-selector/model-trigger.tsx | 2 +- .../nodes/_base/components/setting-item.tsx | 6 +++--- 4 files changed, 21 insertions(+), 19 deletions(-) diff --git a/web/app/components/header/account-setting/model-provider-page/model-icon/index.tsx b/web/app/components/header/account-setting/model-provider-page/model-icon/index.tsx index 9c81d99675..47eb0ef0ba 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-icon/index.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-icon/index.tsx @@ -22,18 +22,18 @@ const ModelIcon: FC = ({ }) => { const language = useLanguage() if (provider?.provider.includes('openai') && modelName?.includes('gpt-4o')) - return + return

if (provider?.provider.includes('openai') && modelName?.startsWith('gpt-4')) - return + return
if (provider?.icon_small) { return ( -
+
model-icon
) @@ -41,10 +41,10 @@ const ModelIcon: FC = ({ return (
-
+
diff --git a/web/app/components/header/account-setting/model-provider-page/model-selector/deprecated-model-trigger.tsx b/web/app/components/header/account-setting/model-provider-page/model-selector/deprecated-model-trigger.tsx index 6d42561774..8a6b283bdd 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-selector/deprecated-model-trigger.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-selector/deprecated-model-trigger.tsx @@ -22,19 +22,21 @@ const ModelTrigger: FC = ({ return (
- -
- {modelName} +
+ +
+ {modelName} +
- +
diff --git a/web/app/components/header/account-setting/model-provider-page/model-selector/model-trigger.tsx b/web/app/components/header/account-setting/model-provider-page/model-selector/model-trigger.tsx index 0a6b129fe0..fa8abee9b0 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-selector/model-trigger.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-selector/model-trigger.tsx @@ -34,7 +34,7 @@ const ModelTrigger: FC = ({ return (
{ const indicator: ComponentProps['color'] = status === 'error' ? 'red' : status === 'warning' ? 'yellow' : undefined const needTooltip = ['error', 'warning'].includes(status as any) - return
-
+ return
+
{label}
-
+
{children}
From 3a09f43f707e6f40446fac75367e91c4c77885ae Mon Sep 17 00:00:00 2001 From: Yi Date: Tue, 31 Dec 2024 11:46:36 +0800 Subject: [PATCH 09/19] feat: update the install logic --- .../agent-model-trigger.tsx | 18 +++++++++++++++--- .../model-selector/index.tsx | 1 + 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/agent-model-trigger.tsx b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/agent-model-trigger.tsx index e1a8873cde..adffecb82a 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/agent-model-trigger.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/agent-model-trigger.tsx @@ -39,6 +39,7 @@ export type AgentModelTriggerProps = { providerName?: string modelId?: string hasDeprecated?: boolean + scope?: string } const AgentModelTrigger: FC = ({ @@ -48,6 +49,7 @@ const AgentModelTrigger: FC = ({ providerName, modelId, hasDeprecated, + scope, }) => { const { t } = useTranslation() const { modelProviders } = useProviderContext() @@ -136,9 +138,19 @@ const AgentModelTrigger: FC = ({ try { const { all_installed } = await installPackageFromMarketPlace(pluginInfo.latest_package_identifier) if (all_installed) { - setInstalled(true) + [ + ModelTypeEnum.textGeneration, + ModelTypeEnum.textEmbedding, + ModelTypeEnum.rerank, + ModelTypeEnum.moderation, + ModelTypeEnum.speech2text, + ModelTypeEnum.tts, + ].forEach((type: ModelTypeEnum) => { + if (scope?.includes(type)) + updateModelList(type) + }) updateModelProviders() - updateModelList(ModelTypeEnum.textGeneration) + setInstalled(true) } } catch (error) { @@ -190,7 +202,7 @@ const AgentModelTrigger: FC = ({ t={t} /> )} - {modelProvider && !disabled && ( + {modelProvider && !disabled && !needsConfiguration && (
diff --git a/web/app/components/plugins/plugin-detail-panel/model-selector/index.tsx b/web/app/components/plugins/plugin-detail-panel/model-selector/index.tsx index ce8b70776a..00b0cd74eb 100644 --- a/web/app/components/plugins/plugin-detail-panel/model-selector/index.tsx +++ b/web/app/components/plugins/plugin-detail-panel/model-selector/index.tsx @@ -190,6 +190,7 @@ const ModelParameterModal: FC = ({ currentModel={currentModel} providerName={value?.provider} modelId={value?.model} + scope={scope} /> : Date: Tue, 31 Dec 2024 11:51:04 +0800 Subject: [PATCH 10/19] fix: choose tools --- .../plugins/marketplace/search-box/index.tsx | 10 ++++++---- .../block-selector/market-place-plugin/list.tsx | 6 ++++-- web/app/components/workflow/block-selector/tabs.tsx | 1 + 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/web/app/components/plugins/marketplace/search-box/index.tsx b/web/app/components/plugins/marketplace/search-box/index.tsx index 513f8b98ad..61fff2c7a9 100644 --- a/web/app/components/plugins/marketplace/search-box/index.tsx +++ b/web/app/components/plugins/marketplace/search-box/index.tsx @@ -40,7 +40,7 @@ const SearchBox = ({ locale={locale} />
-
+
{ search && ( - onSearchChange('')}> - - +
+ onSearchChange('')}> + + +
) }
diff --git a/web/app/components/workflow/block-selector/market-place-plugin/list.tsx b/web/app/components/workflow/block-selector/market-place-plugin/list.tsx index 540b7d924f..596b6f21c5 100644 --- a/web/app/components/workflow/block-selector/market-place-plugin/list.tsx +++ b/web/app/components/workflow/block-selector/market-place-plugin/list.tsx @@ -74,11 +74,13 @@ const List = ({ ) } + const maxWidthClassName = 'max-w-[300px]' + return ( <> {hasRes && (
{t('plugin.fromMarketplace')} @@ -93,7 +95,7 @@ const List = ({
)} -
+
{list.map((item, index) => ( = ({ { activeTab === TabsEnum.Tools && ( Date: Tue, 31 Dec 2024 11:52:15 +0800 Subject: [PATCH 11/19] wip: instruction field --- .../components/agent-strategy-selector.tsx | 2 +- .../nodes/_base/components/agent-strategy.tsx | 54 ++++++++++++++++--- .../nodes/_base/components/prompt/editor.tsx | 35 ++++++++---- .../components/workflow/nodes/agent/panel.tsx | 2 +- .../workflow/nodes/agent/use-config.ts | 18 ++++--- 5 files changed, 86 insertions(+), 25 deletions(-) diff --git a/web/app/components/workflow/nodes/_base/components/agent-strategy-selector.tsx b/web/app/components/workflow/nodes/_base/components/agent-strategy-selector.tsx index f82beaab93..9a4944080b 100644 --- a/web/app/components/workflow/nodes/_base/components/agent-strategy-selector.tsx +++ b/web/app/components/workflow/nodes/_base/components/agent-strategy-selector.tsx @@ -89,7 +89,7 @@ export const AgentStrategySelector = (props: AgentStrategySelectorProps) => { const { t } = useTranslation() return -
setOpen(o => !o)}> +
setOpen(o => !o)}> {/* eslint-disable-next-line @next/next/no-img-element */} {icon &&
= Omit & { typ type ToolSelectorSchema = CustomSchema<'tool-selector'> type MultipleToolSelectorSchema = CustomSchema<'array[tools]'> type StringSchema = CustomSchema<'string', { - template: { + template?: { enabled: boolean }, - auto_generate: { + auto_generate?: { type: string } }> @@ -50,6 +51,7 @@ export const AgentStrategy = (props: AgentStrategyProps) => { const { strategy, onStrategyChange, formSchema, formValue, onFormValueChange } = props const { t } = useTranslation() const language = useLanguage() + const defaultModel = useDefaultModel(ModelTypeEnum.textGeneration) const override: ComponentProps>['override'] = [ [FormTypeEnum.textNumber], (schema, props) => { @@ -125,14 +127,33 @@ export const AgentStrategy = (props: AgentStrategyProps) => { } case 'string': { const value = props.value[schema.variable] - const onChange = (value: any) => { + const onChange = (value: string) => { props.onChange({ ...props.value, [schema.variable]: value }) } return } } @@ -143,7 +164,26 @@ export const AgentStrategy = (props: AgentStrategyProps) => { strategy ?
- formSchemas={formSchema} + formSchemas={[ + ...formSchema, + ...[{ + name: 'instruction2', + type: 'string', + required: true, + label: { + en_US: 'Instruction2', + zh_Hans: '指令2', + pt_BR: 'Instruction2', + }, + auto_generate: { + type: 'prompt_instruction', + }, + template: { + enabled: true, + }, + // @ts-expect-error just for test + }].map(strategyParamToCredientialForm), + ]} value={formValue} onChange={onFormValueChange} validating={false} diff --git a/web/app/components/workflow/nodes/_base/components/prompt/editor.tsx b/web/app/components/workflow/nodes/_base/components/prompt/editor.tsx index 31e4282ff2..7fc0362da1 100644 --- a/web/app/components/workflow/nodes/_base/components/prompt/editor.tsx +++ b/web/app/components/workflow/nodes/_base/components/prompt/editor.tsx @@ -1,5 +1,5 @@ 'use client' -import type { FC } from 'react' +import type { FC, ReactNode } from 'react' import React, { useCallback, useRef } from 'react' import { RiDeleteBinLine, @@ -37,7 +37,7 @@ import Switch from '@/app/components/base/switch' import { Jinja } from '@/app/components/base/icons/src/vender/workflow' import { useStore } from '@/app/components/workflow/store' -interface Props { +type Props = { className?: string headerClassName?: string instanceId?: string @@ -68,6 +68,12 @@ interface Props { onEditionTypeChange?: (editionType: EditionType) => void varList?: Variable[] handleAddVariable?: (payload: any) => void + containerClassName?: string + gradientBorder?: boolean + titleTooltip?: ReactNode + inputClassName?: string + editorContainerClassName?: string + placeholderClassName?: string } const Editor: FC = ({ @@ -96,6 +102,12 @@ const Editor: FC = ({ handleAddVariable, onGenerated, modelConfig, + containerClassName, + gradientBorder = true, + titleTooltip, + inputClassName, + placeholderClassName, + editorContainerClassName, }) => { const { t } = useTranslation() const { eventEmitter } = useEventEmitterContextContext() @@ -129,10 +141,13 @@ const Editor: FC = ({ return ( -
-
-
-
{title}
+
+
+
+
+
{title}
+ {titleTooltip && } +
{value?.length || 0}
{isSupportPromptGenerator && ( @@ -201,12 +216,13 @@ const Editor: FC = ({
{!(isSupportJinja && editionType === EditionType.jinja2) ? ( -
+
= ({
) : ( -
+
= ({ onChange={onChange} noWrapper isExpand={isExpand} + className={inputClassName} />
)} diff --git a/web/app/components/workflow/nodes/agent/panel.tsx b/web/app/components/workflow/nodes/agent/panel.tsx index 31ef8fc6a4..7ea501815b 100644 --- a/web/app/components/workflow/nodes/agent/panel.tsx +++ b/web/app/components/workflow/nodes/agent/panel.tsx @@ -11,7 +11,7 @@ import type { CredentialFormSchema } from '@/app/components/header/account-setti const i18nPrefix = 'workflow.nodes.agent' -function strategyParamToCredientialForm(param: StrategyParamItem): CredentialFormSchema { +export function strategyParamToCredientialForm(param: StrategyParamItem): CredentialFormSchema { return { ...param as any, variable: param.name, diff --git a/web/app/components/workflow/nodes/agent/use-config.ts b/web/app/components/workflow/nodes/agent/use-config.ts index 04020da497..9cd3d12d87 100644 --- a/web/app/components/workflow/nodes/agent/use-config.ts +++ b/web/app/components/workflow/nodes/agent/use-config.ts @@ -16,12 +16,18 @@ const useConfig = (id: string, payload: AgentNodeType) => { inputs, setInputs, }) - const strategies = useStrategyProviderDetail( + const strategyProvider = useStrategyProviderDetail( inputs.agent_strategy_provider_name || '', ) - const currentStrategy = strategies.data?.declaration.strategies.find( + const currentStrategy = strategyProvider.data?.declaration.strategies.find( str => str.identity.name === inputs.agent_strategy_name, ) + const currentStrategyStatus = useMemo(() => { + if (strategyProvider.isLoading) return 'loading' + if (strategyProvider.isError) return 'plugin-not-found' + if (!currentStrategy) return 'strategy-not-found' + return 'success' + }, [currentStrategy, strategyProvider]) const formData = useMemo(() => { return Object.fromEntries( Object.entries(inputs.agent_parameters || {}).map(([key, value]) => { @@ -31,12 +37,9 @@ const useConfig = (id: string, payload: AgentNodeType) => { }, [inputs.agent_parameters]) const onFormChange = (value: Record) => { const res: ToolVarInputs = {} - const params = currentStrategy!.parameters Object.entries(value).forEach(([key, val]) => { - const param = params.find(p => p.name === key) - const isMixed = param?.type === 'string' res[key] = { - type: isMixed ? VarType.mixed : VarType.constant, + type: VarType.constant, value: val, } }) @@ -44,7 +47,6 @@ const useConfig = (id: string, payload: AgentNodeType) => { ...inputs, agent_parameters: res, }) - console.log(res) } return { readOnly, @@ -55,6 +57,8 @@ const useConfig = (id: string, payload: AgentNodeType) => { currentStrategy, formData, onFormChange, + currentStrategyStatus, + strategyProvider: strategyProvider.data, } } From df421796bb493ec9814d64dc0f8fb72115c5f0a1 Mon Sep 17 00:00:00 2001 From: JzoNg Date: Tue, 31 Dec 2024 12:38:07 +0800 Subject: [PATCH 12/19] add model info in model selector --- .../header/account-setting/menu-dialog.tsx | 2 +- .../model-selector/index.tsx | 1 + .../model-selector/popup-item.tsx | 100 +++++++++++++++--- .../model-selector/popup.tsx | 14 +++ .../plugins/plugin-detail-panel/index.tsx | 11 ++ .../model-selector/index.tsx | 2 +- web/i18n/en-US/common.ts | 2 + web/i18n/zh-Hans/common.ts | 2 + 8 files changed, 116 insertions(+), 18 deletions(-) diff --git a/web/app/components/header/account-setting/menu-dialog.tsx b/web/app/components/header/account-setting/menu-dialog.tsx index 4df8ab8ab1..76296b84dd 100644 --- a/web/app/components/header/account-setting/menu-dialog.tsx +++ b/web/app/components/header/account-setting/menu-dialog.tsx @@ -32,7 +32,7 @@ const MenuDialog = ({ return ( - {}}> + {}}>
= ({ modelList={modelList} onSelect={handleSelect} scopeFeatures={scopeFeatures} + onHide={() => setOpen(false)} />
diff --git a/web/app/components/header/account-setting/model-provider-page/model-selector/popup-item.tsx b/web/app/components/header/account-setting/model-provider-page/model-selector/popup-item.tsx index 78e1d68357..df6e69193e 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-selector/popup-item.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-selector/popup-item.tsx @@ -1,10 +1,25 @@ import type { FC } from 'react' import { useTranslation } from 'react-i18next' +import { + RiFileTextLine, + RiFilmAiLine, + RiImageCircleAiLine, + RiVoiceAiFill, +} from '@remixicon/react' import type { DefaultModel, Model, ModelItem, } from '../declarations' +import { + ModelFeatureEnum, + ModelFeatureTextEnum, + ModelTypeEnum, +} from '../declarations' +import { + modelTypeFormat, + sizeFormat, +} from '../utils' import { useLanguage, useUpdateModelList, @@ -12,15 +27,16 @@ import { } from '../hooks' import ModelIcon from '../model-icon' import ModelName from '../model-name' +import ModelBadge from '../model-badge' import { ConfigurationMethodEnum, - MODEL_STATUS_TEXT, ModelStatusEnum, } from '../declarations' import { Check } from '@/app/components/base/icons/src/vender/line/general' import { useModalContext } from '@/context/modal-context' import { useProviderContext } from '@/context/provider-context' import Tooltip from '@/app/components/base/tooltip' +import cn from '@/utils/classnames' type PopupItemProps = { defaultModel?: DefaultModel @@ -71,34 +87,86 @@ const PopupItem: FC = ({ model.models.map(modelItem => ( +
+ +
{modelItem.label[language] || modelItem.label.en_US}
+
+ {/* {currentProvider?.description && ( +
{currentProvider?.description?.[language] || currentProvider?.description?.en_US}
+ )} */} +
+ {modelItem.model_type && ( + + {modelTypeFormat(modelItem.model_type)} + + )} + {modelItem.model_properties.mode && ( + + {(modelItem.model_properties.mode as string).toLocaleUpperCase()} + + )} + {modelItem.model_properties.context_size && ( + + {sizeFormat(modelItem.model_properties.context_size as number)} + + )} +
+ {modelItem.model_type === ModelTypeEnum.textGeneration && modelItem.features?.some(feature => [ModelFeatureEnum.vision, ModelFeatureEnum.audio, ModelFeatureEnum.video, ModelFeatureEnum.document].includes(feature)) && ( +
+
{t('common.model.capabilities')}
+
+ {modelItem.features?.includes(ModelFeatureEnum.vision) && ( + + + {ModelFeatureTextEnum.vision} + + )} + {modelItem.features?.includes(ModelFeatureEnum.audio) && ( + + + {ModelFeatureTextEnum.audio} + + )} + {modelItem.features?.includes(ModelFeatureEnum.video) && ( + + + {ModelFeatureTextEnum.video} + + )} + {modelItem.features?.includes(ModelFeatureEnum.document) && ( + + + {ModelFeatureTextEnum.document} + + )} +
+
+ )} +
+ } >
handleSelect(model.provider, modelItem)} >
{ diff --git a/web/app/components/header/account-setting/model-provider-page/model-selector/popup.tsx b/web/app/components/header/account-setting/model-provider-page/model-selector/popup.tsx index 1089697c98..ad06c3238b 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-selector/popup.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-selector/popup.tsx @@ -1,6 +1,8 @@ import type { FC } from 'react' import { useMemo, useState } from 'react' +import { useTranslation } from 'react-i18next' import { + RiArrowRightUpLine, RiSearchLine, } from '@remixicon/react' import type { @@ -12,21 +14,26 @@ import { ModelFeatureEnum } from '../declarations' import { useLanguage } from '../hooks' import PopupItem from './popup-item' import { XCircle } from '@/app/components/base/icons/src/vender/solid/general' +import { useModalContext } from '@/context/modal-context' type PopupProps = { defaultModel?: DefaultModel modelList: Model[] onSelect: (provider: string, model: ModelItem) => void scopeFeatures?: string[] + onHide: () => void } const Popup: FC = ({ defaultModel, modelList, onSelect, scopeFeatures = [], + onHide, }) => { + const { t } = useTranslation() const language = useLanguage() const [searchText, setSearchText] = useState('') + const { setShowAccountSettingModal } = useModalContext() const filteredModelList = useMemo(() => { return modelList.map((model) => { @@ -99,6 +106,13 @@ const Popup: FC = ({ ) }
+
{ + onHide() + setShowAccountSettingModal({ payload: 'provider' }) + }}> + {t('common.model.settingsLink')} + +
) } diff --git a/web/app/components/plugins/plugin-detail-panel/index.tsx b/web/app/components/plugins/plugin-detail-panel/index.tsx index 4d20c0877d..f0afd15f58 100644 --- a/web/app/components/plugins/plugin-detail-panel/index.tsx +++ b/web/app/components/plugins/plugin-detail-panel/index.tsx @@ -10,6 +10,8 @@ import Drawer from '@/app/components/base/drawer' import type { PluginDetail } from '@/app/components/plugins/types' import cn from '@/utils/classnames' +import ModelParameterModal from '@/app/components/plugins/plugin-detail-panel/model-selector' + type Props = { detail?: PluginDetail onUpdate: () => void @@ -52,6 +54,15 @@ const PluginDetailPanel: FC = ({ {!!detail.declaration.agent_strategy && } {!!detail.declaration.endpoint && } {!!detail.declaration.model && } + {}} + scope={'llm'} + />
)} diff --git a/web/app/components/plugins/plugin-detail-panel/model-selector/index.tsx b/web/app/components/plugins/plugin-detail-panel/model-selector/index.tsx index 00b0cd74eb..1408b4f9be 100644 --- a/web/app/components/plugins/plugin-detail-panel/model-selector/index.tsx +++ b/web/app/components/plugins/plugin-detail-panel/model-selector/index.tsx @@ -205,7 +205,7 @@ const ModelParameterModal: FC = ({ ) } - +
diff --git a/web/i18n/en-US/common.ts b/web/i18n/en-US/common.ts index 91e27aa044..0dd789aa1b 100644 --- a/web/i18n/en-US/common.ts +++ b/web/i18n/en-US/common.ts @@ -126,6 +126,8 @@ const translation = { Custom: 'Custom', }, addMoreModel: 'Go to settings to add more models', + settingsLink: 'Model Provider Settings', + capabilities: 'MultiModal Capabilities', }, menus: { status: 'beta', diff --git a/web/i18n/zh-Hans/common.ts b/web/i18n/zh-Hans/common.ts index 9a5c377f7f..76e8d4b027 100644 --- a/web/i18n/zh-Hans/common.ts +++ b/web/i18n/zh-Hans/common.ts @@ -126,6 +126,8 @@ const translation = { Custom: '自定义', }, addMoreModel: '添加更多模型', + settingsLink: '模型设置', + capabilities: '多模态能力', }, menus: { status: 'beta', From e47aaad3964eb528d0c985ca9f070e9e24b4eb00 Mon Sep 17 00:00:00 2001 From: JzoNg Date: Tue, 31 Dec 2024 12:42:49 +0800 Subject: [PATCH 13/19] remove test codes --- .../components/plugins/plugin-detail-panel/index.tsx | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/web/app/components/plugins/plugin-detail-panel/index.tsx b/web/app/components/plugins/plugin-detail-panel/index.tsx index f0afd15f58..4d20c0877d 100644 --- a/web/app/components/plugins/plugin-detail-panel/index.tsx +++ b/web/app/components/plugins/plugin-detail-panel/index.tsx @@ -10,8 +10,6 @@ import Drawer from '@/app/components/base/drawer' import type { PluginDetail } from '@/app/components/plugins/types' import cn from '@/utils/classnames' -import ModelParameterModal from '@/app/components/plugins/plugin-detail-panel/model-selector' - type Props = { detail?: PluginDetail onUpdate: () => void @@ -54,15 +52,6 @@ const PluginDetailPanel: FC = ({ {!!detail.declaration.agent_strategy && } {!!detail.declaration.endpoint && } {!!detail.declaration.model && } - {}} - scope={'llm'} - />
)} From 4855e878767acfd5ae5ec7ba92ab7848c0f98f78 Mon Sep 17 00:00:00 2001 From: AkaraChen Date: Tue, 31 Dec 2024 13:46:06 +0800 Subject: [PATCH 14/19] fix: editor --- .../nodes/_base/components/agent-strategy.tsx | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/web/app/components/workflow/nodes/_base/components/agent-strategy.tsx b/web/app/components/workflow/nodes/_base/components/agent-strategy.tsx index 209f3108c9..1e9b8ff827 100644 --- a/web/app/components/workflow/nodes/_base/components/agent-strategy.tsx +++ b/web/app/components/workflow/nodes/_base/components/agent-strategy.tsx @@ -16,6 +16,7 @@ import type { ComponentProps } from 'react' import { useDefaultModel, useLanguage } from '@/app/components/header/account-setting/model-provider-page/hooks' import Editor from './prompt/editor' import { strategyParamToCredientialForm } from '../../agent/panel' +import { useWorkflowStore } from '../../../store' export type Strategy = { agent_strategy_provider_name: string @@ -52,6 +53,10 @@ export const AgentStrategy = (props: AgentStrategyProps) => { const { t } = useTranslation() const language = useLanguage() const defaultModel = useDefaultModel(ModelTypeEnum.textGeneration) + const workflowStore = useWorkflowStore() + const { + setControlPromptEditorRerenderKey, + } = workflowStore.getState() const override: ComponentProps>['override'] = [ [FormTypeEnum.textNumber], (schema, props) => { @@ -130,9 +135,14 @@ export const AgentStrategy = (props: AgentStrategyProps) => { const onChange = (value: string) => { props.onChange({ ...props.value, [schema.variable]: value }) } + const handleGenerated = (value: string) => { + onChange(value) + setControlPromptEditorRerenderKey(Math.random()) + } return { completion_params: {}, } : undefined } - onGenerated={onChange} placeholderClassName='px-2 py-1' inputClassName='px-2 py-1 bg-components-input-bg-normal focus:bg-components-input-bg-active focus:border-components-input-border-active focus:border rounded-lg' /> From 066595f3aa96ae867687b8e4a2f8ba03ac35f7d6 Mon Sep 17 00:00:00 2001 From: AkaraChen Date: Tue, 31 Dec 2024 13:50:31 +0800 Subject: [PATCH 15/19] chore: remove unused code --- .../nodes/_base/components/agent-strategy.tsx | 24 ++----------------- 1 file changed, 2 insertions(+), 22 deletions(-) diff --git a/web/app/components/workflow/nodes/_base/components/agent-strategy.tsx b/web/app/components/workflow/nodes/_base/components/agent-strategy.tsx index 1e9b8ff827..402aefc364 100644 --- a/web/app/components/workflow/nodes/_base/components/agent-strategy.tsx +++ b/web/app/components/workflow/nodes/_base/components/agent-strategy.tsx @@ -15,7 +15,6 @@ import Field from './field' import type { ComponentProps } from 'react' import { useDefaultModel, useLanguage } from '@/app/components/header/account-setting/model-provider-page/hooks' import Editor from './prompt/editor' -import { strategyParamToCredientialForm } from '../../agent/panel' import { useWorkflowStore } from '../../../store' export type Strategy = { @@ -144,7 +143,7 @@ export const AgentStrategy = (props: AgentStrategyProps) => { onChange={onChange} onGenerated={handleGenerated} title={schema.label[language]} - headerClassName='bg-transparent px-0 text-text-secondary system-sm-semibold-uppercase !text-base' + headerClassName='bg-transparent px-0 text-text-secondary system-sm-semibold-uppercase' containerClassName='bg-transparent' gradientBorder={false} isSupportPromptGenerator={!!schema.auto_generate?.type} @@ -173,26 +172,7 @@ export const AgentStrategy = (props: AgentStrategyProps) => { strategy ?
- formSchemas={[ - ...formSchema, - ...[{ - name: 'instruction2', - type: 'string', - required: true, - label: { - en_US: 'Instruction2', - zh_Hans: '指令2', - pt_BR: 'Instruction2', - }, - auto_generate: { - type: 'prompt_instruction', - }, - template: { - enabled: true, - }, - // @ts-expect-error just for test - }].map(strategyParamToCredientialForm), - ]} + formSchemas={formSchema} value={formValue} onChange={onFormValueChange} validating={false} From ec2dd750f1d22c47ecfbe692941b6926c86cca4b Mon Sep 17 00:00:00 2001 From: Yi Date: Tue, 31 Dec 2024 13:59:55 +0800 Subject: [PATCH 16/19] fix: set a fixed height to the model selection in agent node --- .../model-selector/deprecated-model-trigger.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/app/components/header/account-setting/model-provider-page/model-selector/deprecated-model-trigger.tsx b/web/app/components/header/account-setting/model-provider-page/model-selector/deprecated-model-trigger.tsx index 8a6b283bdd..ff4509cad6 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-selector/deprecated-model-trigger.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-selector/deprecated-model-trigger.tsx @@ -22,7 +22,7 @@ const ModelTrigger: FC = ({ return (
Date: Tue, 31 Dec 2024 14:04:03 +0800 Subject: [PATCH 17/19] fix the model icon size in the agent node --- .../model-selector/deprecated-model-trigger.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/app/components/header/account-setting/model-provider-page/model-selector/deprecated-model-trigger.tsx b/web/app/components/header/account-setting/model-provider-page/model-selector/deprecated-model-trigger.tsx index ff4509cad6..ce40ab7b22 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-selector/deprecated-model-trigger.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-selector/deprecated-model-trigger.tsx @@ -26,7 +26,7 @@ const ModelTrigger: FC = ({ >
From 21d082f3da220f3c62dcfff1fc248b55c81f8f11 Mon Sep 17 00:00:00 2001 From: zxhlyh Date: Tue, 31 Dec 2024 14:02:50 +0800 Subject: [PATCH 18/19] feat: tool single run --- .../workflow/nodes/agent/use-config.ts | 34 +++++++++++++++++++ .../components/workflow/nodes/tool/panel.tsx | 7 +++- .../workflow/run/agent-log/agent-log-nav.tsx | 2 +- .../run/agent-log/agent-result-panel.tsx | 19 ++++++++++- web/app/components/workflow/run/hooks.ts | 6 ++-- web/app/components/workflow/run/node.tsx | 3 +- .../components/workflow/run/result-panel.tsx | 3 +- .../workflow/run/special-result-panel.tsx | 2 +- 8 files changed, 67 insertions(+), 9 deletions(-) diff --git a/web/app/components/workflow/nodes/agent/use-config.ts b/web/app/components/workflow/nodes/agent/use-config.ts index 9cd3d12d87..fcd1bed5aa 100644 --- a/web/app/components/workflow/nodes/agent/use-config.ts +++ b/web/app/components/workflow/nodes/agent/use-config.ts @@ -1,6 +1,7 @@ import { useStrategyProviderDetail } from '@/service/use-strategy' import useNodeCrud from '../_base/hooks/use-node-crud' import useVarList from '../_base/hooks/use-var-list' +import useOneStepRun from '../_base/hooks/use-one-step-run' import type { AgentNodeType } from './types' import { useNodesReadOnly, @@ -19,6 +20,27 @@ const useConfig = (id: string, payload: AgentNodeType) => { const strategyProvider = useStrategyProviderDetail( inputs.agent_strategy_provider_name || '', ) + + // single run + const agentInputKey = `${id}.input_selector` + const { + isShowSingleRun, + showSingleRun, + hideSingleRun, + toVarInputs, + runningStatus, + handleRun, + handleStop, + runInputData, + setRunInputData, + runResult, + } = useOneStepRun({ + id, + data: inputs, + defaultRunInputData: { + [agentInputKey]: [''], + }, + }) const currentStrategy = strategyProvider.data?.declaration.strategies.find( str => str.identity.name === inputs.agent_strategy_name, ) @@ -59,6 +81,18 @@ const useConfig = (id: string, payload: AgentNodeType) => { onFormChange, currentStrategyStatus, strategyProvider: strategyProvider.data, + + isShowSingleRun, + showSingleRun, + hideSingleRun, + toVarInputs, + runningStatus, + handleRun, + handleStop, + runInputData, + setRunInputData, + runResult, + agentInputKey, } } diff --git a/web/app/components/workflow/nodes/tool/panel.tsx b/web/app/components/workflow/nodes/tool/panel.tsx index 01dff077eb..251e7242bf 100644 --- a/web/app/components/workflow/nodes/tool/panel.tsx +++ b/web/app/components/workflow/nodes/tool/panel.tsx @@ -15,6 +15,8 @@ import BeforeRunForm from '@/app/components/workflow/nodes/_base/components/befo import OutputVars, { VarItem } from '@/app/components/workflow/nodes/_base/components/output-vars' import ResultPanel from '@/app/components/workflow/run/result-panel' import { useToolIcon } from '@/app/components/workflow/hooks' +import { useLogs } from '@/app/components/workflow/run/hooks' +import formatToTracingNodeList from '@/app/components/workflow/run/utils/format-log' const i18nPrefix = 'workflow.nodes.tool' @@ -51,6 +53,8 @@ const Panel: FC> = ({ outputSchema, } = useConfig(id, data) const toolIcon = useToolIcon(data) + const logsParams = useLogs() + const nodeInfo = formatToTracingNodeList([runResult], t)[0] if (isLoading) { return
@@ -161,7 +165,8 @@ const Panel: FC> = ({ runningStatus={runningStatus} onRun={handleRun} onStop={handleStop} - result={} + {...logsParams} + result={} /> )}
diff --git a/web/app/components/workflow/run/agent-log/agent-log-nav.tsx b/web/app/components/workflow/run/agent-log/agent-log-nav.tsx index a56730a45a..21663c8c56 100644 --- a/web/app/components/workflow/run/agent-log/agent-log-nav.tsx +++ b/web/app/components/workflow/run/agent-log/agent-log-nav.tsx @@ -4,7 +4,7 @@ import Button from '@/app/components/base/button' import type { AgentLogItemWithChildren } from '@/types/workflow' type AgentLogNavProps = { - agentOrToolLogItemStack: { id: string; label: string }[] + agentOrToolLogItemStack: AgentLogItemWithChildren[] onShowAgentOrToolLog: (detail?: AgentLogItemWithChildren) => void } const AgentLogNav = ({ diff --git a/web/app/components/workflow/run/agent-log/agent-result-panel.tsx b/web/app/components/workflow/run/agent-log/agent-result-panel.tsx index 3028384f4a..d02e69f8da 100644 --- a/web/app/components/workflow/run/agent-log/agent-result-panel.tsx +++ b/web/app/components/workflow/run/agent-log/agent-result-panel.tsx @@ -1,9 +1,10 @@ +import { RiAlertFill } from '@remixicon/react' import AgentLogItem from './agent-log-item' import AgentLogNav from './agent-log-nav' import type { AgentLogItemWithChildren } from '@/types/workflow' type AgentResultPanelProps = { - agentOrToolLogItemStack: { id: string; label: string }[] + agentOrToolLogItemStack: AgentLogItemWithChildren[] agentOrToolLogListMap: Record onShowAgentOrToolLog: (detail?: AgentLogItemWithChildren) => void } @@ -34,6 +35,22 @@ const AgentResultPanel = ({ }
} + { + top.hasCircle && ( +
+
+ +
+ There is circular invocation of tools/nodes in the current workflow. +
+
+ ) + }
) } diff --git a/web/app/components/workflow/run/hooks.ts b/web/app/components/workflow/run/hooks.ts index b9c879a204..55ddc4cbfc 100644 --- a/web/app/components/workflow/run/hooks.ts +++ b/web/app/components/workflow/run/hooks.ts @@ -33,7 +33,7 @@ export const useLogs = () => { setIterationResultDurationMap(iterDurationMap) }, [setShowIteratingDetailTrue, setIterationResultList, setIterationResultDurationMap]) - const [agentOrToolLogItemStack, setAgentOrToolLogItemStack] = useState<{ id: string; label: string }[]>([]) + const [agentOrToolLogItemStack, setAgentOrToolLogItemStack] = useState([]) const agentOrToolLogItemStackRef = useRef(agentOrToolLogItemStack) const [agentOrToolLogListMap, setAgentOrToolLogListMap] = useState>({}) const agentOrToolLogListMapRef = useRef(agentOrToolLogListMap) @@ -43,14 +43,14 @@ export const useLogs = () => { agentOrToolLogItemStackRef.current = [] return } - const { id, label, children } = detail + const { id, children } = detail let currentAgentOrToolLogItemStack = agentOrToolLogItemStackRef.current.slice() const index = currentAgentOrToolLogItemStack.findIndex(logItem => logItem.id === id) if (index > -1) currentAgentOrToolLogItemStack = currentAgentOrToolLogItemStack.slice(0, index + 1) else - currentAgentOrToolLogItemStack = [...currentAgentOrToolLogItemStack.slice(), { id, label }] + currentAgentOrToolLogItemStack = [...currentAgentOrToolLogItemStack.slice(), detail] setAgentOrToolLogItemStack(currentAgentOrToolLogItemStack) agentOrToolLogItemStackRef.current = currentAgentOrToolLogItemStack diff --git a/web/app/components/workflow/run/node.tsx b/web/app/components/workflow/run/node.tsx index 2fdab2bb7b..4d27c9bb4c 100644 --- a/web/app/components/workflow/run/node.tsx +++ b/web/app/components/workflow/run/node.tsx @@ -81,6 +81,7 @@ const NodePanel: FC = ({ const isIterationNode = nodeInfo.node_type === BlockEnum.Iteration const isRetryNode = hasRetryNode(nodeInfo.node_type) && nodeInfo.retryDetail const isAgentNode = nodeInfo.node_type === BlockEnum.Agent + const isToolNode = nodeInfo.node_type === BlockEnum.Tool return (
@@ -144,7 +145,7 @@ const NodePanel: FC = ({ /> )} { - isAgentNode && onShowAgentOrToolLog && ( + (isAgentNode || isToolNode) && onShowAgentOrToolLog && ( = ({ const isIterationNode = nodeInfo?.node_type === BlockEnum.Iteration const isRetryNode = hasRetryNode(nodeInfo?.node_type) && nodeInfo?.retryDetail const isAgentNode = nodeInfo?.node_type === BlockEnum.Agent + const isToolNode = nodeInfo?.node_type === BlockEnum.Tool return (
@@ -90,7 +91,7 @@ const ResultPanel: FC = ({ ) } { - isAgentNode && handleShowAgentOrToolLog && ( + (isAgentNode || isToolNode) && handleShowAgentOrToolLog && ( handleShowAgentOrToolLog?: (detail?: AgentLogItemWithChildren) => void } From d7cbbbca8e7b32da686dd774ff74ddc0ac54603f Mon Sep 17 00:00:00 2001 From: zxhlyh Date: Tue, 31 Dec 2024 14:08:40 +0800 Subject: [PATCH 19/19] fix: tool single run --- web/app/components/workflow/nodes/tool/panel.tsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/web/app/components/workflow/nodes/tool/panel.tsx b/web/app/components/workflow/nodes/tool/panel.tsx index 251e7242bf..93e2e9130f 100644 --- a/web/app/components/workflow/nodes/tool/panel.tsx +++ b/web/app/components/workflow/nodes/tool/panel.tsx @@ -1,5 +1,5 @@ import type { FC } from 'react' -import React from 'react' +import React, { useMemo } from 'react' import { useTranslation } from 'react-i18next' import Split from '../_base/components/split' import type { ToolNodeType } from './types' @@ -54,7 +54,11 @@ const Panel: FC> = ({ } = useConfig(id, data) const toolIcon = useToolIcon(data) const logsParams = useLogs() - const nodeInfo = formatToTracingNodeList([runResult], t)[0] + const nodeInfo = useMemo(() => { + if (!runResult) + return null + return formatToTracingNodeList([runResult], t)[0] + }, [runResult, t]) if (isLoading) { return