@google-labs/graph-runner
Advanced tools
Comparing version 0.0.2 to 0.1.0
@@ -6,5 +6,6 @@ /** | ||
*/ | ||
export type { Edge, GraphDescriptor, NodeConfiguration, NodeDescriptor, NodeHandler, InputValues, OutputValues, NodeHandlers, NodeTypeIdentifier, KitDescriptor, NodeValue, Capability, } from "./types.js"; | ||
export type { Edge, GraphDescriptor, NodeConfiguration, NodeDescriptor, NodeHandler, InputValues, OutputValues, NodeHandlers, NodeTypeIdentifier, KitDescriptor, NodeValue, Capability, TraversalResult, } from "./types.js"; | ||
export { TraversalMachine } from "./traversal/machine.js"; | ||
export { MachineResult } from "./traversal/result.js"; | ||
export { toMermaid } from "./mermaid.js"; | ||
//# sourceMappingURL=index.d.ts.map |
@@ -7,3 +7,4 @@ /** | ||
export { TraversalMachine } from "./traversal/machine.js"; | ||
export { MachineResult } from "./traversal/result.js"; | ||
export { toMermaid } from "./mermaid.js"; | ||
//# sourceMappingURL=index.js.map |
@@ -6,21 +6,12 @@ /** | ||
*/ | ||
import type { Edge, EdgeMap, GraphDescriptor, InputValues, NodeDescriptor } from "../types.js"; | ||
import type { GraphDescriptor, TraversalResult } from "../types.js"; | ||
import { TraversalMachineIterator } from "./iterator.js"; | ||
import { GraphRepresentation } from "./representation.js"; | ||
import { MachineResult } from "./result.js"; | ||
import { TraversalState } from "./state.js"; | ||
export declare class TraversalMachine implements AsyncIterable<MachineResult>, AsyncIterator<MachineResult> { | ||
#private; | ||
descriptor: GraphDescriptor; | ||
state: TraversalState; | ||
export declare class TraversalMachine implements AsyncIterable<TraversalResult> { | ||
graph: GraphRepresentation; | ||
opportunities: Edge[]; | ||
constructor(descriptor: GraphDescriptor); | ||
[Symbol.asyncIterator](): AsyncIterator<MachineResult>; | ||
get done(): boolean; | ||
get value(): MachineResult; | ||
next(): Promise<IteratorResult<MachineResult>>; | ||
start(): TraversalMachine; | ||
static wire(heads: Edge[], outputEdges: EdgeMap): InputValues; | ||
static computeMissingInputs(heads: Edge[], inputs: InputValues, current: NodeDescriptor): string[]; | ||
previousResult?: TraversalResult; | ||
constructor(descriptor: GraphDescriptor, result?: TraversalResult); | ||
[Symbol.asyncIterator](): AsyncIterator<TraversalResult>; | ||
start(): TraversalMachineIterator; | ||
} | ||
//# sourceMappingURL=machine.d.ts.map |
@@ -6,15 +6,12 @@ /** | ||
*/ | ||
import { TraversalMachineIterator } from "./iterator.js"; | ||
import { GraphRepresentation } from "./representation.js"; | ||
import { MachineResult } from "./result.js"; | ||
import { TraversalState } from "./state.js"; | ||
import { MachineEdgeState } from "./state.js"; | ||
export class TraversalMachine { | ||
descriptor; | ||
state; | ||
graph; | ||
opportunities = []; | ||
#current = MachineResult.empty; | ||
constructor(descriptor) { | ||
this.descriptor = descriptor; | ||
this.state = new TraversalState(); | ||
previousResult; | ||
constructor(descriptor, result) { | ||
this.graph = new GraphRepresentation(descriptor); | ||
this.previousResult = result; | ||
} | ||
@@ -24,43 +21,5 @@ [Symbol.asyncIterator]() { | ||
} | ||
get done() { | ||
return this.#current === MachineResult.empty; | ||
} | ||
get value() { | ||
return this.#current; | ||
} | ||
async next() { | ||
// If this is not the first iteration, let's consume the outputs. | ||
// Only do so when there are no missing inputs. | ||
if (this.#current !== MachineResult.empty && !this.#current.skip) { | ||
const { outputs, newOpportunities, descriptor } = this.#current; | ||
this.opportunities.push(...newOpportunities); | ||
this.state.update(descriptor.id, newOpportunities, outputs); | ||
} | ||
// Now, we're ready to start the next iteration. | ||
// If there are no more opportunities, we're done. | ||
if (this.opportunities.length === 0) { | ||
this.#current = MachineResult.empty; | ||
return this; | ||
} | ||
// Otherwise, let's pop the next opportunity from the queue. | ||
const opportunity = this.opportunities.shift(); | ||
const { heads, nodes, tails } = this.graph; | ||
const toNode = opportunity.to; | ||
const currentDescriptor = nodes.get(toNode); | ||
if (!currentDescriptor) | ||
throw new Error(`No node found for id "${toNode}"`); | ||
const incomingEdges = heads.get(toNode) || []; | ||
const inputs = TraversalMachine.wire(incomingEdges, this.state.getAvailableOutputs(toNode)); | ||
const missingInputs = TraversalMachine.computeMissingInputs(incomingEdges, inputs, currentDescriptor); | ||
const newOpportunities = tails.get(toNode) || []; | ||
// Pour configuration values into inputs. These are effectively like | ||
// constants. | ||
const inputsWithConfiguration = { | ||
...currentDescriptor.configuration, | ||
...inputs, | ||
}; | ||
this.#current = new MachineResult(currentDescriptor, inputsWithConfiguration, missingInputs, newOpportunities); | ||
return this; | ||
} | ||
start() { | ||
if (this.previousResult) | ||
return new TraversalMachineIterator(this.graph, this.previousResult); | ||
const { entries } = this.graph; | ||
@@ -70,43 +29,10 @@ if (entries.length === 0) | ||
// Create fake edges to represent entry points. | ||
this.opportunities = entries.map((entry) => ({ | ||
const opportunities = entries.map((entry) => ({ | ||
from: "$entry", | ||
to: entry, | ||
})); | ||
return this; | ||
const entryResult = new MachineResult({ id: "$empty", type: "$empty" }, {}, [], opportunities, [], new MachineEdgeState()); | ||
return new TraversalMachineIterator(this.graph, entryResult); | ||
} | ||
static wire(heads, outputEdges) { | ||
const result = {}; | ||
heads.forEach((head) => { | ||
const from = head.from; | ||
const outputs = outputEdges.get(from) || {}; | ||
const out = head.out; | ||
if (!out) | ||
return; | ||
if (out === "*") { | ||
Object.assign(result, outputs); | ||
return; | ||
} | ||
const output = outputs[out]; | ||
const input = head.in; | ||
if (!input) | ||
return; | ||
if (output != null && output != undefined) | ||
result[input] = output; | ||
}); | ||
return result; | ||
} | ||
static computeMissingInputs(heads, inputs, current) { | ||
const requiredInputs = [ | ||
...new Set(heads | ||
.filter((edge) => !!edge.in && !edge.optional) | ||
.map((edge) => edge.in || "")), | ||
]; | ||
const inputsWithConfiguration = new Set(); | ||
Object.keys(inputs).forEach((key) => inputsWithConfiguration.add(key)); | ||
if (current.configuration) { | ||
Object.keys(current.configuration).forEach((key) => inputsWithConfiguration.add(key)); | ||
} | ||
return requiredInputs.filter((input) => !inputsWithConfiguration.has(input)); | ||
} | ||
} | ||
//# sourceMappingURL=machine.js.map |
@@ -6,20 +6,19 @@ /** | ||
*/ | ||
import type { Edge, InputValues, NodeDescriptor, OutputValues } from "../types.js"; | ||
export declare class MachineResult { | ||
import type { Edge, EdgeState, InputValues, NodeDescriptor, OutputValues, TraversalResult } from "../types.js"; | ||
export declare class MachineResult implements TraversalResult { | ||
descriptor: NodeDescriptor; | ||
inputs: InputValues; | ||
missingInputs: string[]; | ||
opportunities: Edge[]; | ||
newOpportunities: Edge[]; | ||
state: EdgeState; | ||
outputs?: OutputValues; | ||
constructor(descriptor: NodeDescriptor, inputs: InputValues, missingInputs: string[], newOpportunities: Edge[]); | ||
constructor(descriptor: NodeDescriptor, inputs: InputValues, missingInputs: string[], opportunities: Edge[], newOpportunities: Edge[], state: EdgeState); | ||
/** | ||
* `true` if the machine decided that the node should be skipped, rather than | ||
* run. | ||
* visited. | ||
*/ | ||
get skip(): boolean; | ||
/** | ||
* Sentinel value for when the machine is done. | ||
*/ | ||
static empty: MachineResult; | ||
static fromObject(o: TraversalResult): MachineResult; | ||
} | ||
//# sourceMappingURL=result.d.ts.map |
@@ -6,2 +6,3 @@ /** | ||
*/ | ||
import { MachineEdgeState } from "./state.js"; | ||
export class MachineResult { | ||
@@ -11,13 +12,17 @@ descriptor; | ||
missingInputs; | ||
opportunities; | ||
newOpportunities; | ||
state; | ||
outputs; | ||
constructor(descriptor, inputs, missingInputs, newOpportunities) { | ||
constructor(descriptor, inputs, missingInputs, opportunities, newOpportunities, state) { | ||
this.descriptor = descriptor; | ||
this.inputs = inputs; | ||
this.missingInputs = missingInputs; | ||
this.opportunities = opportunities; | ||
this.newOpportunities = newOpportunities; | ||
this.state = state; | ||
} | ||
/** | ||
* `true` if the machine decided that the node should be skipped, rather than | ||
* run. | ||
* visited. | ||
*/ | ||
@@ -27,7 +32,9 @@ get skip() { | ||
} | ||
/** | ||
* Sentinel value for when the machine is done. | ||
*/ | ||
static empty = new MachineResult({ id: "$empty", type: "$empty" }, {}, [], []); | ||
static fromObject(o) { | ||
const edgeState = new MachineEdgeState(); | ||
edgeState.constants = o.state.constants; | ||
edgeState.state = o.state.state; | ||
return new MachineResult(o.descriptor, o.inputs, o.missingInputs, o.opportunities, o.newOpportunities, edgeState); | ||
} | ||
} | ||
//# sourceMappingURL=result.js.map |
@@ -6,5 +6,7 @@ /** | ||
*/ | ||
import { Edge, EdgeMap, NodeIdentifier, OutputValues } from "../types.js"; | ||
export declare class TraversalState { | ||
import type { Edge, EdgeMap, EdgeState, EdgeStateMap, NodeIdentifier, OutputValues } from "../types.js"; | ||
export declare class MachineEdgeState implements EdgeState { | ||
#private; | ||
state: EdgeStateMap; | ||
constants: EdgeStateMap; | ||
update(node: NodeIdentifier, opportunities: Edge[], outputs?: OutputValues): void; | ||
@@ -11,0 +13,0 @@ getAvailableOutputs(node: NodeIdentifier): EdgeMap; |
@@ -6,5 +6,5 @@ /** | ||
*/ | ||
export class TraversalState { | ||
#state = new Map(); | ||
#constants = new Map(); | ||
export class MachineEdgeState { | ||
state = new Map(); | ||
constants = new Map(); | ||
#splitOutConstants(edges) { | ||
@@ -36,3 +36,3 @@ const constants = []; | ||
// there forever -- or until the edge is traversed again. | ||
this.#state.delete(node); | ||
this.state.delete(node); | ||
if (!outputs) | ||
@@ -42,8 +42,8 @@ outputs = {}; | ||
// 2. Add entries for each opportunity. | ||
this.#addToState(this.#state, state, outputs); | ||
this.#addToState(this.#constants, constants, outputs); | ||
this.#addToState(this.state, state, outputs); | ||
this.#addToState(this.constants, constants, outputs); | ||
} | ||
getAvailableOutputs(node) { | ||
const constantEdges = this.#constants.get(node) || new Map(); | ||
const stateEdges = this.#state.get(node) || new Map(); | ||
const constantEdges = this.constants.get(node) || new Map(); | ||
const stateEdges = this.state.get(node) || new Map(); | ||
const result = new Map([...constantEdges, ...stateEdges]); | ||
@@ -50,0 +50,0 @@ return result; |
@@ -116,3 +116,26 @@ /** | ||
}; | ||
/** | ||
* Additional concept: whether or not an output was consumed by the intended | ||
* input. | ||
* State stores all outputs that have not yet been consumed, organized as | ||
* a map of maps | ||
*/ | ||
export type EdgeStateMap = Map<string, Map<string, OutputValues>>; | ||
export interface EdgeState { | ||
state: EdgeStateMap; | ||
constants: EdgeStateMap; | ||
update(node: NodeIdentifier, opportunities: Edge[], outputs?: OutputValues): void; | ||
getAvailableOutputs(node: NodeIdentifier): EdgeMap; | ||
} | ||
export type EdgeMap = Map<NodeIdentifier, OutputValues>; | ||
export interface TraversalResult { | ||
descriptor: NodeDescriptor; | ||
inputs: InputValues; | ||
missingInputs: string[]; | ||
opportunities: Edge[]; | ||
newOpportunities: Edge[]; | ||
state: EdgeState; | ||
outputs?: OutputValues; | ||
skip: boolean; | ||
} | ||
/** | ||
@@ -119,0 +142,0 @@ * Values that are supplied as inputs to the `NodeHandler`. |
{ | ||
"name": "@google-labs/graph-runner", | ||
"version": "0.0.2", | ||
"version": "0.1.0", | ||
"description": "Reference implementation of generative app composition graph traversal", | ||
@@ -13,2 +13,3 @@ "main": "./dist/src/index.js", | ||
"watch": "FORCE_COLOR=1 tsc --b --watch", | ||
"lint": "FORCE_COLOR=1 eslint . --ext .ts", | ||
"merm": "npm run build && node scripts/make-graphs.js" | ||
@@ -15,0 +16,0 @@ }, |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
45044
38
684