ai-sdk-graph 0.1.2 → 0.1.3
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/LICENSE +21 -0
- package/dist/graph.d.ts +3 -10
- package/dist/index.js +77 -61
- package/dist/types.d.ts +10 -0
- package/package.json +3 -2
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Can Temizyurek
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/graph.d.ts
CHANGED
|
@@ -13,10 +13,8 @@ export declare class Graph<State extends Record<string, unknown>, NodeKeys exten
|
|
|
13
13
|
private readonly emitter;
|
|
14
14
|
private readonly stateManager;
|
|
15
15
|
private readonly onFinish;
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
onFinish?: (state: State) => Promise<void> | void;
|
|
19
|
-
});
|
|
16
|
+
private readonly onStart;
|
|
17
|
+
constructor(options?: GraphSDK.GraphOptions<State, NodeKeys>);
|
|
20
18
|
node<NewKey extends string>(id: NewKey, execute: ({ state, writer, suspense, update }: {
|
|
21
19
|
state: () => Readonly<State>;
|
|
22
20
|
writer: Writer;
|
|
@@ -34,8 +32,6 @@ export declare class Graph<State extends Record<string, unknown>, NodeKeys exten
|
|
|
34
32
|
toMermaid(options?: {
|
|
35
33
|
direction?: 'TB' | 'LR';
|
|
36
34
|
}): string;
|
|
37
|
-
private generateMermaid;
|
|
38
|
-
private extractPossibleTargets;
|
|
39
35
|
execute(runId: string, initialState: State | ((state: State | undefined) => State)): ReadableStream<import("ai").InferUIMessageChunk<import("ai").UIMessage<unknown, import("ai").UIDataTypes, import("ai").UITools>>>;
|
|
40
36
|
executeInternal(runId: string, initialState: State, writer: Writer): Promise<State>;
|
|
41
37
|
private registerBuiltInNodes;
|
|
@@ -69,8 +65,5 @@ export declare class Graph<State extends Record<string, unknown>, NodeKeys exten
|
|
|
69
65
|
private deduplicateNodes;
|
|
70
66
|
private excludeTerminalNodes;
|
|
71
67
|
}
|
|
72
|
-
export declare function graph<State extends Record<string, unknown>, NodeKeys extends string = 'START' | 'END'>(options?:
|
|
73
|
-
storage?: GraphSDK.GraphStorage<State, NodeKeys>;
|
|
74
|
-
onFinish?: (state: State) => Promise<void> | void;
|
|
75
|
-
}): Graph<State, NodeKeys>;
|
|
68
|
+
export declare function graph<State extends Record<string, unknown>, NodeKeys extends string = 'START' | 'END'>(options?: GraphSDK.GraphOptions<State, NodeKeys>): Graph<State, NodeKeys>;
|
|
76
69
|
export {};
|
package/dist/index.js
CHANGED
|
@@ -55,10 +55,12 @@ class Graph {
|
|
|
55
55
|
emitter = new NodeEventEmitter;
|
|
56
56
|
stateManager = new StateManager;
|
|
57
57
|
onFinish;
|
|
58
|
+
onStart;
|
|
58
59
|
constructor(options = {}) {
|
|
59
60
|
this.storage = options.storage ?? new InMemoryStorage;
|
|
60
61
|
this.registerBuiltInNodes();
|
|
61
|
-
this.onFinish = options.onFinish ?? ((
|
|
62
|
+
this.onFinish = options.onFinish ?? (() => {});
|
|
63
|
+
this.onStart = options.onStart ?? (() => {});
|
|
62
64
|
}
|
|
63
65
|
node(id, execute) {
|
|
64
66
|
const node = { id, execute };
|
|
@@ -89,72 +91,20 @@ class Graph {
|
|
|
89
91
|
return this.subgraphRegistry;
|
|
90
92
|
}
|
|
91
93
|
toMermaid(options) {
|
|
92
|
-
const
|
|
93
|
-
return
|
|
94
|
-
}
|
|
95
|
-
generateMermaid(direction, prefix) {
|
|
96
|
-
const lines = [];
|
|
97
|
-
const indent = prefix ? " " : " ";
|
|
98
|
-
if (!prefix) {
|
|
99
|
-
lines.push(`flowchart ${direction}`);
|
|
100
|
-
}
|
|
101
|
-
for (const [nodeId] of this.nodeRegistry) {
|
|
102
|
-
const prefixedId = prefix ? `${prefix}_${nodeId}` : nodeId;
|
|
103
|
-
const subgraphEntry = this.subgraphRegistry.get(nodeId);
|
|
104
|
-
if (subgraphEntry) {
|
|
105
|
-
lines.push(`${indent}subgraph ${prefixedId}[${nodeId}]`);
|
|
106
|
-
lines.push(`${indent} direction ${direction}`);
|
|
107
|
-
const subgraphContent = subgraphEntry.subgraph.generateMermaid(direction, prefixedId);
|
|
108
|
-
const subgraphLines = subgraphContent.split(`
|
|
109
|
-
`);
|
|
110
|
-
lines.push(...subgraphLines.map((line) => `${indent}${line}`));
|
|
111
|
-
lines.push(`${indent}end`);
|
|
112
|
-
} else if (nodeId === "START" || nodeId === "END") {
|
|
113
|
-
lines.push(`${indent}${prefixedId}([${nodeId}])`);
|
|
114
|
-
} else {
|
|
115
|
-
lines.push(`${indent}${prefixedId}[${nodeId}]`);
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
for (const [fromId, edges] of this.edgeRegistry) {
|
|
119
|
-
for (const edge of edges) {
|
|
120
|
-
const prefixedFrom = prefix ? `${prefix}_${fromId}` : fromId;
|
|
121
|
-
if (typeof edge.to === "function") {
|
|
122
|
-
const possibleTargets = this.extractPossibleTargets(edge.to);
|
|
123
|
-
for (const targetId of possibleTargets) {
|
|
124
|
-
const prefixedTo = prefix ? `${prefix}_${targetId}` : targetId;
|
|
125
|
-
lines.push(`${indent}${prefixedFrom} -.-> ${prefixedTo}`);
|
|
126
|
-
}
|
|
127
|
-
} else {
|
|
128
|
-
const prefixedTo = prefix ? `${prefix}_${edge.to}` : edge.to;
|
|
129
|
-
lines.push(`${indent}${prefixedFrom} --> ${prefixedTo}`);
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
return lines.join(`
|
|
134
|
-
`);
|
|
135
|
-
}
|
|
136
|
-
extractPossibleTargets(edgeFn) {
|
|
137
|
-
const fnString = edgeFn.toString();
|
|
138
|
-
const nodeIds = Array.from(this.nodeRegistry.keys());
|
|
139
|
-
return nodeIds.filter((nodeId) => {
|
|
140
|
-
const patterns = [
|
|
141
|
-
`'${nodeId}'`,
|
|
142
|
-
`"${nodeId}"`,
|
|
143
|
-
`\`${nodeId}\``
|
|
144
|
-
];
|
|
145
|
-
return patterns.some((pattern) => fnString.includes(pattern));
|
|
146
|
-
});
|
|
94
|
+
const generator = new MermaidGenerator(this.nodeRegistry, this.edgeRegistry, this.subgraphRegistry);
|
|
95
|
+
return generator.generate(options);
|
|
147
96
|
}
|
|
148
97
|
execute(runId, initialState) {
|
|
149
98
|
let context;
|
|
150
99
|
return createUIMessageStream({
|
|
151
100
|
execute: async ({ writer }) => {
|
|
152
101
|
context = await this.createExecutionContext(runId, initialState, writer);
|
|
102
|
+
await this.onStart({ state: context.state, writer });
|
|
153
103
|
await this.runExecutionLoop(context);
|
|
154
104
|
},
|
|
155
105
|
onFinish: async () => {
|
|
156
106
|
if (context) {
|
|
157
|
-
await this.onFinish(context.state);
|
|
107
|
+
await this.onFinish({ state: context.state });
|
|
158
108
|
}
|
|
159
109
|
}
|
|
160
110
|
});
|
|
@@ -346,10 +296,7 @@ class Graph {
|
|
|
346
296
|
}
|
|
347
297
|
}
|
|
348
298
|
function graph(options = {}) {
|
|
349
|
-
return new Graph(
|
|
350
|
-
storage: options.storage,
|
|
351
|
-
onFinish: options.onFinish
|
|
352
|
-
});
|
|
299
|
+
return new Graph(options);
|
|
353
300
|
}
|
|
354
301
|
|
|
355
302
|
class NodeEventEmitter {
|
|
@@ -381,6 +328,75 @@ class StateManager {
|
|
|
381
328
|
return typeof initialState === "function";
|
|
382
329
|
}
|
|
383
330
|
}
|
|
331
|
+
|
|
332
|
+
class MermaidGenerator {
|
|
333
|
+
nodeRegistry;
|
|
334
|
+
edgeRegistry;
|
|
335
|
+
subgraphRegistry;
|
|
336
|
+
constructor(nodeRegistry, edgeRegistry, subgraphRegistry) {
|
|
337
|
+
this.nodeRegistry = nodeRegistry;
|
|
338
|
+
this.edgeRegistry = edgeRegistry;
|
|
339
|
+
this.subgraphRegistry = subgraphRegistry;
|
|
340
|
+
}
|
|
341
|
+
generate(options) {
|
|
342
|
+
const direction = options?.direction ?? "TB";
|
|
343
|
+
return this.generateMermaid(direction, "");
|
|
344
|
+
}
|
|
345
|
+
generateMermaid(direction, prefix) {
|
|
346
|
+
const lines = [];
|
|
347
|
+
const indent = prefix ? " " : " ";
|
|
348
|
+
if (!prefix) {
|
|
349
|
+
lines.push(`flowchart ${direction}`);
|
|
350
|
+
}
|
|
351
|
+
for (const [nodeId] of this.nodeRegistry) {
|
|
352
|
+
const prefixedId = prefix ? `${prefix}_${nodeId}` : nodeId;
|
|
353
|
+
const subgraphEntry = this.subgraphRegistry.get(nodeId);
|
|
354
|
+
if (subgraphEntry) {
|
|
355
|
+
lines.push(`${indent}subgraph ${prefixedId}[${nodeId}]`);
|
|
356
|
+
lines.push(`${indent} direction ${direction}`);
|
|
357
|
+
const subgraphGenerator = new MermaidGenerator(subgraphEntry.subgraph.nodes, subgraphEntry.subgraph.edges, subgraphEntry.subgraph.subgraphs);
|
|
358
|
+
const subgraphContent = subgraphGenerator.generateMermaid(direction, prefixedId);
|
|
359
|
+
const subgraphLines = subgraphContent.split(`
|
|
360
|
+
`);
|
|
361
|
+
lines.push(...subgraphLines.map((line) => `${indent}${line}`));
|
|
362
|
+
lines.push(`${indent}end`);
|
|
363
|
+
} else if (nodeId === "START" || nodeId === "END") {
|
|
364
|
+
lines.push(`${indent}${prefixedId}([${nodeId}])`);
|
|
365
|
+
} else {
|
|
366
|
+
lines.push(`${indent}${prefixedId}[${nodeId}]`);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
for (const [fromId, edges] of this.edgeRegistry) {
|
|
370
|
+
for (const edge of edges) {
|
|
371
|
+
const prefixedFrom = prefix ? `${prefix}_${fromId}` : fromId;
|
|
372
|
+
if (typeof edge.to === "function") {
|
|
373
|
+
const possibleTargets = this.extractPossibleTargets(edge.to);
|
|
374
|
+
for (const targetId of possibleTargets) {
|
|
375
|
+
const prefixedTo = prefix ? `${prefix}_${targetId}` : targetId;
|
|
376
|
+
lines.push(`${indent}${prefixedFrom} -.-> ${prefixedTo}`);
|
|
377
|
+
}
|
|
378
|
+
} else {
|
|
379
|
+
const prefixedTo = prefix ? `${prefix}_${edge.to}` : edge.to;
|
|
380
|
+
lines.push(`${indent}${prefixedFrom} --> ${prefixedTo}`);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
return lines.join(`
|
|
385
|
+
`);
|
|
386
|
+
}
|
|
387
|
+
extractPossibleTargets(edgeFn) {
|
|
388
|
+
const fnString = edgeFn.toString();
|
|
389
|
+
const nodeIds = Array.from(this.nodeRegistry.keys());
|
|
390
|
+
return nodeIds.filter((nodeId) => {
|
|
391
|
+
const patterns = [
|
|
392
|
+
`'${nodeId}'`,
|
|
393
|
+
`"${nodeId}"`,
|
|
394
|
+
`\`${nodeId}\``
|
|
395
|
+
];
|
|
396
|
+
return patterns.some((pattern) => fnString.includes(pattern));
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
}
|
|
384
400
|
export {
|
|
385
401
|
graph,
|
|
386
402
|
SuspenseError,
|
package/dist/types.d.ts
CHANGED
|
@@ -39,4 +39,14 @@ export declare namespace GraphSDK {
|
|
|
39
39
|
suspendedNodes: Node<State, NodeKeys>[];
|
|
40
40
|
writer: UIMessageStreamWriter;
|
|
41
41
|
}
|
|
42
|
+
interface GraphOptions<State extends Record<string, unknown>, NodeKeys extends string> {
|
|
43
|
+
storage?: GraphStorage<State, NodeKeys>;
|
|
44
|
+
onFinish?: (args: {
|
|
45
|
+
state: State;
|
|
46
|
+
}) => Promise<void> | void;
|
|
47
|
+
onStart?: (args: {
|
|
48
|
+
state: State;
|
|
49
|
+
writer: UIMessageStreamWriter;
|
|
50
|
+
}) => Promise<void> | void;
|
|
51
|
+
}
|
|
42
52
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai-sdk-graph",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Graph-based workflows for the AI SDK",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -25,7 +25,8 @@
|
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
27
|
"@changesets/cli": "^2.29.8",
|
|
28
|
-
"@types/bun": "latest"
|
|
28
|
+
"@types/bun": "latest",
|
|
29
|
+
"prettier": "^3.8.0"
|
|
29
30
|
},
|
|
30
31
|
"peerDependencies": {
|
|
31
32
|
"typescript": "^5"
|