2026-04-08 11:44:24 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 从 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: '数据库查询' },
|
2026-05-03 21:57:30 +08:00
|
|
|
|
{ name: 'adb_log', label: 'ADB 日志' },
|
|
|
|
|
|
{ name: 'agent_call', label: '调用 Agent' },
|
2026-04-08 11:44:24 +08:00
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
export const BUILTIN_SKILL_LABELS: Record<string, string> = 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<string>()
|
|
|
|
|
|
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
|
|
|
|
|
|
}
|