feat: config field in /cypher/execute — node-level component override

- /cypher/execute now accepts separate `config` field:
  {node_name: {component: "cmp_19e62efd", ...staticParams}}
- graph-builder reads config[node].component to override componentId
  (supports cmp_ hash, rec_ hash, or canonical_id)
- config[node] other fields become node.data (static params merged at runtime)
- acr run now sends workflow.config as separate `config` (not flattened into context)
- context is now only --input dynamic params

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-16 18:42:26 +08:00
parent 60d3e41905
commit 7b18387113
4 changed files with 31 additions and 10 deletions
+19 -7
View File
@@ -2,22 +2,34 @@ import type { ParsedTriplets } from './triplet-parser';
import { toEdgeType } from './triplet-parser';
import type { SearchResult } from './search-nodes';
/** 從 nodeResults + parsed 組成可直接送入 /execute 的 ExecutionGraph */
/** 從 nodeResults + parsed 組成可直接送入 /execute 的 ExecutionGraph
*
* config 格式(來自 workflow YAML 的 config 欄位):
* { node_name: { component: "cmp_xxxxxxxx" | "rec_xxxxxxxx" | canonical_id, ...params } }
*
* 若 config[node].component 存在,以它覆蓋 searchNodes 偵測到的 componentId。
* config[node] 的其他欄位作為節點靜態參數(node.data),合併進執行 context。
*/
export function buildExecutionGraph(
parsed: ParsedTriplets,
nodeResults: SearchResult['nodeResults'],
graphId: string,
graphName: string,
config?: Record<string, Record<string, unknown>>,
) {
const nodes = [...parsed.nodeNames].map(name => {
const nr = nodeResults[name]!;
const id = name.toLowerCase().replace(/\s+/g, '-');
return {
id,
type: nr.type,
componentId: nr.componentId,
label: name,
};
const nodeConfig = config?.[name] ?? {};
// config[name].component 可以是 hash 或 canonical_id,覆蓋自動偵測的 componentId
const componentId = (nodeConfig.component as string | undefined) ?? nr.componentId;
// 其他 config 欄位作為 node.data(靜態參數)
const { component: _component, ...staticParams } = nodeConfig;
const data = Object.keys(staticParams).length > 0 ? staticParams : undefined;
return { id, type: nr.type, componentId, label: name, data };
});
const edges = parsed.edges.map(e => ({