@ghchinoy/litflow 0.2.8 → 0.3.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/CHANGELOG.md +5 -0
- package/dist/breadboard/data/common.d.ts +35 -0
- package/dist/breadboard/engine/loader/capability.d.ts +21 -0
- package/dist/breadboard/engine/loader/loader.d.ts +29 -0
- package/dist/breadboard/engine/loader/resolve-graph-urls.d.ts +16 -0
- package/dist/breadboard/engine/runtime/bubble.d.ts +23 -0
- package/dist/breadboard/engine/runtime/graph-based-node-handler.d.ts +16 -0
- package/dist/breadboard/engine/runtime/handler.d.ts +27 -0
- package/dist/breadboard/engine/runtime/harness/events.d.ts +145 -0
- package/dist/breadboard/engine/runtime/harness/local.d.ts +10 -0
- package/dist/breadboard/engine/runtime/harness/plan-runner.d.ts +25 -0
- package/dist/breadboard/engine/runtime/run/invoke-graph.d.ts +12 -0
- package/dist/breadboard/engine/runtime/run/node-invoker.d.ts +12 -0
- package/dist/breadboard/engine/runtime/run/run-graph.d.ts +12 -0
- package/dist/breadboard/engine/runtime/run.d.ts +29 -0
- package/dist/breadboard/engine/runtime/sandbox/capabilities-manager.d.ts +15 -0
- package/dist/breadboard/engine/runtime/sandbox/file-system-handler-factory.d.ts +15 -0
- package/dist/breadboard/engine/runtime/sandbox/invoke-describer.d.ts +10 -0
- package/dist/breadboard/engine/runtime/static/create-plan.d.ts +14 -0
- package/dist/breadboard/engine/runtime/static/orchestrator.d.ts +72 -0
- package/dist/breadboard/engine/runtime/traversal/index.d.ts +20 -0
- package/dist/breadboard/engine/runtime/traversal/iterator.d.ts +12 -0
- package/dist/breadboard/engine/runtime/traversal/machine.d.ts +14 -0
- package/dist/breadboard/engine/runtime/traversal/representation.d.ts +27 -0
- package/dist/breadboard/engine/runtime/traversal/result.d.ts +24 -0
- package/dist/breadboard/engine/runtime/traversal/state.d.ts +34 -0
- package/dist/breadboard/lit-flow-runner.d.ts +13 -0
- package/dist/breadboard/lit-flow-runner.test.d.ts +1 -0
- package/dist/breadboard/runner.d.ts +13 -0
- package/dist/index.d.ts +2 -0
- package/dist/lit-chiclet.d.ts +9 -0
- package/dist/lit-schema-node.d.ts +13 -0
- package/dist/lit-schema-node.test.d.ts +1 -0
- package/dist/litflow.js +708 -442
- package/dist/litflow.js.map +1 -1
- package/package.json +15 -3
- package/src/breadboard/data/common.ts +450 -0
- package/src/breadboard/data/file-system.ts +54 -0
- package/src/breadboard/data/inline-all-content.ts +126 -0
- package/src/breadboard/data/recent-boards.ts +118 -0
- package/src/breadboard/data/save-outputs-as-file.ts +104 -0
- package/src/breadboard/engine/add-run-module.ts +168 -0
- package/src/breadboard/engine/editor/blank.ts +65 -0
- package/src/breadboard/engine/editor/edge.ts +27 -0
- package/src/breadboard/engine/editor/events.ts +64 -0
- package/src/breadboard/engine/editor/graph.ts +383 -0
- package/src/breadboard/engine/editor/history.ts +98 -0
- package/src/breadboard/engine/editor/index.ts +8 -0
- package/src/breadboard/engine/editor/operations/add-asset.ts +45 -0
- package/src/breadboard/engine/editor/operations/add-edge.ts +142 -0
- package/src/breadboard/engine/editor/operations/add-graph.ts +47 -0
- package/src/breadboard/engine/editor/operations/add-module.ts +64 -0
- package/src/breadboard/engine/editor/operations/add-node.ts +86 -0
- package/src/breadboard/engine/editor/operations/change-asset-metadata.ts +70 -0
- package/src/breadboard/engine/editor/operations/change-configuration.ts +82 -0
- package/src/breadboard/engine/editor/operations/change-edge-metadata.ts +58 -0
- package/src/breadboard/engine/editor/operations/change-edge.ts +111 -0
- package/src/breadboard/engine/editor/operations/change-graph-metadata.ts +52 -0
- package/src/breadboard/engine/editor/operations/change-metadata.ts +92 -0
- package/src/breadboard/engine/editor/operations/change-module.ts +64 -0
- package/src/breadboard/engine/editor/operations/error.ts +21 -0
- package/src/breadboard/engine/editor/operations/remove-asset.ts +48 -0
- package/src/breadboard/engine/editor/operations/remove-edge.ts +89 -0
- package/src/breadboard/engine/editor/operations/remove-graph.ts +49 -0
- package/src/breadboard/engine/editor/operations/remove-integration.ts +54 -0
- package/src/breadboard/engine/editor/operations/remove-module.ts +69 -0
- package/src/breadboard/engine/editor/operations/remove-node.ts +86 -0
- package/src/breadboard/engine/editor/operations/replace-graph.ts +52 -0
- package/src/breadboard/engine/editor/operations/toggle-export.ts +72 -0
- package/src/breadboard/engine/editor/operations/upsert-integration.ts +43 -0
- package/src/breadboard/engine/editor/selection.ts +58 -0
- package/src/breadboard/engine/editor/transforms/configure-sidewire.ts +73 -0
- package/src/breadboard/engine/editor/transforms/isolate-selection.ts +54 -0
- package/src/breadboard/engine/editor/transforms/merge-graph.ts +58 -0
- package/src/breadboard/engine/editor/transforms/move-to-graph.ts +102 -0
- package/src/breadboard/engine/editor/transforms/move-to-new-graph.ts +72 -0
- package/src/breadboard/engine/editor/transforms/sidewire-to-new-graph.ts +82 -0
- package/src/breadboard/engine/file-system/blob-transform.ts +44 -0
- package/src/breadboard/engine/file-system/composed-peristent-backend.ts +140 -0
- package/src/breadboard/engine/file-system/ephemeral-blob-store.ts +46 -0
- package/src/breadboard/engine/file-system/in-memory-blob-store.ts +87 -0
- package/src/breadboard/engine/file-system/index.ts +723 -0
- package/src/breadboard/engine/file-system/partial-persistent-backend.ts +109 -0
- package/src/breadboard/engine/file-system/path.ts +125 -0
- package/src/breadboard/engine/file-system/persistent-file.ts +66 -0
- package/src/breadboard/engine/file-system/readable-stream-file.ts +61 -0
- package/src/breadboard/engine/file-system/stub-file-system.ts +47 -0
- package/src/breadboard/engine/file-system/utils.ts +40 -0
- package/src/breadboard/engine/inspector/graph/bubbled-node.ts +162 -0
- package/src/breadboard/engine/inspector/graph/describe-cache.ts +78 -0
- package/src/breadboard/engine/inspector/graph/describe-type-cache.ts +48 -0
- package/src/breadboard/engine/inspector/graph/edge-cache.ts +118 -0
- package/src/breadboard/engine/inspector/graph/edge.ts +133 -0
- package/src/breadboard/engine/inspector/graph/event.ts +35 -0
- package/src/breadboard/engine/inspector/graph/exports-describer.ts +45 -0
- package/src/breadboard/engine/inspector/graph/graph-cache.ts +54 -0
- package/src/breadboard/engine/inspector/graph/graph-describer-manager.ts +338 -0
- package/src/breadboard/engine/inspector/graph/graph-descriptor-handle.ts +73 -0
- package/src/breadboard/engine/inspector/graph/graph-node-type.ts +111 -0
- package/src/breadboard/engine/inspector/graph/graph-queries.ts +256 -0
- package/src/breadboard/engine/inspector/graph/graph.ts +163 -0
- package/src/breadboard/engine/inspector/graph/inspectable-asset.ts +36 -0
- package/src/breadboard/engine/inspector/graph/kits.ts +208 -0
- package/src/breadboard/engine/inspector/graph/module.ts +69 -0
- package/src/breadboard/engine/inspector/graph/mutable-graph.ts +150 -0
- package/src/breadboard/engine/inspector/graph/node-cache.ts +123 -0
- package/src/breadboard/engine/inspector/graph/node-describer-manager.ts +279 -0
- package/src/breadboard/engine/inspector/graph/node-type-describer-manager.ts +122 -0
- package/src/breadboard/engine/inspector/graph/node.ts +149 -0
- package/src/breadboard/engine/inspector/graph/port-cache.ts +80 -0
- package/src/breadboard/engine/inspector/graph/ports.ts +292 -0
- package/src/breadboard/engine/inspector/graph/schemas.ts +131 -0
- package/src/breadboard/engine/inspector/graph/virtual-node.ts +184 -0
- package/src/breadboard/engine/inspector/graph-store.ts +629 -0
- package/src/breadboard/engine/inspector/index.ts +13 -0
- package/src/breadboard/engine/inspector/utils.ts +20 -0
- package/src/breadboard/engine/loader/capability.ts +184 -0
- package/src/breadboard/engine/loader/index.ts +14 -0
- package/src/breadboard/engine/loader/loader.ts +244 -0
- package/src/breadboard/engine/loader/resolve-graph-urls.ts +111 -0
- package/src/breadboard/engine/runtime/bubble.ts +269 -0
- package/src/breadboard/engine/runtime/graph-based-node-handler.ts +174 -0
- package/src/breadboard/engine/runtime/handler.ts +166 -0
- package/src/breadboard/engine/runtime/harness/diagnostics.ts +22 -0
- package/src/breadboard/engine/runtime/harness/events.ts +217 -0
- package/src/breadboard/engine/runtime/harness/index.ts +14 -0
- package/src/breadboard/engine/runtime/harness/local.ts +48 -0
- package/src/breadboard/engine/runtime/harness/plan-runner.ts +759 -0
- package/src/breadboard/engine/runtime/index.ts +8 -0
- package/src/breadboard/engine/runtime/legacy.ts +28 -0
- package/src/breadboard/engine/runtime/run/invoke-graph.ts +79 -0
- package/src/breadboard/engine/runtime/run/node-invoker.ts +137 -0
- package/src/breadboard/engine/runtime/run/run-graph.ts +186 -0
- package/src/breadboard/engine/runtime/run.ts +111 -0
- package/src/breadboard/engine/runtime/sandbox/capabilities-manager.ts +253 -0
- package/src/breadboard/engine/runtime/sandbox/file-system-handler-factory.ts +53 -0
- package/src/breadboard/engine/runtime/sandbox/invoke-describer.ts +125 -0
- package/src/breadboard/engine/runtime/static/condense.ts +155 -0
- package/src/breadboard/engine/runtime/static/create-plan.ts +134 -0
- package/src/breadboard/engine/runtime/static/nodes-to-subgraph.ts +168 -0
- package/src/breadboard/engine/runtime/static/orchestrator.ts +664 -0
- package/src/breadboard/engine/runtime/static/types.ts +77 -0
- package/src/breadboard/engine/runtime/traversal/index.ts +58 -0
- package/src/breadboard/engine/runtime/traversal/iterator.ts +124 -0
- package/src/breadboard/engine/runtime/traversal/machine.ts +58 -0
- package/src/breadboard/engine/runtime/traversal/representation.ts +115 -0
- package/src/breadboard/engine/runtime/traversal/result.ts +72 -0
- package/src/breadboard/engine/runtime/traversal/state.ts +115 -0
- package/src/breadboard/engine/telemetry.ts +121 -0
- package/src/breadboard/engine/types.ts +32 -0
- package/src/breadboard/lit-flow-runner.test.ts +44 -0
- package/src/breadboard/lit-flow-runner.ts +98 -0
- package/src/breadboard/runner.ts +80 -0
- package/src/index.ts +2 -0
- package/src/lit-chiclet.ts +69 -0
- package/src/lit-flow.ts +17 -7
- package/src/lit-schema-node.test.ts +65 -0
- package/src/lit-schema-node.ts +194 -0
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
/**
|
|
3
|
+
* @license
|
|
4
|
+
* Copyright 2025 Google LLC
|
|
5
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { NodeIdentifier, Outcome } from "@breadboard-ai/types";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Very WIP, sketching out reactive Model + Controller
|
|
12
|
+
* that replaces the current HarnessRunner
|
|
13
|
+
*/
|
|
14
|
+
export type OrchestrationController = {
|
|
15
|
+
/**
|
|
16
|
+
* Run the graph from start to finish or the next breakpoint.
|
|
17
|
+
* Always restarts, resetting current state.
|
|
18
|
+
* Promise resolves when the run completes.
|
|
19
|
+
*/
|
|
20
|
+
run(): Promise<Outcome<void>>;
|
|
21
|
+
/**
|
|
22
|
+
* Run all incomplete stages to finish or the next breakpoint.
|
|
23
|
+
* Promise resolves when the run completes.
|
|
24
|
+
*/
|
|
25
|
+
continue(): Promise<Outcome<void>>;
|
|
26
|
+
/**
|
|
27
|
+
* Invoke the next "ready" node in the orchestration.
|
|
28
|
+
* Promise resolves when the node invocation completes.
|
|
29
|
+
* If the node reports failure, this is not an error that affects outcome.
|
|
30
|
+
*/
|
|
31
|
+
stepThroughNode(): Promise<Outcome<void>>;
|
|
32
|
+
/**
|
|
33
|
+
* Step through the next incomplete stage in the orchestration.
|
|
34
|
+
* Promise resolves when the stage completes.
|
|
35
|
+
*/
|
|
36
|
+
stepThroughStage(): Promise<Outcome<void>>;
|
|
37
|
+
/**
|
|
38
|
+
* Provides a way to manage breakpoints.
|
|
39
|
+
*/
|
|
40
|
+
breakpoints: BreakpointsController;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export type BreakpointsController = {
|
|
44
|
+
/**
|
|
45
|
+
* All current breakpoints.
|
|
46
|
+
*/
|
|
47
|
+
readonly breakpoints: ReadonlyMap<NodeIdentifier, Breakpoint>;
|
|
48
|
+
/**
|
|
49
|
+
* Creates a breakpoint. Can be called multiple times on the same node.
|
|
50
|
+
* @param node - the node at which to set the breakpoint
|
|
51
|
+
*/
|
|
52
|
+
create(node: NodeIdentifier): void;
|
|
53
|
+
/**
|
|
54
|
+
* Removes a breakpoint. Can be called multiple times on the same node.
|
|
55
|
+
* @param node - the node at which to remove the breakpoint
|
|
56
|
+
*/
|
|
57
|
+
delete(node: NodeIdentifier): void;
|
|
58
|
+
/**
|
|
59
|
+
* Clears all breakpoints.
|
|
60
|
+
*/
|
|
61
|
+
clear(): void;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
export type Breakpoint = {
|
|
65
|
+
/**
|
|
66
|
+
* The node at which the breakpoint is set.
|
|
67
|
+
*/
|
|
68
|
+
readonly id: NodeIdentifier;
|
|
69
|
+
/**
|
|
70
|
+
* Disables the breakpoint.
|
|
71
|
+
*/
|
|
72
|
+
disable(): void;
|
|
73
|
+
/**
|
|
74
|
+
* Enables the breakpoint.
|
|
75
|
+
*/
|
|
76
|
+
enable(): void;
|
|
77
|
+
};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
/**
|
|
3
|
+
* @license
|
|
4
|
+
* Copyright 2023 Google LLC
|
|
5
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
Edge,
|
|
10
|
+
InputValues,
|
|
11
|
+
NodeDescriptor,
|
|
12
|
+
NodeIdentifier,
|
|
13
|
+
} from "@breadboard-ai/types";
|
|
14
|
+
|
|
15
|
+
const requiredInputsFromEdges = (edges: Edge[]): string[] => {
|
|
16
|
+
return [
|
|
17
|
+
...new Set(
|
|
18
|
+
edges
|
|
19
|
+
.filter((edge: Edge) => !!edge.in && !edge.optional)
|
|
20
|
+
.map((edge: Edge) => edge.in || "")
|
|
21
|
+
),
|
|
22
|
+
];
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* This class holds important parts of the graph traversal algorithm.
|
|
27
|
+
*/
|
|
28
|
+
export class Traversal {
|
|
29
|
+
/**
|
|
30
|
+
* Computes the missing inputs for a node. A missing input is an input that is
|
|
31
|
+
* required by the node, but is not (yet) available in the current state.
|
|
32
|
+
* @param heads All the edges that point to the node.
|
|
33
|
+
* @param inputs The input values that will be passed to the node
|
|
34
|
+
* @param current The node that is being visited.
|
|
35
|
+
* @returns Array of missing input names.
|
|
36
|
+
*/
|
|
37
|
+
static computeMissingInputs(
|
|
38
|
+
heads: Edge[],
|
|
39
|
+
inputs: InputValues,
|
|
40
|
+
current: NodeDescriptor,
|
|
41
|
+
start?: NodeIdentifier
|
|
42
|
+
): string[] {
|
|
43
|
+
const isStartNode = current.id === start;
|
|
44
|
+
const requiredInputs: string[] = isStartNode
|
|
45
|
+
? []
|
|
46
|
+
: requiredInputsFromEdges(heads);
|
|
47
|
+
const inputsWithConfiguration = new Set();
|
|
48
|
+
Object.keys(inputs).forEach((key) => inputsWithConfiguration.add(key));
|
|
49
|
+
if (current.configuration) {
|
|
50
|
+
Object.keys(current.configuration).forEach((key) =>
|
|
51
|
+
inputsWithConfiguration.add(key)
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
return requiredInputs.filter(
|
|
55
|
+
(input) => !inputsWithConfiguration.has(input)
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
/**
|
|
3
|
+
* @license
|
|
4
|
+
* Copyright 2023 Google LLC
|
|
5
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type {
|
|
9
|
+
Edge,
|
|
10
|
+
ErrorCapability,
|
|
11
|
+
GraphRepresentation,
|
|
12
|
+
TraversalResult,
|
|
13
|
+
} from "@breadboard-ai/types";
|
|
14
|
+
import { Traversal } from "./index.js";
|
|
15
|
+
import { MachineResult } from "./result.js";
|
|
16
|
+
|
|
17
|
+
export class TraversalMachineIterator
|
|
18
|
+
implements AsyncIterator<TraversalResult>
|
|
19
|
+
{
|
|
20
|
+
graph: GraphRepresentation;
|
|
21
|
+
#current: TraversalResult;
|
|
22
|
+
|
|
23
|
+
constructor(graph: GraphRepresentation, result: TraversalResult) {
|
|
24
|
+
this.graph = graph;
|
|
25
|
+
this.#current = result;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async next(): Promise<IteratorResult<TraversalResult>> {
|
|
29
|
+
// If there are no missing inputs, let's consume the outputs
|
|
30
|
+
if (!this.#current.skip) {
|
|
31
|
+
const { inputs, descriptor } = this.#current;
|
|
32
|
+
let { outputs, newOpportunities } = this.#current;
|
|
33
|
+
|
|
34
|
+
// Mark inputs as used, i.e. shift inputs queues.
|
|
35
|
+
this.#current.state.useInputs(descriptor.id, this.#current.inputs);
|
|
36
|
+
|
|
37
|
+
if (outputs && outputs.$error) {
|
|
38
|
+
const $error = (
|
|
39
|
+
typeof outputs.$error === "string"
|
|
40
|
+
? { error: new Error(outputs.$error) }
|
|
41
|
+
: outputs.$error
|
|
42
|
+
) as ErrorCapability;
|
|
43
|
+
outputs.$error = {
|
|
44
|
+
descriptor,
|
|
45
|
+
...($error as object),
|
|
46
|
+
...("metadata" in outputs ? { metadata: outputs.metadata } : {}),
|
|
47
|
+
inputs: { ...inputs, ...$error.inputs },
|
|
48
|
+
};
|
|
49
|
+
newOpportunities = newOpportunities.filter(
|
|
50
|
+
(edge) => edge.out === "$error"
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
outputs ??= {};
|
|
55
|
+
|
|
56
|
+
// Process outputs.
|
|
57
|
+
this.#current.opportunities.push(...newOpportunities);
|
|
58
|
+
this.#current.state.wireOutputs(newOpportunities, outputs);
|
|
59
|
+
|
|
60
|
+
if (outputs.$error) {
|
|
61
|
+
if (newOpportunities.length === 0) {
|
|
62
|
+
// If the node threw an exception and it wasn't routed via $error,
|
|
63
|
+
// throw it again. This will cause the traversal to stop.
|
|
64
|
+
throw new Error(
|
|
65
|
+
"Uncaught exception in node handler. Catch by wiring up the $error output.",
|
|
66
|
+
{
|
|
67
|
+
cause: outputs.$error,
|
|
68
|
+
}
|
|
69
|
+
);
|
|
70
|
+
} else {
|
|
71
|
+
console.warn(
|
|
72
|
+
"Error in node handler, passing to the wired $error output.",
|
|
73
|
+
outputs.$error,
|
|
74
|
+
newOpportunities
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// If there are no more opportunities and none are pending, we're done.
|
|
81
|
+
if (this.#current.opportunities.length === 0)
|
|
82
|
+
return { done: true, value: null };
|
|
83
|
+
|
|
84
|
+
// Now, we're ready to start the next iteration.
|
|
85
|
+
|
|
86
|
+
// Otherwise, let's pop the next opportunity from the queue.
|
|
87
|
+
const opportunity = this.#current.opportunities.shift() as Edge;
|
|
88
|
+
|
|
89
|
+
const { heads, nodes, tails } = this.graph;
|
|
90
|
+
|
|
91
|
+
const toNode = opportunity.to;
|
|
92
|
+
const currentDescriptor = nodes.get(toNode);
|
|
93
|
+
if (!currentDescriptor) throw new Error(`No node found for id "${toNode}"`);
|
|
94
|
+
|
|
95
|
+
const incomingEdges = heads.get(toNode) || [];
|
|
96
|
+
const inputs = this.#current.state.getAvailableInputs(toNode);
|
|
97
|
+
|
|
98
|
+
const missingInputs = Traversal.computeMissingInputs(
|
|
99
|
+
incomingEdges,
|
|
100
|
+
inputs,
|
|
101
|
+
currentDescriptor,
|
|
102
|
+
this.graph.start
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
const newOpportunities = tails.get(toNode) || [];
|
|
106
|
+
// Pour configuration values into inputs. These are effectively like
|
|
107
|
+
// constants.
|
|
108
|
+
const inputsWithConfiguration = {
|
|
109
|
+
...currentDescriptor.configuration,
|
|
110
|
+
...inputs,
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
this.#current = new MachineResult(
|
|
114
|
+
currentDescriptor,
|
|
115
|
+
inputsWithConfiguration,
|
|
116
|
+
missingInputs,
|
|
117
|
+
opportunity,
|
|
118
|
+
this.#current.opportunities,
|
|
119
|
+
newOpportunities,
|
|
120
|
+
this.#current.state
|
|
121
|
+
);
|
|
122
|
+
return { done: false, value: this.#current };
|
|
123
|
+
}
|
|
124
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
/**
|
|
3
|
+
* @license
|
|
4
|
+
* Copyright 2023 Google LLC
|
|
5
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type {
|
|
9
|
+
GraphDescriptor,
|
|
10
|
+
TraversalResult,
|
|
11
|
+
NodeIdentifier,
|
|
12
|
+
GraphRepresentation,
|
|
13
|
+
} from "@breadboard-ai/types";
|
|
14
|
+
import { TraversalMachineIterator } from "./iterator.js";
|
|
15
|
+
import { MachineResult } from "./result.js";
|
|
16
|
+
import { MachineEdgeState } from "./state.js";
|
|
17
|
+
import { GraphRepresentationImpl } from "./representation.js";
|
|
18
|
+
|
|
19
|
+
export class TraversalMachine implements AsyncIterable<TraversalResult> {
|
|
20
|
+
graph: GraphRepresentation;
|
|
21
|
+
previousResult?: TraversalResult;
|
|
22
|
+
|
|
23
|
+
constructor(
|
|
24
|
+
descriptor: GraphDescriptor,
|
|
25
|
+
result?: TraversalResult,
|
|
26
|
+
start?: NodeIdentifier
|
|
27
|
+
) {
|
|
28
|
+
this.graph = new GraphRepresentationImpl(descriptor, start);
|
|
29
|
+
this.previousResult = result;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
[Symbol.asyncIterator](): AsyncIterator<TraversalResult> {
|
|
33
|
+
return this.start();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
start(): TraversalMachineIterator {
|
|
37
|
+
if (this.previousResult)
|
|
38
|
+
return new TraversalMachineIterator(this.graph, this.previousResult);
|
|
39
|
+
|
|
40
|
+
const { entries } = this.graph;
|
|
41
|
+
if (entries.length === 0) throw new Error("No entry node found in graph.");
|
|
42
|
+
// Create fake edges to represent entry points.
|
|
43
|
+
const opportunities = entries.map((entry) => ({
|
|
44
|
+
from: "$entry",
|
|
45
|
+
to: entry,
|
|
46
|
+
}));
|
|
47
|
+
const entryResult = new MachineResult(
|
|
48
|
+
{ id: "$empty", type: "$empty" },
|
|
49
|
+
{},
|
|
50
|
+
[],
|
|
51
|
+
{ from: "$entry", to: entries[0] },
|
|
52
|
+
opportunities,
|
|
53
|
+
[],
|
|
54
|
+
new MachineEdgeState()
|
|
55
|
+
);
|
|
56
|
+
return new TraversalMachineIterator(this.graph, entryResult);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
/**
|
|
3
|
+
* @license
|
|
4
|
+
* Copyright 2023 Google LLC
|
|
5
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
Edge,
|
|
10
|
+
GraphDescriptor,
|
|
11
|
+
GraphRepresentation,
|
|
12
|
+
NodeDescriptor,
|
|
13
|
+
NodeIdentifier,
|
|
14
|
+
} from "@breadboard-ai/types";
|
|
15
|
+
|
|
16
|
+
export class GraphRepresentationImpl implements GraphRepresentation {
|
|
17
|
+
start?: NodeIdentifier;
|
|
18
|
+
/**
|
|
19
|
+
* Tails: a map of all outgoing edges, keyed by node id.
|
|
20
|
+
*/
|
|
21
|
+
tails: Map<NodeIdentifier, Edge[]> = new Map();
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Heads: a map of all incoming edges, keyed by node id.
|
|
25
|
+
*/
|
|
26
|
+
heads: Map<NodeIdentifier, Edge[]> = new Map();
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Nodes: a map of all nodes, keyed by node id.
|
|
30
|
+
*/
|
|
31
|
+
nodes: Map<NodeIdentifier, NodeDescriptor> = new Map();
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Entries: a list of all nodes that have no incoming edges.
|
|
35
|
+
*/
|
|
36
|
+
entries: NodeIdentifier[] = [];
|
|
37
|
+
|
|
38
|
+
#notInHeads(id: NodeIdentifier) {
|
|
39
|
+
return !this.heads.has(id) || this.heads.get(id)?.length === 0;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
#notInTails(id: NodeIdentifier) {
|
|
43
|
+
return !this.tails.has(id) || this.tails.get(id)?.length === 0;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
#findEntries(): NodeIdentifier[] {
|
|
47
|
+
const entries = Array.from(this.nodes.keys()).filter((node) =>
|
|
48
|
+
this.#notInHeads(node)
|
|
49
|
+
);
|
|
50
|
+
// If entries is empty, return empty array
|
|
51
|
+
if (entries.length === 0) return [];
|
|
52
|
+
|
|
53
|
+
// Now, let's separate out all standalone steps and see if maybe we only
|
|
54
|
+
// have standalone nodes.
|
|
55
|
+
const standalone: NodeIdentifier[] = [];
|
|
56
|
+
const connected: NodeIdentifier[] = [];
|
|
57
|
+
let onlyStandalone = true;
|
|
58
|
+
entries.forEach((node) => {
|
|
59
|
+
if (this.#notInTails(node)) {
|
|
60
|
+
standalone.push(node);
|
|
61
|
+
} else {
|
|
62
|
+
onlyStandalone = false;
|
|
63
|
+
connected.push(node);
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// If there are no standalone nodes, return all entries as usual.
|
|
68
|
+
if (standalone.length === 0) return entries;
|
|
69
|
+
|
|
70
|
+
// First, let's see if we can find the starting one
|
|
71
|
+
const start = standalone.find(
|
|
72
|
+
(node) => this.nodes.get(node)!.metadata?.start
|
|
73
|
+
);
|
|
74
|
+
// If there's a standalone start node, let's return.
|
|
75
|
+
if (start) return [start];
|
|
76
|
+
|
|
77
|
+
if (onlyStandalone) {
|
|
78
|
+
// This is the situation when we have a bunch of random nodes in graph
|
|
79
|
+
// and they are not connected, and there's no designated start node.
|
|
80
|
+
|
|
81
|
+
// Just return the first standalone node.
|
|
82
|
+
return [standalone.at(0)!];
|
|
83
|
+
} else {
|
|
84
|
+
// If there are both standalone and connected nodes, we just ignore
|
|
85
|
+
// all standalone nodes.
|
|
86
|
+
return connected;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
constructor(descriptor: GraphDescriptor, start?: NodeIdentifier) {
|
|
91
|
+
if (start) {
|
|
92
|
+
this.start = start;
|
|
93
|
+
}
|
|
94
|
+
this.tails = descriptor.edges.reduce((acc, edge) => {
|
|
95
|
+
const from = edge.from;
|
|
96
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
|
97
|
+
acc.has(from) ? acc.get(from)?.push(edge) : acc.set(from, [edge]);
|
|
98
|
+
return acc;
|
|
99
|
+
}, new Map());
|
|
100
|
+
|
|
101
|
+
this.heads = descriptor.edges.reduce((acc, edge) => {
|
|
102
|
+
const to = edge.to;
|
|
103
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
|
104
|
+
acc.has(to) ? acc.get(to)?.push(edge) : acc.set(to, [edge]);
|
|
105
|
+
return acc;
|
|
106
|
+
}, new Map());
|
|
107
|
+
|
|
108
|
+
this.nodes = descriptor.nodes.reduce((acc, node) => {
|
|
109
|
+
acc.set(node.id, node);
|
|
110
|
+
return acc;
|
|
111
|
+
}, new Map());
|
|
112
|
+
|
|
113
|
+
this.entries = this.#findEntries();
|
|
114
|
+
}
|
|
115
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
/**
|
|
3
|
+
* @license
|
|
4
|
+
* Copyright 2023 Google LLC
|
|
5
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type {
|
|
9
|
+
Edge,
|
|
10
|
+
QueuedNodeValuesState,
|
|
11
|
+
InputValues,
|
|
12
|
+
NodeDescriptor,
|
|
13
|
+
OutputValues,
|
|
14
|
+
TraversalResult,
|
|
15
|
+
} from "@breadboard-ai/types";
|
|
16
|
+
import { MachineEdgeState } from "./state.js";
|
|
17
|
+
|
|
18
|
+
export class MachineResult implements TraversalResult {
|
|
19
|
+
descriptor: NodeDescriptor;
|
|
20
|
+
inputs: InputValues;
|
|
21
|
+
missingInputs: string[];
|
|
22
|
+
current: Edge;
|
|
23
|
+
opportunities: Edge[];
|
|
24
|
+
newOpportunities: Edge[];
|
|
25
|
+
state: QueuedNodeValuesState;
|
|
26
|
+
outputs?: OutputValues;
|
|
27
|
+
partialOutputs?: OutputValues;
|
|
28
|
+
|
|
29
|
+
constructor(
|
|
30
|
+
descriptor: NodeDescriptor,
|
|
31
|
+
inputs: InputValues,
|
|
32
|
+
missingInputs: string[],
|
|
33
|
+
currentOpportunity: Edge,
|
|
34
|
+
opportunities: Edge[],
|
|
35
|
+
newOpportunities: Edge[],
|
|
36
|
+
state: QueuedNodeValuesState,
|
|
37
|
+
partialOutputs?: OutputValues
|
|
38
|
+
) {
|
|
39
|
+
this.descriptor = descriptor;
|
|
40
|
+
this.inputs = inputs;
|
|
41
|
+
this.missingInputs = missingInputs;
|
|
42
|
+
this.current = currentOpportunity;
|
|
43
|
+
this.opportunities = opportunities;
|
|
44
|
+
this.newOpportunities = newOpportunities;
|
|
45
|
+
this.state = state;
|
|
46
|
+
this.partialOutputs = partialOutputs;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* `true` if the machine decided that the node should be skipped, rather than
|
|
51
|
+
* visited.
|
|
52
|
+
*/
|
|
53
|
+
get skip(): boolean {
|
|
54
|
+
return this.missingInputs.length > 0;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
static fromObject(o: TraversalResult): MachineResult {
|
|
58
|
+
const edgeState = new MachineEdgeState();
|
|
59
|
+
edgeState.constants = o.state.constants;
|
|
60
|
+
edgeState.state = o.state.state;
|
|
61
|
+
return new MachineResult(
|
|
62
|
+
o.descriptor,
|
|
63
|
+
o.inputs,
|
|
64
|
+
o.missingInputs,
|
|
65
|
+
o.current,
|
|
66
|
+
o.opportunities,
|
|
67
|
+
o.newOpportunities,
|
|
68
|
+
edgeState,
|
|
69
|
+
o.partialOutputs
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
/**
|
|
3
|
+
* @license
|
|
4
|
+
* Copyright 2023 Google LLC
|
|
5
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type {
|
|
9
|
+
Edge,
|
|
10
|
+
QueuedNodeValuesState,
|
|
11
|
+
NodeIdentifier,
|
|
12
|
+
InputValues,
|
|
13
|
+
OutputValues,
|
|
14
|
+
NodeValue,
|
|
15
|
+
NodeValuesQueuesMap,
|
|
16
|
+
} from "@breadboard-ai/types";
|
|
17
|
+
|
|
18
|
+
export class MachineEdgeState implements QueuedNodeValuesState {
|
|
19
|
+
state: NodeValuesQueuesMap = new Map();
|
|
20
|
+
constants: NodeValuesQueuesMap = new Map();
|
|
21
|
+
|
|
22
|
+
#queueOutput(
|
|
23
|
+
map: NodeValuesQueuesMap,
|
|
24
|
+
node: NodeIdentifier,
|
|
25
|
+
key: string,
|
|
26
|
+
value: NodeValue
|
|
27
|
+
) {
|
|
28
|
+
let queuesMap = map.get(node);
|
|
29
|
+
if (!queuesMap) {
|
|
30
|
+
queuesMap = new Map();
|
|
31
|
+
map.set(node, queuesMap);
|
|
32
|
+
}
|
|
33
|
+
let queue = queuesMap.get(key);
|
|
34
|
+
if (!queue) {
|
|
35
|
+
queue = [];
|
|
36
|
+
queuesMap.set(key, queue);
|
|
37
|
+
}
|
|
38
|
+
queue.push(value);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Processes outputs by wiring them to the destinations according
|
|
43
|
+
* to the supplied edges. Assumes that the outputs were generated by
|
|
44
|
+
* the from node.
|
|
45
|
+
*
|
|
46
|
+
* @param opportunites {Edge[]} Edges to process
|
|
47
|
+
* @param outputs {OutputValues} Outputs to wire
|
|
48
|
+
*/
|
|
49
|
+
wireOutputs(opportunites: Edge[], outputs: OutputValues): void {
|
|
50
|
+
// Verify that all edges are from the same node.
|
|
51
|
+
if (
|
|
52
|
+
opportunites.filter(
|
|
53
|
+
(opportunity) => opportunity.from != opportunites[0].from
|
|
54
|
+
).length !== 0
|
|
55
|
+
)
|
|
56
|
+
throw new Error("All opportunities must be from the same node");
|
|
57
|
+
|
|
58
|
+
opportunites.forEach((opportunity) => {
|
|
59
|
+
const to = opportunity.to;
|
|
60
|
+
const out = opportunity.out;
|
|
61
|
+
const queuesMap = opportunity.constant ? this.constants : this.state;
|
|
62
|
+
if (!out) return;
|
|
63
|
+
if (out === "*") {
|
|
64
|
+
for (const key in outputs) {
|
|
65
|
+
const output = outputs[key];
|
|
66
|
+
if (output != null && output != undefined)
|
|
67
|
+
this.#queueOutput(queuesMap, to, key, output);
|
|
68
|
+
}
|
|
69
|
+
} else if (opportunity.in) {
|
|
70
|
+
const output = outputs[out];
|
|
71
|
+
// TODO: Check and document why we don't allow that
|
|
72
|
+
if (output != null && output != undefined)
|
|
73
|
+
this.#queueOutput(queuesMap, to, opportunity.in, output);
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Returns the available inputs for a given node.
|
|
80
|
+
*
|
|
81
|
+
* @param nodeId {NodeIdentifier} The node to get the inputs for.
|
|
82
|
+
* @returns {InputValues} The available inputs.
|
|
83
|
+
*/
|
|
84
|
+
getAvailableInputs(nodeId: NodeIdentifier): InputValues {
|
|
85
|
+
const result: InputValues = {};
|
|
86
|
+
|
|
87
|
+
for (const queuesMap of [
|
|
88
|
+
this.constants.get(nodeId), // Constants are overwritten by state.
|
|
89
|
+
this.state.get(nodeId),
|
|
90
|
+
]) {
|
|
91
|
+
if (!queuesMap) continue;
|
|
92
|
+
for (const [key, queue] of queuesMap.entries()) {
|
|
93
|
+
if (queue.length === 0) continue;
|
|
94
|
+
result[key] = queue[0];
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return result;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Shifts inputs from the queues. Leaves constants as is.
|
|
102
|
+
*
|
|
103
|
+
* @param nodeId {NodeIdentifier} The node to shift the inputs for.
|
|
104
|
+
* @param inputs {InputValues} The inputs that are used.
|
|
105
|
+
*/
|
|
106
|
+
useInputs(nodeId: NodeIdentifier, inputs: InputValues): void {
|
|
107
|
+
const queuesMap = this.state.get(nodeId);
|
|
108
|
+
if (!queuesMap) return;
|
|
109
|
+
for (const key in inputs) {
|
|
110
|
+
const queue = queuesMap.get(key);
|
|
111
|
+
if (!queue) continue;
|
|
112
|
+
queue.shift();
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|