/** * 从 Agent 工作流中提取 / 写入 LLM 节点的工具(skills)配置。 */ import type { WorkflowNode } from '@/types' /** 与后端 tools_bootstrap 内置工具一致,用于列表展示与配置勾选 */ export const BUILTIN_SKILL_OPTIONS: { name: string; label: string }[] = [ { name: 'http_request', label: 'HTTP 请求' }, { name: 'file_read', label: '读文件' }, { name: 'file_write', label: '写文件' }, { name: 'text_analyze', label: '文本分析' }, { name: 'datetime', label: '日期时间' }, { name: 'math_calculate', label: '数学计算' }, { name: 'system_info', label: '系统信息' }, { name: 'json_process', label: 'JSON 处理' }, { name: 'database_query', label: '数据库查询' }, { name: 'adb_log', label: 'ADB 日志' }, { name: 'agent_call', label: '调用 Agent' }, ] export const BUILTIN_SKILL_LABELS: Record = Object.fromEntries( BUILTIN_SKILL_OPTIONS.map((o) => [o.name, o.label]) ) function isLlmNode(n: WorkflowNode): boolean { const t = (n.type || '').toLowerCase() const dt = (n.data?.type || '').toLowerCase() return t === 'llm' || t === 'template' || dt === 'llm' } /** 工作流中所有 LLM 节点上已选工具的并集(去重排序) */ export function extractSkillToolNames(workflow_config: { nodes?: WorkflowNode[] } | null | undefined): string[] { const set = new Set() for (const n of workflow_config?.nodes || []) { if (!isLlmNode(n)) continue const raw = n.data?.tools ?? n.data?.selected_tools if (Array.isArray(raw)) { raw.forEach((x) => set.add(String(x))) } } return Array.from(set).sort() } /** * 作为「能力配置」写入时的目标节点:优先 llm-unified,否则第一个带工具的 LLM,否则第一个 LLM。 */ export function findPrimaryLlmNodeForTools(nodes: WorkflowNode[] | undefined): WorkflowNode | null { if (!nodes?.length) return null const byId = nodes.find((n) => n.id === 'llm-unified' && isLlmNode(n)) if (byId) return byId const withTools = nodes.find( (n) => isLlmNode(n) && (n.data?.enable_tools === true || (Array.isArray(n.data?.tools) && n.data.tools.length > 0) || (Array.isArray(n.data?.selected_tools) && n.data.selected_tools.length > 0)) ) if (withTools) return withTools return nodes.find((n) => isLlmNode(n)) || null } export function patchWorkflowSkillTools( workflow_config: { nodes: WorkflowNode[]; edges: unknown[] }, toolNames: string[] ): { nodes: WorkflowNode[]; edges: unknown[] } { const wf = JSON.parse(JSON.stringify(workflow_config)) as { nodes: WorkflowNode[]; edges: unknown[] } const target = findPrimaryLlmNodeForTools(wf.nodes) if (!target) return wf const names = [...new Set(toolNames)].filter(Boolean).sort() if (!target.data) target.data = {} target.data.enable_tools = names.length > 0 target.data.tools = names target.data.selected_tools = names return wf }