@darkhorseprojects/circuitry 0.4.0 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +7346 -9
- package/dist/index.d.ts +1 -8
- package/dist/index.js +7404 -821
- package/dist/resolve.d.ts +9 -0
- package/dist/resolve.js +7548 -0
- package/package.json +8 -6
- package/dist/bundle.d.ts +0 -32
- package/dist/host.d.ts +0 -41
- package/dist/node.d.ts +0 -58
- package/dist/node.js +0 -824
- package/dist/presets.d.ts +0 -4
- package/dist/runtime.d.ts +0 -16
- package/dist/scheduler.d.ts +0 -30
- package/dist/simulation.d.ts +0 -89
- package/dist/tools.d.ts +0 -553
- package/dist/types.d.ts +0 -41
package/dist/node.js
DELETED
|
@@ -1,824 +0,0 @@
|
|
|
1
|
-
// src/node.ts
|
|
2
|
-
import { access, readFile, writeFile } from "node:fs/promises";
|
|
3
|
-
import fs from "node:fs";
|
|
4
|
-
import path from "node:path";
|
|
5
|
-
import os from "node:os";
|
|
6
|
-
|
|
7
|
-
// src/yaml.ts
|
|
8
|
-
import YAML2 from "yaml";
|
|
9
|
-
|
|
10
|
-
// src/graph.ts
|
|
11
|
-
import YAML from "yaml";
|
|
12
|
-
var CIRCUITRY_SPEC_VERSION = "0.4";
|
|
13
|
-
var DEFAULT_CIRCUITRY_VALIDATION_STANDARD = {
|
|
14
|
-
version: CIRCUITRY_SPEC_VERSION,
|
|
15
|
-
requireSpecVersion: true
|
|
16
|
-
};
|
|
17
|
-
var createCircuitryValidationStandard = (standard = {}) => ({
|
|
18
|
-
version: standard.version || DEFAULT_CIRCUITRY_VALIDATION_STANDARD.version,
|
|
19
|
-
requireSpecVersion: standard.requireSpecVersion ?? DEFAULT_CIRCUITRY_VALIDATION_STANDARD.requireSpecVersion
|
|
20
|
-
});
|
|
21
|
-
var getResourceInputs = (resource) => {
|
|
22
|
-
const inputs = resource.inputs;
|
|
23
|
-
if (!inputs) return [];
|
|
24
|
-
if (Array.isArray(inputs)) return inputs.map((from) => ({ from, to: "" }));
|
|
25
|
-
return Object.entries(inputs).map(([name, from]) => ({ from, to: "", name }));
|
|
26
|
-
};
|
|
27
|
-
var resourceInputIds = (resource) => getResourceInputs(resource).map((dependency) => dependency.from);
|
|
28
|
-
var getCircuitryDependencies = (graph) => {
|
|
29
|
-
const dependencies = [];
|
|
30
|
-
for (const [to, resource] of Object.entries(graph.resources || {})) {
|
|
31
|
-
for (const dependency of getResourceInputs(resource)) dependencies.push({ ...dependency, to });
|
|
32
|
-
}
|
|
33
|
-
return dependencies;
|
|
34
|
-
};
|
|
35
|
-
var getCircuitryRunSet = (graph, entry = graph.entry) => {
|
|
36
|
-
if (!entry) return [];
|
|
37
|
-
const visited = /* @__PURE__ */ new Set();
|
|
38
|
-
const visit = (id) => {
|
|
39
|
-
if (visited.has(id)) return;
|
|
40
|
-
const resource = graph.resources?.[id];
|
|
41
|
-
if (!resource) return;
|
|
42
|
-
for (const input of resourceInputIds(resource)) visit(input);
|
|
43
|
-
visited.add(id);
|
|
44
|
-
};
|
|
45
|
-
visit(entry);
|
|
46
|
-
return [...visited];
|
|
47
|
-
};
|
|
48
|
-
var toposortCircuitryResources = (graph, entry) => {
|
|
49
|
-
const ids = entry ? new Set(getCircuitryRunSet(graph, entry)) : new Set(Object.keys(graph.resources || {}));
|
|
50
|
-
const visiting = /* @__PURE__ */ new Set();
|
|
51
|
-
const visited = /* @__PURE__ */ new Set();
|
|
52
|
-
const ordered = [];
|
|
53
|
-
const visit = (id) => {
|
|
54
|
-
if (!ids.has(id) || visited.has(id)) return;
|
|
55
|
-
if (visiting.has(id)) throw new Error(`Circuitry resource dependency cycle at ${id}`);
|
|
56
|
-
visiting.add(id);
|
|
57
|
-
const resource = graph.resources?.[id];
|
|
58
|
-
if (resource) for (const input of resourceInputIds(resource)) visit(input);
|
|
59
|
-
visiting.delete(id);
|
|
60
|
-
visited.add(id);
|
|
61
|
-
ordered.push(id);
|
|
62
|
-
};
|
|
63
|
-
for (const id of ids) visit(id);
|
|
64
|
-
return ordered;
|
|
65
|
-
};
|
|
66
|
-
var runtimeInputToText = (value) => {
|
|
67
|
-
if (typeof value === "string") return value;
|
|
68
|
-
if (value === void 0) return "";
|
|
69
|
-
return YAML.stringify(value).trimEnd();
|
|
70
|
-
};
|
|
71
|
-
var resolveCircuitrySource = (graph) => {
|
|
72
|
-
const inputs = graph.inputs || graph.args || {};
|
|
73
|
-
const resources = { ...graph.resources || {} };
|
|
74
|
-
for (const [id, input] of Object.entries(inputs)) {
|
|
75
|
-
if (!resources[id]) {
|
|
76
|
-
resources[id] = {
|
|
77
|
-
type: "input",
|
|
78
|
-
from: id,
|
|
79
|
-
label: input.label,
|
|
80
|
-
description: input.description,
|
|
81
|
-
mime: input.mime || input.mimeType
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
return { ...graph, circuitry: CIRCUITRY_SPEC_VERSION, imports: graph.imports || [], inputs, resources };
|
|
86
|
-
};
|
|
87
|
-
var applyCircuitryRuntimeInputs = (graph, inputs = {}) => {
|
|
88
|
-
const next = resolveCircuitrySource(graph);
|
|
89
|
-
const resources = { ...next.resources || {} };
|
|
90
|
-
for (const [id, value] of Object.entries(inputs)) {
|
|
91
|
-
const resource = resources[id];
|
|
92
|
-
if (!resource) throw new Error(`unknown input: ${id}`);
|
|
93
|
-
if (resource.type === "input") resources[id] = { ...resource, value, from: resource.from || id };
|
|
94
|
-
else resources[id] = { ...resource, value: runtimeInputToText(value) };
|
|
95
|
-
}
|
|
96
|
-
return { ...next, resources };
|
|
97
|
-
};
|
|
98
|
-
var validateCircuitrySource = (graph, standard = {}) => {
|
|
99
|
-
const resolvedStandard = createCircuitryValidationStandard(standard);
|
|
100
|
-
const errors = [];
|
|
101
|
-
const issue = (code, message = code, path2) => errors.push({ code, message, ...path2 ? { path: path2 } : {} });
|
|
102
|
-
if (!graph || typeof graph !== "object" || Array.isArray(graph)) {
|
|
103
|
-
return { ok: false, errors: [{ code: "invalid_graph", message: "invalid graph" }], standard: resolvedStandard };
|
|
104
|
-
}
|
|
105
|
-
const g = graph;
|
|
106
|
-
if (resolvedStandard.requireSpecVersion && g.circuitry !== resolvedStandard.version) issue("invalid_version", "invalid version", ["circuitry"]);
|
|
107
|
-
if (!g.resources || Object.keys(g.resources).length === 0) issue("missing_resources", "missing resources", ["resources"]);
|
|
108
|
-
if (g.nodes) issue("forbidden_nodes", "forbidden nodes", ["nodes"]);
|
|
109
|
-
if (g.edges) issue("forbidden_edges", "forbidden edges", ["edges"]);
|
|
110
|
-
if (g.agents) issue("forbidden_agents", "forbidden agents", ["agents"]);
|
|
111
|
-
const resolved = resolveCircuitrySource(g);
|
|
112
|
-
const ids = new Set(Object.keys(resolved.resources || {}));
|
|
113
|
-
if (resolved.entry && !ids.has(resolved.entry)) issue("unknown_entry", "unknown entry", ["entry"]);
|
|
114
|
-
for (const [name, target] of Object.entries(resolved.entries || {})) {
|
|
115
|
-
if (!ids.has(target)) issue("unknown_entry", `unknown entry: ${name}`, ["entries", name]);
|
|
116
|
-
}
|
|
117
|
-
for (const [id, resource] of Object.entries(resolved.resources || {})) {
|
|
118
|
-
if (!resource.type) issue("missing_resource_type", "missing resource type", ["resources", id, "type"]);
|
|
119
|
-
for (const input of resourceInputIds(resource)) {
|
|
120
|
-
if (!ids.has(input)) issue("unknown_resource_input", `unknown resource input: ${input}`, ["resources", id, "inputs"]);
|
|
121
|
-
if (input === id) issue("self_input", "self input", ["resources", id, "inputs"]);
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
for (const [name, output] of Object.entries(resolved.outputs || {})) {
|
|
125
|
-
const root = output.from.split(".")[0];
|
|
126
|
-
if (!ids.has(root)) issue("unknown_output_source", `unknown output source: ${output.from}`, ["outputs", name, "from"]);
|
|
127
|
-
}
|
|
128
|
-
try {
|
|
129
|
-
toposortCircuitryResources(resolved);
|
|
130
|
-
} catch {
|
|
131
|
-
issue("cycle", "cycle");
|
|
132
|
-
}
|
|
133
|
-
return { ok: errors.length === 0, errors, standard: resolvedStandard };
|
|
134
|
-
};
|
|
135
|
-
var validateCircuitryGraphWithStandard = validateCircuitrySource;
|
|
136
|
-
var validateCircuitryExecutionGraphWithStandard = validateCircuitrySource;
|
|
137
|
-
var validateCircuitryGraph = (graph, standard = {}) => validateCircuitrySource(graph, standard).errors.map((error) => error.message);
|
|
138
|
-
var normalizeCircuitryGraph = (graph) => {
|
|
139
|
-
const resolved = resolveCircuitrySource(graph);
|
|
140
|
-
const nodes = [];
|
|
141
|
-
for (const [id, resource] of Object.entries(resolved.resources || {})) {
|
|
142
|
-
if (resource.type === "agent") nodes.push({ id, kind: "agent", label: resource.label || resource.identity || id, agent: { identity: resource.identity, model: resource.model, tools: resource.tools, instructions: resource.instructions, personality: resource.personality, context: resource.context }, skills: resource.skills, expect: resource.expect });
|
|
143
|
-
else nodes.push({ id, kind: resource.type === "tool" ? "tool" : "input", label: resource.label || id, input: { type: resource.type, value: typeof resource.value === "string" ? resource.value : void 0, uri: resource.uri || resource.path, mimeType: resource.mime || resource.mimeType, data: resource.data } });
|
|
144
|
-
}
|
|
145
|
-
const edges = getCircuitryDependencies(resolved).map(({ from, to }) => ({ from, to, kind: "dependency" }));
|
|
146
|
-
return { ...resolved, nodes, edges };
|
|
147
|
-
};
|
|
148
|
-
|
|
149
|
-
// src/yaml.ts
|
|
150
|
-
var parseYamlData = (text) => {
|
|
151
|
-
try {
|
|
152
|
-
return YAML2.parse(text);
|
|
153
|
-
} catch (error) {
|
|
154
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
155
|
-
throw new Error(`Could not parse YAML.
|
|
156
|
-
${message}`);
|
|
157
|
-
}
|
|
158
|
-
};
|
|
159
|
-
var stringifyYamlData = (value) => YAML2.stringify(value, {
|
|
160
|
-
aliasDuplicateObjects: false,
|
|
161
|
-
defaultKeyType: "PLAIN",
|
|
162
|
-
defaultStringType: "QUOTE_DOUBLE",
|
|
163
|
-
doubleQuotedAsJSON: true
|
|
164
|
-
});
|
|
165
|
-
var parseCircuitryYaml = (text, standard = {}, options = {}) => {
|
|
166
|
-
const graph = parseYamlData(text);
|
|
167
|
-
if (!graph || typeof graph !== "object" || Array.isArray(graph)) {
|
|
168
|
-
throw new Error("Circuitry YAML must be a graph object.");
|
|
169
|
-
}
|
|
170
|
-
if (options.validate !== false) {
|
|
171
|
-
const errors = validateCircuitryGraph(graph, standard);
|
|
172
|
-
if (errors.length) throw new Error(`Invalid Circuitry graph:
|
|
173
|
-
${errors.join("\n")}`);
|
|
174
|
-
}
|
|
175
|
-
return graph;
|
|
176
|
-
};
|
|
177
|
-
var stringifyCircuitryYaml = (graph) => YAML2.stringify(graph);
|
|
178
|
-
var parseCircuitryText = (text, standard = {}, options = {}) => parseCircuitryYaml(text, standard, options);
|
|
179
|
-
var stringifyCircuitryText = (graph) => stringifyCircuitryYaml(graph);
|
|
180
|
-
|
|
181
|
-
// src/scheduler.ts
|
|
182
|
-
import YAML3 from "yaml";
|
|
183
|
-
var buildCircuitryExecutionGraph = (graph) => {
|
|
184
|
-
const normalized = normalizeCircuitryGraph(graph);
|
|
185
|
-
const nodes = /* @__PURE__ */ new Map();
|
|
186
|
-
const inputSets = /* @__PURE__ */ new Map();
|
|
187
|
-
const outputSets = /* @__PURE__ */ new Map();
|
|
188
|
-
for (const node of normalized.nodes || []) {
|
|
189
|
-
if (node.kind !== "agent" && node.kind !== "tool" && node.kind !== "output" && node.kind !== "input") {
|
|
190
|
-
continue;
|
|
191
|
-
}
|
|
192
|
-
nodes.set(node.id, {
|
|
193
|
-
id: node.id,
|
|
194
|
-
node,
|
|
195
|
-
inputNodeIds: [],
|
|
196
|
-
outputNodeIds: [],
|
|
197
|
-
// Input nodes are already "done" with their value as output
|
|
198
|
-
...node.kind === "input" ? { output: node.input?.value || "", completed: true } : {}
|
|
199
|
-
});
|
|
200
|
-
inputSets.set(node.id, /* @__PURE__ */ new Set());
|
|
201
|
-
outputSets.set(node.id, /* @__PURE__ */ new Set());
|
|
202
|
-
}
|
|
203
|
-
for (const edge of normalized.edges || []) {
|
|
204
|
-
if (!nodes.has(edge.to)) {
|
|
205
|
-
continue;
|
|
206
|
-
}
|
|
207
|
-
inputSets.get(edge.to).add(edge.from);
|
|
208
|
-
if (nodes.has(edge.from)) {
|
|
209
|
-
outputSets.get(edge.from).add(edge.to);
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
for (const [id, node] of nodes) {
|
|
213
|
-
node.inputNodeIds = [...inputSets.get(id)];
|
|
214
|
-
node.outputNodeIds = [...outputSets.get(id)];
|
|
215
|
-
}
|
|
216
|
-
return { nodes };
|
|
217
|
-
};
|
|
218
|
-
var formatEdgeData = (data) => {
|
|
219
|
-
if (!data || Object.keys(data).length === 0) {
|
|
220
|
-
return "";
|
|
221
|
-
}
|
|
222
|
-
return ` data=${YAML3.stringify(data).trimEnd()}`;
|
|
223
|
-
};
|
|
224
|
-
var describeEdge = (edge, direction) => {
|
|
225
|
-
const otherId = direction === "incoming" ? edge.from : edge.to;
|
|
226
|
-
const kind = edge.kind ? ` [${edge.kind}]` : "";
|
|
227
|
-
const label = edge.label ? `: ${edge.label}` : "";
|
|
228
|
-
return `- ${direction === "incoming" ? "from" : "to"} ${otherId}${kind}${label}${formatEdgeData(edge.data)}`;
|
|
229
|
-
};
|
|
230
|
-
var composeCircuitryPrompt = (graph, node, inputs, contextInputs) => {
|
|
231
|
-
const agent = node.agent || {};
|
|
232
|
-
const upstreamSection = inputs.length ? inputs.map(
|
|
233
|
-
(input, index) => `Upstream ${index + 1} from ${input.nodeId}:
|
|
234
|
-
${input.output}`
|
|
235
|
-
).join("\n\n") : "No upstream node outputs.";
|
|
236
|
-
const contextSection = contextInputs.length ? contextInputs.map(
|
|
237
|
-
(input, index) => input.kind === "text" ? `Context ${index + 1} [text] from ${input.sourceId}:
|
|
238
|
-
${input.text || ""}` : `Context ${index + 1} [${input.kind}] from ${input.sourceId}${input.text ? `:
|
|
239
|
-
${input.text}` : ""}`
|
|
240
|
-
).join("\n\n") : "No connected external context.";
|
|
241
|
-
const incomingEdges = (graph.edges || []).filter((edge) => edge.to === node.id);
|
|
242
|
-
const outgoingEdges = (graph.edges || []).filter((edge) => edge.from === node.id);
|
|
243
|
-
const wiringSection = [
|
|
244
|
-
"Canvas Wiring:",
|
|
245
|
-
"Incoming edges:",
|
|
246
|
-
incomingEdges.length ? incomingEdges.map((edge) => describeEdge(edge, "incoming")).join("\n") : "- none",
|
|
247
|
-
"Outgoing edges:",
|
|
248
|
-
outgoingEdges.length ? outgoingEdges.map((edge) => describeEdge(edge, "outgoing")).join("\n") : "- none"
|
|
249
|
-
].join("\n");
|
|
250
|
-
return [
|
|
251
|
-
`Node label: ${node.label}`,
|
|
252
|
-
agent.identity ? `Identity: ${agent.identity}` : "",
|
|
253
|
-
agent.context ? `Context:
|
|
254
|
-
${agent.context}` : "",
|
|
255
|
-
agent.personality ? `Personality:
|
|
256
|
-
${agent.personality}` : "",
|
|
257
|
-
agent.instructions ? `Instructions:
|
|
258
|
-
${agent.instructions}` : "",
|
|
259
|
-
wiringSection,
|
|
260
|
-
`Upstream Node Outputs:
|
|
261
|
-
${upstreamSection}`,
|
|
262
|
-
`Connected Context Inputs:
|
|
263
|
-
${contextSection}`
|
|
264
|
-
].filter(Boolean).join("\n\n");
|
|
265
|
-
};
|
|
266
|
-
var collectImages = (contextInputs) => contextInputs.filter((item) => item.kind === "image" && item.image).map((item) => item.image);
|
|
267
|
-
var stripYamlFence = (output) => {
|
|
268
|
-
const trimmed = output.trim();
|
|
269
|
-
const fenced = trimmed.match(/^```(?:ya?ml)?\s*([\s\S]*?)\s*```$/i);
|
|
270
|
-
return fenced ? fenced[1].trim() : trimmed;
|
|
271
|
-
};
|
|
272
|
-
var parseExpectedOutput = (nodeId, output) => {
|
|
273
|
-
try {
|
|
274
|
-
return YAML3.parse(stripYamlFence(output));
|
|
275
|
-
} catch (error) {
|
|
276
|
-
const detail = error instanceof Error ? error.message : String(error);
|
|
277
|
-
throw new Error(`Node ${nodeId} output does not match expect: output is not valid YAML (${detail})`);
|
|
278
|
-
}
|
|
279
|
-
};
|
|
280
|
-
var isFieldObject = (schema) => !!schema && typeof schema === "object" && !Array.isArray(schema) && typeof schema.type === "string";
|
|
281
|
-
var describeExpectedType = (schema) => Array.isArray(schema) ? "list" : typeof schema === "string" ? schema : isFieldObject(schema) ? schema.type : "dict";
|
|
282
|
-
var matchesPrimitiveType = (value, type) => {
|
|
283
|
-
switch (type) {
|
|
284
|
-
case "str":
|
|
285
|
-
return typeof value === "string";
|
|
286
|
-
case "int":
|
|
287
|
-
return Number.isInteger(value);
|
|
288
|
-
case "float":
|
|
289
|
-
return typeof value === "number" && Number.isFinite(value);
|
|
290
|
-
case "bool":
|
|
291
|
-
return typeof value === "boolean";
|
|
292
|
-
case "list":
|
|
293
|
-
return Array.isArray(value);
|
|
294
|
-
case "dict":
|
|
295
|
-
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
296
|
-
default:
|
|
297
|
-
return true;
|
|
298
|
-
}
|
|
299
|
-
};
|
|
300
|
-
var validateExpectValue = (value, schema, path2, errors) => {
|
|
301
|
-
if (typeof schema === "string") {
|
|
302
|
-
if (!matchesPrimitiveType(value, schema)) {
|
|
303
|
-
errors.push(`${path2} expected ${schema}, got ${Array.isArray(value) ? "list" : typeof value}`);
|
|
304
|
-
}
|
|
305
|
-
return;
|
|
306
|
-
}
|
|
307
|
-
if (Array.isArray(schema)) {
|
|
308
|
-
if (!Array.isArray(value)) {
|
|
309
|
-
errors.push(`${path2} expected list, got ${typeof value}`);
|
|
310
|
-
return;
|
|
311
|
-
}
|
|
312
|
-
if (schema.length > 0) {
|
|
313
|
-
value.forEach((item, index) => validateExpectValue(item, schema[0], `${path2}[${index}]`, errors));
|
|
314
|
-
}
|
|
315
|
-
return;
|
|
316
|
-
}
|
|
317
|
-
if (isFieldObject(schema)) {
|
|
318
|
-
if (schema.optional && value === void 0) return;
|
|
319
|
-
if (!matchesPrimitiveType(value, schema.type)) {
|
|
320
|
-
errors.push(`${path2} expected ${schema.type}, got ${Array.isArray(value) ? "list" : typeof value}`);
|
|
321
|
-
return;
|
|
322
|
-
}
|
|
323
|
-
if (schema.contains && typeof value === "string" && !value.includes(schema.contains)) {
|
|
324
|
-
errors.push(`${path2} must include string: ${schema.contains}`);
|
|
325
|
-
}
|
|
326
|
-
if (schema.type === "list" && schema.items && Array.isArray(value)) {
|
|
327
|
-
value.forEach((item, index) => validateExpectValue(item, schema.items, `${path2}[${index}]`, errors));
|
|
328
|
-
}
|
|
329
|
-
return;
|
|
330
|
-
}
|
|
331
|
-
if (schema && typeof schema === "object") {
|
|
332
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
333
|
-
errors.push(`${path2} expected dict, got ${Array.isArray(value) ? "list" : typeof value}`);
|
|
334
|
-
return;
|
|
335
|
-
}
|
|
336
|
-
for (const [key, childSchema] of Object.entries(schema)) {
|
|
337
|
-
const childOptional = isFieldObject(childSchema) && childSchema.optional;
|
|
338
|
-
const childValue = value[key];
|
|
339
|
-
if (childValue === void 0 && !childOptional) {
|
|
340
|
-
errors.push(`${path2}.${key} is missing required field of type ${describeExpectedType(childSchema)}`);
|
|
341
|
-
continue;
|
|
342
|
-
}
|
|
343
|
-
validateExpectValue(childValue, childSchema, `${path2}.${key}`, errors);
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
};
|
|
347
|
-
var assertExpectedOutput = (node, output) => {
|
|
348
|
-
if (!node.expect) return;
|
|
349
|
-
const parsed = parseExpectedOutput(node.id, output);
|
|
350
|
-
const errors = [];
|
|
351
|
-
validateExpectValue(parsed, node.expect, node.id, errors);
|
|
352
|
-
if (errors.length) {
|
|
353
|
-
throw new Error(`Node ${node.id} output does not match expect:
|
|
354
|
-
${errors.join("\n")}`);
|
|
355
|
-
}
|
|
356
|
-
};
|
|
357
|
-
var executeCircuitryNode = async ({
|
|
358
|
-
graph,
|
|
359
|
-
item,
|
|
360
|
-
cycle,
|
|
361
|
-
inputPayload,
|
|
362
|
-
contextInputs,
|
|
363
|
-
defaultModel,
|
|
364
|
-
executeNode,
|
|
365
|
-
onNodeStart,
|
|
366
|
-
onNodeComplete
|
|
367
|
-
}) => {
|
|
368
|
-
onNodeStart?.(item.id, cycle);
|
|
369
|
-
if (item.node.kind === "output") {
|
|
370
|
-
const output = inputPayload.map((input) => input.output).join("\n\n");
|
|
371
|
-
onNodeComplete?.(item.id, { output });
|
|
372
|
-
return {
|
|
373
|
-
nodeId: item.id,
|
|
374
|
-
cycle,
|
|
375
|
-
inputNodeIds: inputPayload.map((input) => input.nodeId),
|
|
376
|
-
output
|
|
377
|
-
};
|
|
378
|
-
}
|
|
379
|
-
const agent = item.node.agent || {};
|
|
380
|
-
let result;
|
|
381
|
-
try {
|
|
382
|
-
result = await executeNode({
|
|
383
|
-
nodeId: item.id,
|
|
384
|
-
model: (agent.model === "inherit" ? void 0 : agent.model) || (defaultModel === "inherit" ? void 0 : defaultModel) || "inherit",
|
|
385
|
-
tools: agent.tools || [],
|
|
386
|
-
personality: agent.personality || "",
|
|
387
|
-
instructions: agent.instructions || "",
|
|
388
|
-
context: agent.context || "",
|
|
389
|
-
inputs: inputPayload,
|
|
390
|
-
contextInputs,
|
|
391
|
-
images: collectImages(contextInputs),
|
|
392
|
-
prompt: composeCircuitryPrompt(graph, item.node, inputPayload, contextInputs)
|
|
393
|
-
});
|
|
394
|
-
} catch (error) {
|
|
395
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
396
|
-
onNodeComplete?.(item.id, { error: message });
|
|
397
|
-
return {
|
|
398
|
-
nodeId: item.id,
|
|
399
|
-
cycle,
|
|
400
|
-
inputNodeIds: inputPayload.map((input) => input.nodeId),
|
|
401
|
-
output: "",
|
|
402
|
-
error: message
|
|
403
|
-
};
|
|
404
|
-
}
|
|
405
|
-
assertExpectedOutput(item.node, result.output);
|
|
406
|
-
onNodeComplete?.(item.id, { output: result.output });
|
|
407
|
-
return {
|
|
408
|
-
nodeId: item.id,
|
|
409
|
-
cycle,
|
|
410
|
-
inputNodeIds: inputPayload.map((input) => input.nodeId),
|
|
411
|
-
output: result.output
|
|
412
|
-
};
|
|
413
|
-
};
|
|
414
|
-
var runCircuitryGraphExecution = async ({
|
|
415
|
-
graph,
|
|
416
|
-
selectedNodeId,
|
|
417
|
-
executeNode,
|
|
418
|
-
resolveContextInputs,
|
|
419
|
-
onNodeStart,
|
|
420
|
-
onNodeComplete,
|
|
421
|
-
maxParallelRuns = 4
|
|
422
|
-
}) => {
|
|
423
|
-
const executionGraph = buildCircuitryExecutionGraph(graph);
|
|
424
|
-
const runs = [];
|
|
425
|
-
const parallelism = Math.max(1, Math.floor(maxParallelRuns));
|
|
426
|
-
const defaultModel = typeof graph.runtime?.model === "string" ? graph.runtime.model : void 0;
|
|
427
|
-
const runOne = async (nodeId, outputs2) => {
|
|
428
|
-
const cycle = false;
|
|
429
|
-
const item = executionGraph.nodes.get(nodeId);
|
|
430
|
-
const inputPayload = item.inputNodeIds.filter((sourceId) => outputs2.has(sourceId)).map((sourceId) => ({
|
|
431
|
-
nodeId: sourceId,
|
|
432
|
-
output: outputs2.get(sourceId)
|
|
433
|
-
}));
|
|
434
|
-
const contextInputs = await resolveContextInputs?.({
|
|
435
|
-
nodeId,
|
|
436
|
-
inputNodeIds: new Set(item.inputNodeIds),
|
|
437
|
-
graph
|
|
438
|
-
}) || [];
|
|
439
|
-
return executeCircuitryNode({
|
|
440
|
-
graph,
|
|
441
|
-
item,
|
|
442
|
-
cycle,
|
|
443
|
-
inputPayload,
|
|
444
|
-
contextInputs,
|
|
445
|
-
defaultModel,
|
|
446
|
-
executeNode,
|
|
447
|
-
onNodeStart,
|
|
448
|
-
onNodeComplete
|
|
449
|
-
});
|
|
450
|
-
};
|
|
451
|
-
if (selectedNodeId) {
|
|
452
|
-
if (!executionGraph.nodes.has(selectedNodeId)) {
|
|
453
|
-
return runs;
|
|
454
|
-
}
|
|
455
|
-
runs.push(await runOne(selectedNodeId, /* @__PURE__ */ new Map()));
|
|
456
|
-
return runs;
|
|
457
|
-
}
|
|
458
|
-
const completed = /* @__PURE__ */ new Set();
|
|
459
|
-
const queued = /* @__PURE__ */ new Set();
|
|
460
|
-
const outputs = /* @__PURE__ */ new Map();
|
|
461
|
-
const nodeIds = [...executionGraph.nodes.keys()];
|
|
462
|
-
const remainingDependencies = /* @__PURE__ */ new Map();
|
|
463
|
-
const ready = [];
|
|
464
|
-
for (const nodeId of nodeIds) {
|
|
465
|
-
const node = executionGraph.nodes.get(nodeId);
|
|
466
|
-
if (!node.completed) continue;
|
|
467
|
-
completed.add(nodeId);
|
|
468
|
-
outputs.set(nodeId, node.output || "");
|
|
469
|
-
}
|
|
470
|
-
for (const nodeId of nodeIds) {
|
|
471
|
-
if (completed.has(nodeId)) continue;
|
|
472
|
-
const node = executionGraph.nodes.get(nodeId);
|
|
473
|
-
const count = node.inputNodeIds.filter(
|
|
474
|
-
(sourceId) => executionGraph.nodes.has(sourceId) && !completed.has(sourceId)
|
|
475
|
-
).length;
|
|
476
|
-
remainingDependencies.set(nodeId, count);
|
|
477
|
-
if (count === 0) {
|
|
478
|
-
ready.push(nodeId);
|
|
479
|
-
queued.add(nodeId);
|
|
480
|
-
}
|
|
481
|
-
}
|
|
482
|
-
while (completed.size < nodeIds.length) {
|
|
483
|
-
if (ready.length === 0) {
|
|
484
|
-
const blocked = nodeIds.filter((id) => !completed.has(id));
|
|
485
|
-
throw new Error(`Circuitry execution stalled; unresolved dependencies: ${blocked.join(", ")}`);
|
|
486
|
-
}
|
|
487
|
-
const chunk = ready.splice(0, parallelism);
|
|
488
|
-
const chunkResults = await Promise.all(
|
|
489
|
-
chunk.map(async (nodeId) => ({
|
|
490
|
-
nodeId,
|
|
491
|
-
result: await runOne(nodeId, outputs)
|
|
492
|
-
}))
|
|
493
|
-
);
|
|
494
|
-
for (const { nodeId, result } of chunkResults) {
|
|
495
|
-
outputs.set(nodeId, result.output);
|
|
496
|
-
completed.add(nodeId);
|
|
497
|
-
runs.push(result);
|
|
498
|
-
for (const outputNodeId of executionGraph.nodes.get(nodeId).outputNodeIds) {
|
|
499
|
-
if (completed.has(outputNodeId) || queued.has(outputNodeId)) {
|
|
500
|
-
continue;
|
|
501
|
-
}
|
|
502
|
-
const nextCount = Math.max(
|
|
503
|
-
0,
|
|
504
|
-
(remainingDependencies.get(outputNodeId) || 0) - 1
|
|
505
|
-
);
|
|
506
|
-
remainingDependencies.set(outputNodeId, nextCount);
|
|
507
|
-
if (nextCount === 0) {
|
|
508
|
-
ready.push(outputNodeId);
|
|
509
|
-
queued.add(outputNodeId);
|
|
510
|
-
}
|
|
511
|
-
}
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
|
-
return runs;
|
|
515
|
-
};
|
|
516
|
-
|
|
517
|
-
// src/node.ts
|
|
518
|
-
var loadPiSDK = async () => {
|
|
519
|
-
const possiblePaths = [
|
|
520
|
-
path.join(process.cwd(), "node_modules/@earendil-works/pi-coding-agent/dist/index.js"),
|
|
521
|
-
path.join(os.homedir(), ".npm-global/lib/node_modules/@earendil-works/pi-coding-agent/dist/index.js")
|
|
522
|
-
];
|
|
523
|
-
for (const modulePath of possiblePaths) {
|
|
524
|
-
if (fs.existsSync(modulePath)) {
|
|
525
|
-
return await import(modulePath);
|
|
526
|
-
}
|
|
527
|
-
}
|
|
528
|
-
const packageName = "@earendil-works/pi-coding-agent";
|
|
529
|
-
return await import(packageName);
|
|
530
|
-
};
|
|
531
|
-
var extractAssistantText = (message) => {
|
|
532
|
-
if (!message || message.role !== "assistant") return "";
|
|
533
|
-
if (typeof message.content === "string") return message.content;
|
|
534
|
-
if (!Array.isArray(message.content)) return "";
|
|
535
|
-
return message.content.filter((part) => part?.type === "text" && typeof part.text === "string").map((part) => part.text).join("");
|
|
536
|
-
};
|
|
537
|
-
var findRequestedModel = (modelRegistry, available, requestedModel) => {
|
|
538
|
-
const [provider, ...modelParts] = requestedModel.split("/");
|
|
539
|
-
const modelId = modelParts.join("/");
|
|
540
|
-
const found = (provider && modelId ? modelRegistry.find(provider, modelId) : void 0) || available.find(
|
|
541
|
-
(entry) => `${entry.provider}/${entry.id}` === requestedModel || entry.id === requestedModel
|
|
542
|
-
);
|
|
543
|
-
if (!found) {
|
|
544
|
-
throw new Error(`Requested Pi model is not available: ${requestedModel}`);
|
|
545
|
-
}
|
|
546
|
-
return found;
|
|
547
|
-
};
|
|
548
|
-
var withTimeout = async (promise, timeoutMs) => {
|
|
549
|
-
let timeout;
|
|
550
|
-
try {
|
|
551
|
-
return await Promise.race([
|
|
552
|
-
promise,
|
|
553
|
-
new Promise((_, reject) => {
|
|
554
|
-
timeout = setTimeout(
|
|
555
|
-
() => reject(new Error(`Pi SDK request timed out after ${timeoutMs}ms`)),
|
|
556
|
-
timeoutMs
|
|
557
|
-
);
|
|
558
|
-
})
|
|
559
|
-
]);
|
|
560
|
-
} finally {
|
|
561
|
-
clearTimeout(timeout);
|
|
562
|
-
}
|
|
563
|
-
};
|
|
564
|
-
var runNodeWithPiSDK = async ({
|
|
565
|
-
model,
|
|
566
|
-
prompt,
|
|
567
|
-
images = [],
|
|
568
|
-
tools = []
|
|
569
|
-
}) => {
|
|
570
|
-
let sdk;
|
|
571
|
-
try {
|
|
572
|
-
sdk = await loadPiSDK();
|
|
573
|
-
} catch (error) {
|
|
574
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
575
|
-
throw new Error(
|
|
576
|
-
[
|
|
577
|
-
"Circuitry could not load the Pi SDK package for execution.",
|
|
578
|
-
"Ensure @earendil-works/pi-coding-agent is installed.",
|
|
579
|
-
message
|
|
580
|
-
].join("\n")
|
|
581
|
-
);
|
|
582
|
-
}
|
|
583
|
-
const { AuthStorage, ModelRegistry, SessionManager, SettingsManager, createAgentSession } = sdk;
|
|
584
|
-
const authStorage = AuthStorage.create();
|
|
585
|
-
const modelRegistry = ModelRegistry.create(authStorage);
|
|
586
|
-
const settingsManager = SettingsManager.create(process.cwd());
|
|
587
|
-
await settingsManager.reload();
|
|
588
|
-
const requestedModel = String(model || "");
|
|
589
|
-
const selectedModel = requestedModel && requestedModel !== "inherit" ? findRequestedModel(modelRegistry, await modelRegistry.getAvailable(), requestedModel) : void 0;
|
|
590
|
-
const { session } = await createAgentSession({
|
|
591
|
-
...selectedModel ? { model: selectedModel } : {},
|
|
592
|
-
sessionManager: SessionManager.inMemory(),
|
|
593
|
-
authStorage,
|
|
594
|
-
modelRegistry,
|
|
595
|
-
settingsManager
|
|
596
|
-
});
|
|
597
|
-
session.setActiveToolsByName(Array.isArray(tools) ? tools.map((tool) => String(tool)) : []);
|
|
598
|
-
let output = "";
|
|
599
|
-
const unsubscribe = session.subscribe((event) => {
|
|
600
|
-
if ((event.type === "message_update" || event.type === "message_end") && event.message?.role === "assistant") {
|
|
601
|
-
output = extractAssistantText(event.message);
|
|
602
|
-
}
|
|
603
|
-
if (event.type === "agent_end" && Array.isArray(event.messages)) {
|
|
604
|
-
const assistantMessage = [...event.messages].reverse().find((message) => message.role === "assistant");
|
|
605
|
-
output = extractAssistantText(assistantMessage);
|
|
606
|
-
}
|
|
607
|
-
});
|
|
608
|
-
try {
|
|
609
|
-
const promptImages = images.filter((image) => image?.mediaType && image?.data).map((image) => ({
|
|
610
|
-
type: "image",
|
|
611
|
-
source: { type: "base64", mediaType: image.mediaType, data: image.data }
|
|
612
|
-
}));
|
|
613
|
-
await withTimeout(session.prompt(prompt, { images: promptImages }), 18e4);
|
|
614
|
-
const assistantMessage = [...session.messages].reverse().find((message) => message.role === "assistant");
|
|
615
|
-
if (assistantMessage?.errorMessage || assistantMessage?.stopReason === "error") {
|
|
616
|
-
throw new Error(assistantMessage.errorMessage || "Pi SDK model returned an error");
|
|
617
|
-
}
|
|
618
|
-
return { output: output.trim() };
|
|
619
|
-
} finally {
|
|
620
|
-
unsubscribe();
|
|
621
|
-
session.dispose();
|
|
622
|
-
}
|
|
623
|
-
};
|
|
624
|
-
var defaultFilename = "graph.circuitry.yaml";
|
|
625
|
-
var resourceInputs = (resource) => {
|
|
626
|
-
if (!("inputs" in resource) || !resource.inputs) return [];
|
|
627
|
-
return Array.isArray(resource.inputs) ? resource.inputs : Object.values(resource.inputs);
|
|
628
|
-
};
|
|
629
|
-
var withResourceInputs = (resource, inputs) => {
|
|
630
|
-
if (!("inputs" in resource) || !resource.inputs) return resource;
|
|
631
|
-
if (Array.isArray(resource.inputs)) return { ...resource, inputs };
|
|
632
|
-
const inputMap = resource.inputs;
|
|
633
|
-
const names = Object.keys(inputMap);
|
|
634
|
-
return { ...resource, inputs: Object.fromEntries(names.map((name, index) => [name, inputs[index] || inputMap[name]])) };
|
|
635
|
-
};
|
|
636
|
-
var selectedImportResources = (imp, resources) => {
|
|
637
|
-
if (!imp.resources || imp.resources === "*") return Object.fromEntries(Object.keys(resources).map((id) => [id, id]));
|
|
638
|
-
if (Array.isArray(imp.resources)) return Object.fromEntries(imp.resources.map((id) => [id, id]));
|
|
639
|
-
return imp.resources;
|
|
640
|
-
};
|
|
641
|
-
var importedId = (localId, prefix) => `${prefix || ""}${localId}`;
|
|
642
|
-
var rewriteResourceInputs = (resource, aliasMap) => {
|
|
643
|
-
const inputs = resourceInputs(resource);
|
|
644
|
-
if (inputs.length === 0) return resource;
|
|
645
|
-
return withResourceInputs(resource, inputs.map((input) => aliasMap[input] || input));
|
|
646
|
-
};
|
|
647
|
-
var resolveCircuitryGraph = async (parsed, graphFile, text, standard, stack = []) => {
|
|
648
|
-
if (stack.includes(graphFile)) {
|
|
649
|
-
throw new Error(`Circuitry graph import cycle: ${[...stack, graphFile].join(" -> ")}`);
|
|
650
|
-
}
|
|
651
|
-
const nextStack = [...stack, graphFile];
|
|
652
|
-
const resources = {};
|
|
653
|
-
const origins = {};
|
|
654
|
-
for (const imp of parsed.imports || []) {
|
|
655
|
-
const linkedFile = path.resolve(path.dirname(graphFile), imp.path);
|
|
656
|
-
const loaded = await loadCircuitryGraphFile(linkedFile, standard, nextStack);
|
|
657
|
-
const available = loaded.graph.resources || {};
|
|
658
|
-
const aliases = selectedImportResources(imp, available);
|
|
659
|
-
const aliasMap = {};
|
|
660
|
-
for (const [sourceId, localId] of Object.entries(aliases)) aliasMap[sourceId] = importedId(localId, imp.prefix);
|
|
661
|
-
for (const [sourceId, localId] of Object.entries(aliases)) {
|
|
662
|
-
const resource = available[sourceId];
|
|
663
|
-
if (!resource) throw new Error(`Imported resource not found: ${sourceId} in ${imp.path}`);
|
|
664
|
-
const nextId = importedId(localId, imp.prefix);
|
|
665
|
-
if (resources[nextId]) throw new Error(`Imported Circuitry resource id collision: ${nextId} from ${imp.path}`);
|
|
666
|
-
resources[nextId] = rewriteResourceInputs(resource, aliasMap);
|
|
667
|
-
origins[nextId] = loaded.origins[sourceId] || linkedFile;
|
|
668
|
-
}
|
|
669
|
-
}
|
|
670
|
-
for (const [id, resource] of Object.entries(parsed.resources || {})) {
|
|
671
|
-
if (resources[id]) throw new Error(`Circuitry resource id collision: ${id} in ${graphFile}`);
|
|
672
|
-
resources[id] = resource;
|
|
673
|
-
origins[id] = graphFile;
|
|
674
|
-
}
|
|
675
|
-
const graph = { ...parsed, circuitry: "0.4", imports: [], inputs: parsed.inputs || parsed.args || {}, resources };
|
|
676
|
-
if (stack.length === 0) {
|
|
677
|
-
const validation = validateCircuitryExecutionGraphWithStandard(graph, standard);
|
|
678
|
-
if (validation.errors.length) {
|
|
679
|
-
throw new Error(`Invalid Circuitry graph:
|
|
680
|
-
${validation.errors.map((e) => e.message).join("\n")}`);
|
|
681
|
-
}
|
|
682
|
-
}
|
|
683
|
-
return { filename: graphFile, text, graph, origins };
|
|
684
|
-
};
|
|
685
|
-
var loadCircuitryGraphFile = async (filename = defaultFilename, standard, stack = []) => {
|
|
686
|
-
const graphFile = path.resolve(process.cwd(), filename);
|
|
687
|
-
const text = await readFile(graphFile, "utf8");
|
|
688
|
-
const parsed = parseCircuitryText(text, standard, { validate: false });
|
|
689
|
-
return resolveCircuitryGraph(parsed, graphFile, text, standard, stack);
|
|
690
|
-
};
|
|
691
|
-
var NodeCircuitryHost = class {
|
|
692
|
-
resolveGraphFile(filename) {
|
|
693
|
-
return path.resolve(
|
|
694
|
-
process.cwd(),
|
|
695
|
-
filename || process.env.CIRCUITRY_GRAPH || defaultFilename
|
|
696
|
-
);
|
|
697
|
-
}
|
|
698
|
-
runFileFor(filename) {
|
|
699
|
-
return `${filename}.run.yaml`;
|
|
700
|
-
}
|
|
701
|
-
async exists(filename) {
|
|
702
|
-
try {
|
|
703
|
-
await access(filename);
|
|
704
|
-
return true;
|
|
705
|
-
} catch {
|
|
706
|
-
return false;
|
|
707
|
-
}
|
|
708
|
-
}
|
|
709
|
-
parseIssue(error, filename) {
|
|
710
|
-
return {
|
|
711
|
-
code: "parse_error",
|
|
712
|
-
message: error instanceof Error ? error.message : String(error),
|
|
713
|
-
path: [filename]
|
|
714
|
-
};
|
|
715
|
-
}
|
|
716
|
-
parseSourceGraph(text, filename, standard) {
|
|
717
|
-
try {
|
|
718
|
-
return { graph: parseCircuitryText(text || "", standard, { validate: false }) };
|
|
719
|
-
} catch (error) {
|
|
720
|
-
return { error: this.parseIssue(error, filename) };
|
|
721
|
-
}
|
|
722
|
-
}
|
|
723
|
-
async parseAndResolveGraph(text, filename, standard) {
|
|
724
|
-
try {
|
|
725
|
-
const graphFile = path.resolve(process.cwd(), filename);
|
|
726
|
-
const source = text || "";
|
|
727
|
-
const parsed = parseCircuitryText(source, standard, { validate: false });
|
|
728
|
-
return { graph: (await resolveCircuitryGraph(parsed, graphFile, source, standard)).graph };
|
|
729
|
-
} catch (error) {
|
|
730
|
-
return { error: this.parseIssue(error, filename) };
|
|
731
|
-
}
|
|
732
|
-
}
|
|
733
|
-
async readGraphFile(filename) {
|
|
734
|
-
const loaded = await loadCircuitryGraphFile(this.resolveGraphFile(filename));
|
|
735
|
-
return { graphFile: loaded.filename, text: loaded.text, graph: loaded.graph, origins: loaded.origins };
|
|
736
|
-
}
|
|
737
|
-
async readGraph(input = {}) {
|
|
738
|
-
const { graphFile, text, graph } = await this.readGraphFile(input.filename);
|
|
739
|
-
const lastRunFile = this.runFileFor(graphFile);
|
|
740
|
-
const lastRun = await this.exists(lastRunFile) ? parseYamlData(await readFile(lastRunFile, "utf8")) : void 0;
|
|
741
|
-
return { filename: graphFile, graph, text, lastRun };
|
|
742
|
-
}
|
|
743
|
-
async validateGraph(input) {
|
|
744
|
-
if (input.graph)
|
|
745
|
-
return validateCircuitryGraphWithStandard(input.graph, input.standard);
|
|
746
|
-
const filename = input.filename || defaultFilename;
|
|
747
|
-
const parsed = this.parseSourceGraph(input.text, filename, input.standard);
|
|
748
|
-
if (!("graph" in parsed)) {
|
|
749
|
-
return {
|
|
750
|
-
ok: false,
|
|
751
|
-
errors: parsed.error ? [parsed.error] : [],
|
|
752
|
-
standard: validateCircuitryGraphWithStandard({}, input.standard).standard
|
|
753
|
-
};
|
|
754
|
-
}
|
|
755
|
-
return validateCircuitryGraphWithStandard(parsed.graph, input.standard);
|
|
756
|
-
}
|
|
757
|
-
async writeGraph(input) {
|
|
758
|
-
const graphFile = this.resolveGraphFile(input.filename);
|
|
759
|
-
const parsed = input.graph ? { graph: input.graph } : this.parseSourceGraph(input.text, input.filename || graphFile, input.standard);
|
|
760
|
-
if (!("graph" in parsed))
|
|
761
|
-
throw new Error(`Invalid Circuitry graph:
|
|
762
|
-
${parsed.error?.message}`);
|
|
763
|
-
const graph = parsed.graph;
|
|
764
|
-
const validation = await this.validateGraph({
|
|
765
|
-
graph,
|
|
766
|
-
standard: input.standard
|
|
767
|
-
});
|
|
768
|
-
if (validation.errors.length)
|
|
769
|
-
throw new Error(
|
|
770
|
-
`Invalid Circuitry graph:
|
|
771
|
-
${validation.errors.map((e) => e.message).join("\n")}`
|
|
772
|
-
);
|
|
773
|
-
await writeFile(
|
|
774
|
-
graphFile,
|
|
775
|
-
stringifyCircuitryText(graph),
|
|
776
|
-
"utf8"
|
|
777
|
-
);
|
|
778
|
-
return {
|
|
779
|
-
filename: graphFile,
|
|
780
|
-
graph,
|
|
781
|
-
mode: "replace"
|
|
782
|
-
};
|
|
783
|
-
}
|
|
784
|
-
async runGraph(input = {}) {
|
|
785
|
-
const source = input.source || (input.text ? "text" : "current");
|
|
786
|
-
const graphFile = this.resolveGraphFile(input.filename);
|
|
787
|
-
const parsed = source === "text" ? await this.parseAndResolveGraph(
|
|
788
|
-
input.text,
|
|
789
|
-
input.filename || graphFile,
|
|
790
|
-
input.standard
|
|
791
|
-
) : { graph: (await this.readGraphFile(input.filename)).graph };
|
|
792
|
-
if (!parsed.graph)
|
|
793
|
-
throw new Error(`Invalid Circuitry graph:
|
|
794
|
-
${parsed.error?.message}`);
|
|
795
|
-
const runnableGraph = applyCircuitryRuntimeInputs(parsed.graph, input.inputs);
|
|
796
|
-
const validation = validateCircuitryExecutionGraphWithStandard(
|
|
797
|
-
runnableGraph,
|
|
798
|
-
input.standard
|
|
799
|
-
);
|
|
800
|
-
if (validation.errors.length)
|
|
801
|
-
throw new Error(
|
|
802
|
-
`Invalid Circuitry graph:
|
|
803
|
-
${validation.errors.map((e) => e.message).join("\n")}`
|
|
804
|
-
);
|
|
805
|
-
const runItems = await runCircuitryGraphExecution({
|
|
806
|
-
graph: runnableGraph,
|
|
807
|
-
selectedNodeId: input.selectedNodeId,
|
|
808
|
-
executeNode: (req) => runNodeWithPiSDK(req)
|
|
809
|
-
});
|
|
810
|
-
const result = { completedAt: (/* @__PURE__ */ new Date()).toISOString(), runItems };
|
|
811
|
-
if (source !== "text")
|
|
812
|
-
await writeFile(
|
|
813
|
-
this.runFileFor(graphFile),
|
|
814
|
-
stringifyYamlData(result),
|
|
815
|
-
"utf8"
|
|
816
|
-
);
|
|
817
|
-
return result;
|
|
818
|
-
}
|
|
819
|
-
};
|
|
820
|
-
export {
|
|
821
|
-
NodeCircuitryHost,
|
|
822
|
-
loadCircuitryGraphFile,
|
|
823
|
-
runNodeWithPiSDK
|
|
824
|
-
};
|