@darkhorseprojects/circuitry 0.3.10 → 0.4.0
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/SPEC.md +96 -0
- package/dist/cli.js +132 -142
- package/dist/graph.d.ts +155 -156
- package/dist/index.js +144 -141
- package/dist/node.js +131 -145
- package/examples/agent.circuitry.yaml +24 -0
- package/examples/imports.circuitry.yaml +13 -0
- package/examples/minimal.circuitry.yaml +20 -0
- package/examples/run-resource.circuitry.yaml +24 -0
- package/package.json +4 -1
- package/schema.json +65 -0
package/dist/index.js
CHANGED
|
@@ -27,166 +27,161 @@ var isNodeElement = (element) => {
|
|
|
27
27
|
|
|
28
28
|
// src/graph.ts
|
|
29
29
|
import YAML from "yaml";
|
|
30
|
-
var CIRCUITRY_SPEC_VERSION = "0.
|
|
31
|
-
var DEFAULT_CIRCUITRY_VALIDATION_RULES = [
|
|
32
|
-
"no-self-loops",
|
|
33
|
-
"no-unknown-edge-endpoints",
|
|
34
|
-
"require-executable-inputs",
|
|
35
|
-
"no-cycles"
|
|
36
|
-
];
|
|
30
|
+
var CIRCUITRY_SPEC_VERSION = "0.4";
|
|
37
31
|
var DEFAULT_CIRCUITRY_VALIDATION_STANDARD = {
|
|
38
32
|
version: CIRCUITRY_SPEC_VERSION,
|
|
39
|
-
requireSpecVersion: true
|
|
40
|
-
rules: DEFAULT_CIRCUITRY_VALIDATION_RULES,
|
|
41
|
-
executableKinds: ["agent", "tool", "output"]
|
|
33
|
+
requireSpecVersion: true
|
|
42
34
|
};
|
|
43
|
-
var
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
35
|
+
var createCircuitryValidationStandard = (standard = {}) => ({
|
|
36
|
+
version: standard.version || DEFAULT_CIRCUITRY_VALIDATION_STANDARD.version,
|
|
37
|
+
requireSpecVersion: standard.requireSpecVersion ?? DEFAULT_CIRCUITRY_VALIDATION_STANDARD.requireSpecVersion
|
|
38
|
+
});
|
|
39
|
+
var getResourceInputs = (resource) => {
|
|
40
|
+
const inputs = resource.inputs;
|
|
41
|
+
if (!inputs) return [];
|
|
42
|
+
if (Array.isArray(inputs)) return inputs.map((from) => ({ from, to: "" }));
|
|
43
|
+
return Object.entries(inputs).map(([name, from]) => ({ from, to: "", name }));
|
|
47
44
|
};
|
|
48
|
-
var
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
if (resource.type !== "text") throw new Error(`arg is not text: ${id}`);
|
|
54
|
-
next.resources[id] = { ...resource, value: runtimeInputToText(value) };
|
|
45
|
+
var resourceInputIds = (resource) => getResourceInputs(resource).map((dependency) => dependency.from);
|
|
46
|
+
var getCircuitryDependencies = (graph) => {
|
|
47
|
+
const dependencies = [];
|
|
48
|
+
for (const [to, resource] of Object.entries(graph.resources || {})) {
|
|
49
|
+
for (const dependency of getResourceInputs(resource)) dependencies.push({ ...dependency, to });
|
|
55
50
|
}
|
|
56
|
-
return
|
|
51
|
+
return dependencies;
|
|
57
52
|
};
|
|
58
|
-
var
|
|
59
|
-
var
|
|
60
|
-
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
53
|
+
var getCircuitryDependents = (graph, resourceId) => getCircuitryDependencies(graph).filter((dependency) => dependency.from === resourceId).map((dependency) => dependency.to);
|
|
54
|
+
var getCircuitryRunSet = (graph, entry = graph.entry) => {
|
|
55
|
+
if (!entry) return [];
|
|
56
|
+
const visited = /* @__PURE__ */ new Set();
|
|
57
|
+
const visit = (id) => {
|
|
58
|
+
if (visited.has(id)) return;
|
|
59
|
+
const resource = graph.resources?.[id];
|
|
60
|
+
if (!resource) return;
|
|
61
|
+
for (const input of resourceInputIds(resource)) visit(input);
|
|
62
|
+
visited.add(id);
|
|
67
63
|
};
|
|
64
|
+
visit(entry);
|
|
65
|
+
return [...visited];
|
|
68
66
|
};
|
|
69
|
-
var
|
|
70
|
-
const
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
67
|
+
var toposortCircuitryResources = (graph, entry) => {
|
|
68
|
+
const ids = entry ? new Set(getCircuitryRunSet(graph, entry)) : new Set(Object.keys(graph.resources || {}));
|
|
69
|
+
const visiting = /* @__PURE__ */ new Set();
|
|
70
|
+
const visited = /* @__PURE__ */ new Set();
|
|
71
|
+
const ordered = [];
|
|
72
|
+
const visit = (id) => {
|
|
73
|
+
if (!ids.has(id) || visited.has(id)) return;
|
|
74
|
+
if (visiting.has(id)) throw new Error(`Circuitry resource dependency cycle at ${id}`);
|
|
75
|
+
visiting.add(id);
|
|
76
|
+
const resource = graph.resources?.[id];
|
|
77
|
+
if (resource) for (const input of resourceInputIds(resource)) visit(input);
|
|
78
|
+
visiting.delete(id);
|
|
79
|
+
visited.add(id);
|
|
80
|
+
ordered.push(id);
|
|
81
|
+
};
|
|
82
|
+
for (const id of ids) visit(id);
|
|
83
|
+
return ordered;
|
|
84
|
+
};
|
|
85
|
+
var runtimeInputToText = (value) => {
|
|
86
|
+
if (typeof value === "string") return value;
|
|
87
|
+
if (value === void 0) return "";
|
|
88
|
+
return YAML.stringify(value).trimEnd();
|
|
89
|
+
};
|
|
90
|
+
var resolveCircuitrySource = (graph) => {
|
|
91
|
+
const inputs = graph.inputs || graph.args || {};
|
|
92
|
+
const resources = { ...graph.resources || {} };
|
|
93
|
+
for (const [id, input] of Object.entries(inputs)) {
|
|
94
|
+
if (!resources[id]) {
|
|
95
|
+
resources[id] = {
|
|
96
|
+
type: "input",
|
|
97
|
+
from: id,
|
|
98
|
+
label: input.label,
|
|
99
|
+
description: input.description,
|
|
100
|
+
mime: input.mime || input.mimeType
|
|
101
|
+
};
|
|
100
102
|
}
|
|
101
103
|
}
|
|
102
|
-
return {
|
|
104
|
+
return { ...graph, circuitry: CIRCUITRY_SPEC_VERSION, imports: graph.imports || [], inputs, resources };
|
|
103
105
|
};
|
|
104
|
-
var
|
|
105
|
-
|
|
106
|
-
|
|
106
|
+
var applyCircuitryRuntimeInputs = (graph, inputs = {}) => {
|
|
107
|
+
const next = resolveCircuitrySource(graph);
|
|
108
|
+
const resources = { ...next.resources || {} };
|
|
109
|
+
for (const [id, value] of Object.entries(inputs)) {
|
|
110
|
+
const resource = resources[id];
|
|
111
|
+
if (!resource) throw new Error(`unknown input: ${id}`);
|
|
112
|
+
if (resource.type === "input") resources[id] = { ...resource, value, from: resource.from || id };
|
|
113
|
+
else resources[id] = { ...resource, value: runtimeInputToText(value) };
|
|
114
|
+
}
|
|
115
|
+
return { ...next, resources };
|
|
107
116
|
};
|
|
108
|
-
var
|
|
109
|
-
const
|
|
117
|
+
var validateCircuitrySource = (graph, standard = {}) => {
|
|
118
|
+
const resolvedStandard = createCircuitryValidationStandard(standard);
|
|
110
119
|
const errors = [];
|
|
111
120
|
const issue = (code, message = code, path) => errors.push({ code, message, ...path ? { path } : {} });
|
|
112
|
-
if (!graph || typeof graph !== "object" || Array.isArray(graph))
|
|
121
|
+
if (!graph || typeof graph !== "object" || Array.isArray(graph)) {
|
|
122
|
+
return { ok: false, errors: [{ code: "invalid_graph", message: "invalid graph" }], standard: resolvedStandard };
|
|
123
|
+
}
|
|
113
124
|
const g = graph;
|
|
114
|
-
if (
|
|
115
|
-
if (
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
125
|
+
if (resolvedStandard.requireSpecVersion && g.circuitry !== resolvedStandard.version) issue("invalid_version", "invalid version", ["circuitry"]);
|
|
126
|
+
if (!g.resources || Object.keys(g.resources).length === 0) issue("missing_resources", "missing resources", ["resources"]);
|
|
127
|
+
if (g.nodes) issue("forbidden_nodes", "forbidden nodes", ["nodes"]);
|
|
128
|
+
if (g.edges) issue("forbidden_edges", "forbidden edges", ["edges"]);
|
|
129
|
+
if (g.agents) issue("forbidden_agents", "forbidden agents", ["agents"]);
|
|
130
|
+
const resolved = resolveCircuitrySource(g);
|
|
131
|
+
const ids = new Set(Object.keys(resolved.resources || {}));
|
|
132
|
+
if (resolved.entry && !ids.has(resolved.entry)) issue("unknown_entry", "unknown entry", ["entry"]);
|
|
133
|
+
for (const [name, target] of Object.entries(resolved.entries || {})) {
|
|
134
|
+
if (!ids.has(target)) issue("unknown_entry", `unknown entry: ${name}`, ["entries", name]);
|
|
123
135
|
}
|
|
124
|
-
const
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
issue("missing_node_id", "missing node id", ["nodes", index, "id"]);
|
|
130
|
-
continue;
|
|
136
|
+
for (const [id, resource] of Object.entries(resolved.resources || {})) {
|
|
137
|
+
if (!resource.type) issue("missing_resource_type", "missing resource type", ["resources", id, "type"]);
|
|
138
|
+
for (const input of resourceInputIds(resource)) {
|
|
139
|
+
if (!ids.has(input)) issue("unknown_resource_input", `unknown resource input: ${input}`, ["resources", id, "inputs"]);
|
|
140
|
+
if (input === id) issue("self_input", "self input", ["resources", id, "inputs"]);
|
|
131
141
|
}
|
|
132
|
-
if (ids.has(node.id)) issue("duplicate_node_id", "duplicate node id", ["nodes", index, "id"]);
|
|
133
|
-
ids.add(node.id);
|
|
134
|
-
if (!node.kind) issue("missing_node_kind", "missing node kind", ["nodes", index, "kind"]);
|
|
135
142
|
}
|
|
136
|
-
const
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
adjacency.set(id, []);
|
|
140
|
-
incoming.set(id, 0);
|
|
143
|
+
for (const [name, output] of Object.entries(resolved.outputs || {})) {
|
|
144
|
+
const root = output.from.split(".")[0];
|
|
145
|
+
if (!ids.has(root)) issue("unknown_output_source", `unknown output source: ${output.from}`, ["outputs", name, "from"]);
|
|
141
146
|
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
}
|
|
147
|
-
if (rules.has("no-self-loops") && edge.from === edge.to) {
|
|
148
|
-
issue("self_loop", "self loop", ["edges", index]);
|
|
149
|
-
continue;
|
|
150
|
-
}
|
|
151
|
-
const ok = ids.has(edge.from) && ids.has(edge.to);
|
|
152
|
-
if (rules.has("no-unknown-edge-endpoints")) {
|
|
153
|
-
if (!ids.has(edge.from)) issue("unknown_edge_source", "unknown edge source", ["edges", index, "from"]);
|
|
154
|
-
if (!ids.has(edge.to)) issue("unknown_edge_target", "unknown edge target", ["edges", index, "to"]);
|
|
155
|
-
}
|
|
156
|
-
if (ok) {
|
|
157
|
-
adjacency.get(edge.from).push(edge.to);
|
|
158
|
-
incoming.set(edge.to, (incoming.get(edge.to) || 0) + 1);
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
if (rules.has("require-executable-inputs")) {
|
|
162
|
-
const executable = new Set(resolved.executableKinds);
|
|
163
|
-
for (const [index, node] of (normalized.nodes || []).entries()) {
|
|
164
|
-
if (executable.has(node.kind) && (incoming.get(node.id) || 0) === 0) issue("missing_executable_input", "missing executable input", ["nodes", index]);
|
|
165
|
-
}
|
|
147
|
+
try {
|
|
148
|
+
toposortCircuitryResources(resolved);
|
|
149
|
+
} catch {
|
|
150
|
+
issue("cycle", "cycle");
|
|
166
151
|
}
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
152
|
+
return { ok: errors.length === 0, errors, standard: resolvedStandard };
|
|
153
|
+
};
|
|
154
|
+
var validateCircuitryGraphWithStandard = validateCircuitrySource;
|
|
155
|
+
var validateCircuitryExecutionGraphWithStandard = validateCircuitrySource;
|
|
156
|
+
var validateCircuitryGraph = (graph, standard = {}) => validateCircuitrySource(graph, standard).errors.map((error) => error.message);
|
|
157
|
+
var inspectCircuitrySource = (graph) => {
|
|
158
|
+
const resolved = resolveCircuitrySource(graph);
|
|
159
|
+
return {
|
|
160
|
+
version: String(resolved.circuitry),
|
|
161
|
+
title: resolved.title,
|
|
162
|
+
entry: resolved.entry,
|
|
163
|
+
entries: resolved.entries || {},
|
|
164
|
+
inputs: Object.keys(resolved.inputs || {}),
|
|
165
|
+
resources: Object.keys(resolved.resources || {}),
|
|
166
|
+
outputs: Object.keys(resolved.outputs || {}),
|
|
167
|
+
dependencies: getCircuitryDependencies(resolved)
|
|
168
|
+
};
|
|
169
|
+
};
|
|
170
|
+
var parseCircuitrySource = (value) => {
|
|
171
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("Circuitry source must be an object.");
|
|
172
|
+
return value;
|
|
173
|
+
};
|
|
174
|
+
var isUriInput = (input) => Boolean(input.uri || ["file", "url", "uri", "image", "mcp"].includes(String(input.type)));
|
|
175
|
+
var normalizeCircuitryGraph = (graph) => {
|
|
176
|
+
const resolved = resolveCircuitrySource(graph);
|
|
177
|
+
const nodes = [];
|
|
178
|
+
for (const [id, resource] of Object.entries(resolved.resources || {})) {
|
|
179
|
+
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 });
|
|
180
|
+
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 } });
|
|
183
181
|
}
|
|
184
|
-
|
|
182
|
+
const edges = getCircuitryDependencies(resolved).map(({ from, to }) => ({ from, to, kind: "dependency" }));
|
|
183
|
+
return { ...resolved, nodes, edges };
|
|
185
184
|
};
|
|
186
|
-
var validateCircuitryGraphWithStandard = (graph, standard = {}) => validateCircuitryGraphInternal(graph, standard, true);
|
|
187
|
-
var validateCircuitryExecutionGraphWithStandard = (graph, standard = {}) => validateCircuitryGraphInternal(graph, standard, false);
|
|
188
|
-
var validateCircuitryGraph = (graph, standard = {}) => validateCircuitryGraphWithStandard(graph, standard).errors.map((error) => error.message);
|
|
189
|
-
var isUriInput = (input) => Boolean(input.uri || ["file", "url", "uri", "image", "mcp"].includes(input.type));
|
|
190
185
|
|
|
191
186
|
// src/yaml.ts
|
|
192
187
|
import YAML2 from "yaml";
|
|
@@ -521,7 +516,7 @@ var runCircuitryGraphExecution = async ({
|
|
|
521
516
|
const executionGraph = buildCircuitryExecutionGraph(graph);
|
|
522
517
|
const runs = [];
|
|
523
518
|
const parallelism = Math.max(1, Math.floor(maxParallelRuns));
|
|
524
|
-
const defaultModel = graph.runtime?.model;
|
|
519
|
+
const defaultModel = typeof graph.runtime?.model === "string" ? graph.runtime.model : void 0;
|
|
525
520
|
const runOne = async (nodeId, outputs2) => {
|
|
526
521
|
const cycle = false;
|
|
527
522
|
const item = executionGraph.nodes.get(nodeId);
|
|
@@ -1003,7 +998,6 @@ export {
|
|
|
1003
998
|
CIRCUITRY_NODE_CUSTOM_TYPE,
|
|
1004
999
|
CIRCUITRY_NODE_KIND,
|
|
1005
1000
|
CIRCUITRY_SPEC_VERSION,
|
|
1006
|
-
DEFAULT_CIRCUITRY_VALIDATION_RULES,
|
|
1007
1001
|
DEFAULT_CIRCUITRY_VALIDATION_STANDARD,
|
|
1008
1002
|
DEFAULT_MAX_PARALLEL_RUNS,
|
|
1009
1003
|
applyAgentPresets,
|
|
@@ -1019,16 +1013,23 @@ export {
|
|
|
1019
1013
|
createCircuitryWriteGraphTool,
|
|
1020
1014
|
createDefaultNodeData,
|
|
1021
1015
|
createUnsupportedRuntimeAdapter,
|
|
1016
|
+
getCircuitryDependencies,
|
|
1017
|
+
getCircuitryDependents,
|
|
1018
|
+
getCircuitryRunSet,
|
|
1019
|
+
getResourceInputs,
|
|
1022
1020
|
hasCircuitryNodeData,
|
|
1023
1021
|
importBundleElements,
|
|
1022
|
+
inspectCircuitrySource,
|
|
1024
1023
|
isNodeElement,
|
|
1025
1024
|
isUriInput,
|
|
1026
1025
|
normalizeCircuitryGraph,
|
|
1027
1026
|
parseAgentPresetMarkdown,
|
|
1028
1027
|
parseBundleText,
|
|
1028
|
+
parseCircuitrySource,
|
|
1029
1029
|
parseCircuitryText,
|
|
1030
1030
|
parseCircuitryYaml,
|
|
1031
1031
|
parseYamlData,
|
|
1032
|
+
resolveCircuitrySource,
|
|
1032
1033
|
runCircuitryGraphExecution,
|
|
1033
1034
|
runDependencySimulation,
|
|
1034
1035
|
runGraphExecution,
|
|
@@ -1037,7 +1038,9 @@ export {
|
|
|
1037
1038
|
stringifyCircuitryText,
|
|
1038
1039
|
stringifyCircuitryYaml,
|
|
1039
1040
|
stringifyYamlData,
|
|
1041
|
+
toposortCircuitryResources,
|
|
1040
1042
|
validateCircuitryExecutionGraphWithStandard,
|
|
1041
1043
|
validateCircuitryGraph,
|
|
1042
|
-
validateCircuitryGraphWithStandard
|
|
1044
|
+
validateCircuitryGraphWithStandard,
|
|
1045
|
+
validateCircuitrySource
|
|
1043
1046
|
};
|