import type { FC } from 'react' import type { ToolNodeType } from './types' import type { StrategyDetail, StrategyPluginDetail } from '@/app/components/plugins/types' import type { AgentNodeType } from '@/app/components/workflow/nodes/agent/types' import type { CommonNodeType, NodeProps, Node as WorkflowNode } from '@/app/components/workflow/types' import * as React from 'react' import { useMemo } from 'react' import { useTranslation } from 'react-i18next' import { useNodes } from 'reactflow' import AlertTriangle from '@/app/components/base/icons/src/vender/solid/alertsAndFeedback/AlertTriangle' import { FormTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' import BlockIcon from '@/app/components/workflow/block-icon' import { useNodesMetaData } from '@/app/components/workflow/hooks' import { useNodePluginInstallation } from '@/app/components/workflow/hooks/use-node-plugin-installation' import { InstallPluginButton } from '@/app/components/workflow/nodes/_base/components/install-plugin-button' import { BlockEnum } from '@/app/components/workflow/types' import { AGENT_CONTEXT_VAR_PATTERN, getAgentNodeIdFromContextVar } from '@/app/components/workflow/utils/agent-context' import { useGetLanguage } from '@/context/i18n' import { useStrategyProviders } from '@/service/use-strategy' import { cn } from '@/utils/classnames' import { VarType } from './types' type AgentCheckValidContext = { provider?: StrategyPluginDetail strategy?: StrategyDetail language: string isReadyForCheckValid: boolean } const Node: FC> = ({ id, data, }) => { const { t } = useTranslation() const language = useGetLanguage() const { nodesMap: nodesMetaDataMap } = useNodesMetaData() const { data: strategyProviders } = useStrategyProviders() const nodes = useNodes() const { tool_configurations, paramSchemas } = data const toolConfigs = Object.keys(tool_configurations || {}) const { isChecking, isMissing, uniqueIdentifier, canInstall, onInstallSuccess, shouldDim, } = useNodePluginInstallation(data) const showInstallButton = !isChecking && isMissing && canInstall && uniqueIdentifier const nodesById = useMemo(() => { return nodes.reduce((acc, node) => { acc[node.id] = node return acc }, {} as Record) }, [nodes]) const nestedNodeEntries = useMemo(() => { const entries: Array<{ agentNodeId: string, extractorNodeId?: string, paramKey: string }> = [] const seen = new Set() const toolParams = data.tool_parameters || {} Object.entries(toolParams).forEach(([paramKey, param]) => { const value = param?.value if (typeof value !== 'string') return const matches = value.matchAll(AGENT_CONTEXT_VAR_PATTERN) for (const match of matches) { const agentNodeId = getAgentNodeIdFromContextVar(match[0]) if (!agentNodeId) continue const entryKey = `${paramKey}:${agentNodeId}` if (seen.has(entryKey)) continue seen.add(entryKey) entries.push({ agentNodeId, paramKey, extractorNodeId: param?.nested_node_config?.extractor_node_id || (param?.type === VarType.nested_node ? `${id}_ext_${paramKey}` : undefined), }) } }) return entries }, [data.tool_parameters, id]) const referenceItems = useMemo(() => { if (!nestedNodeEntries.length) return [] const getNodeWarning = (node?: WorkflowNode) => { if (!node) return true const validator = nodesMetaDataMap?.[node.data.type as BlockEnum]?.checkValid if (!validator) return false let moreDataForCheckValid: AgentCheckValidContext | undefined if (node.data.type === BlockEnum.Agent) { const agentData = node.data as AgentNodeType const isReadyForCheckValid = !!strategyProviders const provider = strategyProviders?.find(provider => provider.declaration.identity.name === agentData.agent_strategy_provider_name) const strategy = provider?.declaration.strategies?.find(s => s.identity.name === agentData.agent_strategy_name) moreDataForCheckValid = { provider, strategy, language, isReadyForCheckValid, } } const { errorMessage } = validator(node.data, t, moreDataForCheckValid) return Boolean(errorMessage) } return nestedNodeEntries.map(({ agentNodeId, extractorNodeId, paramKey }) => { const agentNode = nodesById[agentNodeId] const agentLabel = `@${agentNode?.data.title || agentNodeId}` const agentWarning = getNodeWarning(agentNode) const extractorWarning = extractorNodeId ? getNodeWarning(nodesById[extractorNodeId]) : false const hasWarning = agentWarning || extractorWarning return { key: `${paramKey}-${agentNodeId}-${extractorNodeId || 'no-extractor'}`, label: agentLabel, type: BlockEnum.Agent, hasWarning, } }) }, [nestedNodeEntries, nodesById, nodesMetaDataMap, strategyProviders, language, t]) const hasConfigs = toolConfigs.length > 0 const hasReferences = referenceItems.length > 0 if (!showInstallButton && !hasConfigs && !hasReferences) return null return (
{showInstallButton && (
)} {hasConfigs && (
{toolConfigs.map((key, index) => (
{key}
{typeof tool_configurations[key].value === 'string' && (
{paramSchemas?.find(i => i.name === key)?.type === FormTypeEnum.secretInput ? '********' : tool_configurations[key].value}
)} {typeof tool_configurations[key].value === 'number' && (
{Number.isNaN(tool_configurations[key].value) ? '' : tool_configurations[key].value}
)} {typeof tool_configurations[key] !== 'string' && tool_configurations[key]?.type === FormTypeEnum.modelSelector && (
{tool_configurations[key].model}
)}
))}
)} {hasReferences && (
{referenceItems.map(item => (
{item.label}
{item.hasWarning && ( )}
))}
)}
) } export default React.memo(Node)