922a57fe34
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>
57 lines
2.4 KiB
TypeScript
57 lines
2.4 KiB
TypeScript
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 抽 iterator:cypher 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 };
|
||
}
|