Files
Arcrun/cypher-executor/src/actions/graph-builder.ts
T
uncle6me-web 922a57fe34 arcrun — AI workflow execution engine (clean history)
Self-hosted 開源:WASM 零件 + recipe + cypher-executor,跑在你自己的 Cloudflare。

此為重建的乾淨歷史起點(移除曾誤 commit 的 GCP SA 金鑰,舊歷史保留在
richblack/arcrun 與本地 backup 分支)。含:
- acr init --self-hosted installer(建 KV/R2 + codeload 拉預編譯 wasm + wrangler deploy + seed recipe)
- recipe push 把關(資料外流提醒 + 打通檢查)
- 19 個正當零件預編譯 wasm(claude_api/km_writer/kbdb_upsert_block 排除:違反 DECISIONS §1)
- CLI / cypher-executor / registry / 完整 SDD

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 15:52:38 +08:00

57 lines
2.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type { ParsedTriplets } from './triplet-parser';
import { toEdgeType } from './triplet-parser';
import type { SearchResult } from './search-nodes';
/** 從 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, '-');
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 => {
// 「對每個 X」label 抽 iteratorcypher binding 表達 FOREACH 的迭代變數
// 例:'A >> 對每個 paragraph >> B' → type=FOREACH, iterator='paragraph'
// getIterableFromContext 會找 ctx.paragraphs(複數)或 ctx.paragraph
let iterator: string | undefined;
let label = e.label;
const foreachMatch = label.match(/^(?:對每個|FOREACH)\s+(\w+)$/i);
if (foreachMatch) {
iterator = foreachMatch[1];
label = '對每個'; // 改回標準 label 走 SEMANTIC_EDGE_MAP
}
const edge: { from: string; to: string; type: ReturnType<typeof toEdgeType>; iterator?: string } = {
from: e.from.toLowerCase().replace(/\s+/g, '-'),
to: e.to.toLowerCase().replace(/\s+/g, '-'),
type: toEdgeType(label),
};
if (iterator) edge.iterator = iterator;
return edge;
});
return { id: graphId, name: graphName, nodes, edges };
}