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>
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
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 };
|
||||
}
|
||||
Reference in New Issue
Block a user