@langchain/langgraph-api 0.0.34 → 0.0.35
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.
|
@@ -10,8 +10,14 @@ export interface GraphSpec {
|
|
|
10
10
|
sourceFile: string;
|
|
11
11
|
exportSymbol: string;
|
|
12
12
|
}
|
|
13
|
+
type GraphSchemaWithSubgraphs = Record<string, GraphSchema>;
|
|
13
14
|
export declare function getStaticGraphSchema(spec: GraphSpec, options?: {
|
|
14
15
|
mainThread?: boolean;
|
|
15
16
|
timeoutMs?: number;
|
|
16
|
-
}): Promise<
|
|
17
|
+
}): Promise<GraphSchemaWithSubgraphs>;
|
|
18
|
+
export declare function getStaticGraphSchema(specMap: Record<string, GraphSpec>, options?: {
|
|
19
|
+
mainThread?: boolean;
|
|
20
|
+
timeoutMs?: number;
|
|
21
|
+
}): Promise<Record<string, GraphSchemaWithSubgraphs>>;
|
|
17
22
|
export declare function getRuntimeGraphSchema(graph: Pregel<any, any, any, any, any>): Promise<GraphSchema | undefined>;
|
|
23
|
+
export {};
|
|
@@ -1,25 +1,42 @@
|
|
|
1
1
|
import { fileURLToPath } from "node:url";
|
|
2
2
|
import { Worker } from "node:worker_threads";
|
|
3
|
-
|
|
4
|
-
if (
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
3
|
+
const isGraphSpec = (spec) => {
|
|
4
|
+
if (typeof spec !== "object" || spec == null)
|
|
5
|
+
return false;
|
|
6
|
+
if (!("sourceFile" in spec) || typeof spec.sourceFile !== "string")
|
|
7
|
+
return false;
|
|
8
|
+
if (!("exportSymbol" in spec) || typeof spec.exportSymbol !== "string")
|
|
9
|
+
return false;
|
|
10
|
+
return true;
|
|
11
|
+
};
|
|
12
|
+
export async function getStaticGraphSchema(input, options) {
|
|
13
|
+
async function execute(specs) {
|
|
14
|
+
if (options?.mainThread) {
|
|
15
|
+
const { SubgraphExtractor } = await import("./parser.mjs");
|
|
16
|
+
return SubgraphExtractor.extractSchemas(specs, { strict: false });
|
|
17
|
+
}
|
|
18
|
+
return await new Promise((resolve, reject) => {
|
|
19
|
+
const worker = new Worker(fileURLToPath(new URL("./parser.worker.mjs", import.meta.url)), { argv: process.argv.slice(-1) });
|
|
20
|
+
// Set a timeout to reject if the worker takes too long
|
|
21
|
+
const timeoutId = setTimeout(() => {
|
|
22
|
+
worker.terminate();
|
|
23
|
+
reject(new Error("Schema extract worker timed out"));
|
|
24
|
+
}, options?.timeoutMs ?? 30000);
|
|
25
|
+
worker.on("message", (result) => {
|
|
26
|
+
worker.terminate();
|
|
27
|
+
clearTimeout(timeoutId);
|
|
28
|
+
resolve(result);
|
|
29
|
+
});
|
|
30
|
+
worker.on("error", reject);
|
|
31
|
+
worker.postMessage(specs);
|
|
19
32
|
});
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
33
|
+
}
|
|
34
|
+
const specs = isGraphSpec(input) ? [input] : Object.values(input);
|
|
35
|
+
const results = await execute(specs);
|
|
36
|
+
if (isGraphSpec(input)) {
|
|
37
|
+
return results[0];
|
|
38
|
+
}
|
|
39
|
+
return Object.fromEntries(Object.keys(input).map((graphId, idx) => [graphId, results[idx]]));
|
|
23
40
|
}
|
|
24
41
|
export async function getRuntimeGraphSchema(graph) {
|
|
25
42
|
try {
|
|
@@ -35,8 +35,15 @@ export declare class SubgraphExtractor {
|
|
|
35
35
|
node: ts.Node;
|
|
36
36
|
};
|
|
37
37
|
}[];
|
|
38
|
-
getAugmentedSourceFile: (name: string) => {
|
|
39
|
-
|
|
38
|
+
getAugmentedSourceFile: (suffix: string, name: string) => {
|
|
39
|
+
inferFile: {
|
|
40
|
+
fileName: string;
|
|
41
|
+
contents: string;
|
|
42
|
+
};
|
|
43
|
+
sourceFile: {
|
|
44
|
+
fileName: string;
|
|
45
|
+
contents: string;
|
|
46
|
+
};
|
|
40
47
|
exports: {
|
|
41
48
|
typeName: string;
|
|
42
49
|
valueName: string;
|
|
@@ -53,11 +60,15 @@ export declare class SubgraphExtractor {
|
|
|
53
60
|
protected isGraphOrPregelType: (type: ts.Type) => boolean;
|
|
54
61
|
protected getText(node: ts.Node): string;
|
|
55
62
|
protected reduceChildren<Acc>(node: ts.Node, fn: (acc: Acc, node: ts.Node) => Acc, initial: Acc): Acc;
|
|
56
|
-
static extractSchemas(target:
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
63
|
+
static extractSchemas(target: {
|
|
64
|
+
sourceFile: string | {
|
|
65
|
+
name: string;
|
|
66
|
+
contents: string;
|
|
67
|
+
main?: boolean;
|
|
68
|
+
}[];
|
|
69
|
+
exportSymbol: string;
|
|
70
|
+
}[], options?: {
|
|
60
71
|
strict?: boolean;
|
|
61
|
-
}): Record<string, GraphSchema
|
|
72
|
+
}): Record<string, GraphSchema>[];
|
|
62
73
|
}
|
|
63
74
|
export {};
|
|
@@ -11,6 +11,7 @@ const OVERRIDE_RESOLVE = [
|
|
|
11
11
|
new RegExp(`^@langchain\/langgraph(\/.+)?$`),
|
|
12
12
|
new RegExp(`^@langchain\/langgraph-checkpoint(\/.+)?$`),
|
|
13
13
|
];
|
|
14
|
+
const INFER_TEMPLATE_PATH = path.resolve(__dirname, "./schema/types.template.mts");
|
|
14
15
|
const compilerOptions = {
|
|
15
16
|
noEmit: true,
|
|
16
17
|
strict: true,
|
|
@@ -144,7 +145,7 @@ export class SubgraphExtractor {
|
|
|
144
145
|
return this.findSubgraphs(varDecl.initializer, [name]);
|
|
145
146
|
});
|
|
146
147
|
};
|
|
147
|
-
getAugmentedSourceFile = (name) => {
|
|
148
|
+
getAugmentedSourceFile = (suffix, name) => {
|
|
148
149
|
const vars = this.getSubgraphsVariables(name);
|
|
149
150
|
const typeExports = [
|
|
150
151
|
{ typeName: `__langgraph__${name}`, valueName: name, graphName: name },
|
|
@@ -156,14 +157,14 @@ export class SubgraphExtractor {
|
|
|
156
157
|
graphName: [...namespace, node].join("|"),
|
|
157
158
|
});
|
|
158
159
|
}
|
|
159
|
-
const sourceFilePath =
|
|
160
|
+
const sourceFilePath = `__langgraph__source_${suffix}.mts`;
|
|
160
161
|
const sourceContents = [
|
|
161
162
|
this.getText(this.sourceFile),
|
|
162
163
|
...typeExports.map(({ typeName, valueName }) => `export type ${typeName} = typeof ${valueName}`),
|
|
163
164
|
].join("\n\n");
|
|
164
|
-
const inferFilePath =
|
|
165
|
+
const inferFilePath = `__langgraph__infer_${suffix}.mts`;
|
|
165
166
|
const inferContents = [
|
|
166
|
-
...typeExports.map(({ typeName }) => `import type { ${typeName}} from "./
|
|
167
|
+
...typeExports.map(({ typeName }) => `import type { ${typeName}} from "./__langgraph__source_${suffix}.mts"`),
|
|
167
168
|
this.inferFile.getText(this.inferFile),
|
|
168
169
|
...typeExports.flatMap(({ typeName }) => {
|
|
169
170
|
return [
|
|
@@ -181,10 +182,8 @@ export class SubgraphExtractor {
|
|
|
181
182
|
}),
|
|
182
183
|
].join("\n\n");
|
|
183
184
|
return {
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
[inferFilePath, inferContents],
|
|
187
|
-
],
|
|
185
|
+
inferFile: { fileName: inferFilePath, contents: inferContents },
|
|
186
|
+
sourceFile: { fileName: sourceFilePath, contents: sourceContents },
|
|
188
187
|
exports: typeExports,
|
|
189
188
|
};
|
|
190
189
|
};
|
|
@@ -228,11 +227,34 @@ export class SubgraphExtractor {
|
|
|
228
227
|
ts.forEachChild(node, it.bind(this));
|
|
229
228
|
return acc;
|
|
230
229
|
}
|
|
231
|
-
static extractSchemas(target,
|
|
232
|
-
|
|
230
|
+
static extractSchemas(target, options) {
|
|
231
|
+
if (!target.length)
|
|
232
|
+
throw new Error("No graphs found");
|
|
233
|
+
function getCommonPath(a, b) {
|
|
234
|
+
const aSeg = path.normalize(a).split(path.sep);
|
|
235
|
+
const bSeg = path.normalize(b).split(path.sep);
|
|
236
|
+
const maxIter = Math.min(aSeg.length, bSeg.length);
|
|
237
|
+
const result = [];
|
|
238
|
+
for (let i = 0; i < maxIter; ++i) {
|
|
239
|
+
if (aSeg[i] !== bSeg[i])
|
|
240
|
+
break;
|
|
241
|
+
result.push(aSeg[i]);
|
|
242
|
+
}
|
|
243
|
+
return result.join(path.sep);
|
|
244
|
+
}
|
|
245
|
+
const isTestTarget = (check) => {
|
|
246
|
+
return check.every((x) => typeof x.sourceFile === "string");
|
|
247
|
+
};
|
|
248
|
+
const projectDirname = isTestTarget(target)
|
|
249
|
+
? target.reduce((acc, item) => {
|
|
250
|
+
if (!acc)
|
|
251
|
+
return path.dirname(item.sourceFile);
|
|
252
|
+
return getCommonPath(acc, path.dirname(item.sourceFile));
|
|
253
|
+
}, "")
|
|
254
|
+
: __dirname;
|
|
233
255
|
// This API is not well made for Windows, ensure that the paths are UNIX slashes
|
|
234
256
|
const fsMap = new Map();
|
|
235
|
-
const system = vfs.createFSBackedSystem(fsMap,
|
|
257
|
+
const system = vfs.createFSBackedSystem(fsMap, projectDirname, ts);
|
|
236
258
|
// TODO: investigate if we should create a PR in @typescript/vfs
|
|
237
259
|
const oldReadFile = system.readFile.bind(system);
|
|
238
260
|
system.readFile = (fileName) => oldReadFile(fileName) ?? "// Non-existent file";
|
|
@@ -243,17 +265,24 @@ export class SubgraphExtractor {
|
|
|
243
265
|
};
|
|
244
266
|
const vfsHost = vfs.createVirtualCompilerHost(system, compilerOptions, ts);
|
|
245
267
|
const host = vfsHost.compilerHost;
|
|
246
|
-
const
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
268
|
+
const targetPaths = [];
|
|
269
|
+
for (const item of target) {
|
|
270
|
+
if (typeof item.sourceFile === "string") {
|
|
271
|
+
targetPaths.push({ ...item, sourceFile: item.sourceFile });
|
|
272
|
+
}
|
|
273
|
+
else {
|
|
274
|
+
for (const { name, contents, main } of item.sourceFile ?? []) {
|
|
275
|
+
fsMap.set(vfsPath(path.resolve(projectDirname, name)), contents);
|
|
276
|
+
if (main) {
|
|
277
|
+
targetPaths.push({
|
|
278
|
+
...item,
|
|
279
|
+
sourceFile: path.resolve(projectDirname, name),
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
}
|
|
254
283
|
}
|
|
255
284
|
}
|
|
256
|
-
const moduleCache = ts.createModuleResolutionCache(
|
|
285
|
+
const moduleCache = ts.createModuleResolutionCache(projectDirname, (x) => x);
|
|
257
286
|
host.resolveModuleNameLiterals = (entries, containingFile, redirectedReference, options) => entries.flatMap((entry) => {
|
|
258
287
|
const specifier = entry.text;
|
|
259
288
|
// Force module resolution to use @langchain/langgraph from the local project
|
|
@@ -263,7 +292,7 @@ export class SubgraphExtractor {
|
|
|
263
292
|
// check if we're not already importing from node_modules
|
|
264
293
|
if (!containingFile.split(path.sep).includes("node_modules")) {
|
|
265
294
|
// Doesn't matter if the file exists, only used to nudge `ts.resolveModuleName`
|
|
266
|
-
targetFile = path.resolve(
|
|
295
|
+
targetFile = path.resolve(projectDirname, "__langgraph__resolve.mts");
|
|
267
296
|
}
|
|
268
297
|
}
|
|
269
298
|
return [
|
|
@@ -271,17 +300,24 @@ export class SubgraphExtractor {
|
|
|
271
300
|
];
|
|
272
301
|
});
|
|
273
302
|
const research = ts.createProgram({
|
|
274
|
-
rootNames: [
|
|
303
|
+
rootNames: [INFER_TEMPLATE_PATH, ...targetPaths.map((i) => i.sourceFile)],
|
|
275
304
|
options: compilerOptions,
|
|
276
305
|
host,
|
|
277
306
|
});
|
|
278
|
-
const
|
|
279
|
-
const
|
|
280
|
-
|
|
281
|
-
|
|
307
|
+
const researchTargets = [];
|
|
308
|
+
for (const targetPath of targetPaths) {
|
|
309
|
+
const extractor = new SubgraphExtractor(research, research.getSourceFile(targetPath.sourceFile), research.getSourceFile(INFER_TEMPLATE_PATH), options);
|
|
310
|
+
const { sourceFile, inferFile, exports } = extractor.getAugmentedSourceFile(path.basename(targetPath.sourceFile), targetPath.exportSymbol);
|
|
311
|
+
for (const { fileName, contents } of [sourceFile, inferFile]) {
|
|
312
|
+
system.writeFile(vfsPath(path.resolve(projectDirname, fileName)), contents);
|
|
313
|
+
}
|
|
314
|
+
researchTargets.push({
|
|
315
|
+
rootName: path.resolve(projectDirname, inferFile.fileName),
|
|
316
|
+
exports,
|
|
317
|
+
});
|
|
282
318
|
}
|
|
283
319
|
const extract = ts.createProgram({
|
|
284
|
-
rootNames:
|
|
320
|
+
rootNames: researchTargets.map((i) => i.rootName),
|
|
285
321
|
options: compilerOptions,
|
|
286
322
|
host,
|
|
287
323
|
});
|
|
@@ -295,7 +331,7 @@ export class SubgraphExtractor {
|
|
|
295
331
|
}
|
|
296
332
|
return undefined;
|
|
297
333
|
};
|
|
298
|
-
return Object.fromEntries(exports.map(({ typeName, graphName }) => [
|
|
334
|
+
return researchTargets.map(({ exports }) => Object.fromEntries(exports.map(({ typeName, graphName }) => [
|
|
299
335
|
graphName,
|
|
300
336
|
{
|
|
301
337
|
state: trySymbol(schemaGenerator, `${typeName}__update`),
|
|
@@ -303,6 +339,6 @@ export class SubgraphExtractor {
|
|
|
303
339
|
output: trySymbol(schemaGenerator, `${typeName}__output`),
|
|
304
340
|
config: trySymbol(schemaGenerator, `${typeName}__config`),
|
|
305
341
|
},
|
|
306
|
-
]));
|
|
342
|
+
])));
|
|
307
343
|
}
|
|
308
344
|
}
|
|
@@ -2,6 +2,6 @@ import { tsImport } from "tsx/esm/api";
|
|
|
2
2
|
import { parentPort } from "node:worker_threads";
|
|
3
3
|
parentPort?.on("message", async (payload) => {
|
|
4
4
|
const { SubgraphExtractor } = await tsImport("./parser.mjs", import.meta.url);
|
|
5
|
-
const result = SubgraphExtractor.extractSchemas(payload
|
|
5
|
+
const result = SubgraphExtractor.extractSchemas(payload, { strict: false });
|
|
6
6
|
parentPort?.postMessage(result);
|
|
7
7
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@langchain/langgraph-api",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.35",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": "^18.19.0 || >=20.16.0"
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"winston": "^3.17.0",
|
|
54
54
|
"winston-console-format": "^1.0.8",
|
|
55
55
|
"zod": "^3.23.8",
|
|
56
|
-
"@langchain/langgraph-ui": "0.0.
|
|
56
|
+
"@langchain/langgraph-ui": "0.0.35"
|
|
57
57
|
},
|
|
58
58
|
"peerDependencies": {
|
|
59
59
|
"@langchain/core": "^0.3.42",
|