langgraph-api 0.2.8__py3-none-any.whl → 0.2.10__py3-none-any.whl

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.

Potentially problematic release.


This version of langgraph-api might be problematic. Click here for more details.

@@ -1,474 +0,0 @@
1
- import * as ts from "typescript";
2
- import * as vfs from "@typescript/vfs";
3
- import * as path from "node:path";
4
- import dedent from "dedent";
5
- import { buildGenerator } from "./schema/types.mjs";
6
- import { fileURLToPath } from "node:url";
7
-
8
- const __dirname = fileURLToPath(new URL(".", import.meta.url));
9
-
10
- const OVERRIDE_RESOLVE = [
11
- // Override `@langchain/langgraph` or `@langchain/langgraph/prebuilt`,
12
- // but not `@langchain/langgraph-sdk`
13
- new RegExp(`^@langchain\/langgraph(\/.+)?$`),
14
- new RegExp(`^@langchain\/langgraph-checkpoint(\/.+)?$`),
15
- ];
16
-
17
- const compilerOptions = {
18
- noEmit: true,
19
- strict: true,
20
- allowUnusedLabels: true,
21
- };
22
-
23
- export class SubgraphExtractor {
24
- protected program: ts.Program;
25
- protected checker: ts.TypeChecker;
26
- protected sourceFile: ts.SourceFile;
27
- protected inferFile: ts.SourceFile;
28
-
29
- protected anyPregelType: ts.Type;
30
- protected anyGraphType: ts.Type;
31
-
32
- protected strict: boolean;
33
-
34
- constructor(
35
- program: ts.Program,
36
- sourceFile: ts.SourceFile,
37
- inferFile: ts.SourceFile,
38
- options?: { strict?: boolean },
39
- ) {
40
- this.program = program;
41
- this.sourceFile = sourceFile;
42
- this.inferFile = inferFile;
43
-
44
- this.checker = program.getTypeChecker();
45
- this.strict = options?.strict ?? false;
46
-
47
- this.anyPregelType = this.findTypeByName("AnyPregel");
48
- this.anyGraphType = this.findTypeByName("AnyGraph");
49
- }
50
-
51
- private findTypeByName = (needle: string) => {
52
- let result: ts.Type | undefined = undefined;
53
-
54
- const visit = (node: ts.Node) => {
55
- if (ts.isTypeAliasDeclaration(node)) {
56
- const symbol = (node as any).symbol;
57
-
58
- if (symbol != null) {
59
- const name = this.checker
60
- .getFullyQualifiedName(symbol)
61
- .replace(/".*"\./, "");
62
- if (name === needle) result = this.checker.getTypeAtLocation(node);
63
- }
64
- }
65
- if (result == null) ts.forEachChild(node, visit);
66
- };
67
-
68
- ts.forEachChild(this.inferFile, visit);
69
- if (!result) throw new Error(`Failed to find "${needle}" type`);
70
- return result;
71
- };
72
-
73
- private find = (
74
- root: ts.Node,
75
- predicate: (node: ts.Node) => boolean,
76
- ): ts.Node | undefined => {
77
- let result: ts.Node | undefined = undefined;
78
-
79
- const visit = (node: ts.Node) => {
80
- if (predicate(node)) {
81
- result = node;
82
- } else {
83
- ts.forEachChild(node, visit);
84
- }
85
- };
86
-
87
- if (predicate(root)) return root;
88
- ts.forEachChild(root, visit);
89
- return result;
90
- };
91
-
92
- protected findSubgraphs = (
93
- node: ts.Node,
94
- namespace: string[] = [],
95
- ): {
96
- node: string;
97
- namespace: string[];
98
- subgraph: { name: string; node: ts.Node };
99
- }[] => {
100
- const findAllAddNodeCalls = (
101
- acc: {
102
- node: string;
103
- namespace: string[];
104
- subgraph: { name: string; node: ts.Node };
105
- }[],
106
- node: ts.Node,
107
- ) => {
108
- if (ts.isCallExpression(node)) {
109
- const firstChild = node.getChildAt(0);
110
-
111
- if (
112
- ts.isPropertyAccessExpression(firstChild) &&
113
- this.getText(firstChild.name) === "addNode"
114
- ) {
115
- let nodeName: string = "unknown";
116
- let variables: { node: ts.Node; name: string }[] = [];
117
-
118
- const [subgraphNode, callArg] = node.arguments;
119
-
120
- if (subgraphNode && ts.isStringLiteralLike(subgraphNode)) {
121
- nodeName = this.getText(subgraphNode);
122
- if (
123
- (nodeName.startsWith(`"`) && nodeName.endsWith(`"`)) ||
124
- (nodeName.startsWith(`'`) && nodeName.endsWith(`'`))
125
- ) {
126
- nodeName = nodeName.slice(1, -1);
127
- }
128
- }
129
-
130
- if (callArg) {
131
- if (
132
- ts.isFunctionLike(callArg) ||
133
- ts.isCallLikeExpression(callArg)
134
- ) {
135
- variables = this.reduceChildren(
136
- callArg,
137
- this.findSubgraphIdentifiers,
138
- [],
139
- );
140
- } else if (ts.isIdentifier(callArg)) {
141
- variables = this.findSubgraphIdentifiers([], callArg);
142
- }
143
- }
144
-
145
- if (variables.length > 0) {
146
- if (variables.length > 1) {
147
- const targetName = [...namespace, nodeName].join("|");
148
- const errMsg = `Multiple unique subgraph invocations found for "${targetName}"`;
149
- if (this.strict) throw new Error(errMsg);
150
- console.warn(errMsg);
151
- }
152
-
153
- acc.push({
154
- namespace: namespace,
155
- node: nodeName,
156
- subgraph: variables[0],
157
- });
158
- }
159
- }
160
- }
161
-
162
- return acc;
163
- };
164
-
165
- let subgraphs = this.reduceChildren(node, findAllAddNodeCalls, []);
166
-
167
- // TODO: make this more strict, only traverse the flow graph only
168
- // if no `addNode` calls were found
169
- if (!subgraphs.length) {
170
- // internal property, however relied upon by ts-ast-viewer et all
171
- // so that we don't need to traverse the control flow ourselves
172
- // https://github.com/microsoft/TypeScript/pull/58036
173
- type InternalFlowNode = ts.Node & { flowNode?: { node: ts.Node } };
174
- const candidate = this.find(
175
- node,
176
- (node: any) => node && "flowNode" in node && node.flowNode,
177
- ) as InternalFlowNode | undefined;
178
-
179
- if (
180
- candidate?.flowNode &&
181
- this.isGraphOrPregelType(
182
- this.checker.getTypeAtLocation(candidate.flowNode.node),
183
- )
184
- ) {
185
- subgraphs = this.findSubgraphs(candidate.flowNode.node, namespace);
186
- }
187
- }
188
-
189
- // handle recursive behaviour
190
- if (subgraphs.length > 0) {
191
- return [
192
- ...subgraphs,
193
- ...subgraphs.map(({ subgraph, node }) =>
194
- this.findSubgraphs(subgraph.node, [...namespace, node]),
195
- ),
196
- ].flat();
197
- }
198
-
199
- return subgraphs;
200
- };
201
-
202
- protected getSubgraphsVariables = (name: string) => {
203
- const sourceSymbol = this.checker.getSymbolAtLocation(this.sourceFile)!;
204
- const exports = this.checker.getExportsOfModule(sourceSymbol);
205
-
206
- const targetExport = exports.find((item) => item.name === name);
207
- if (!targetExport) throw new Error(`Failed to find export "${name}"`);
208
- const varDecls = (targetExport.declarations ?? []).filter(
209
- ts.isVariableDeclaration,
210
- );
211
-
212
- return varDecls.flatMap((varDecl) => {
213
- if (!varDecl.initializer) return [];
214
- return this.findSubgraphs(varDecl.initializer, [name]);
215
- });
216
- };
217
-
218
- public getAugmentedSourceFile = (
219
- name: string,
220
- ): {
221
- files: [filePath: string, contents: string][];
222
- exports: { typeName: string; valueName: string; graphName: string }[];
223
- } => {
224
- const vars = this.getSubgraphsVariables(name);
225
- type TypeExport = {
226
- typeName: `__langgraph__${string}`;
227
- valueName: string;
228
- graphName: string;
229
- };
230
-
231
- const typeExports: TypeExport[] = [
232
- { typeName: `__langgraph__${name}`, valueName: name, graphName: name },
233
- ];
234
-
235
- for (const { subgraph, node, namespace } of vars) {
236
- typeExports.push({
237
- typeName: `__langgraph__${namespace.join("_")}_${node}`,
238
- valueName: subgraph.name,
239
- graphName: [...namespace, node].join("|"),
240
- });
241
- }
242
-
243
- const sourceFilePath = "__langgraph__source.mts";
244
- const sourceContents = [
245
- this.getText(this.sourceFile),
246
- ...typeExports.map(
247
- ({ typeName, valueName }) =>
248
- `export type ${typeName} = typeof ${valueName}`,
249
- ),
250
- ].join("\n\n");
251
-
252
- const inferFilePath = "__langraph__infer.mts";
253
- const inferContents = [
254
- ...typeExports.map(
255
- ({ typeName }) =>
256
- `import type { ${typeName}} from "./__langgraph__source.mts"`,
257
- ),
258
- this.inferFile.getText(this.inferFile),
259
-
260
- ...typeExports.flatMap(({ typeName }) => {
261
- return [
262
- dedent`
263
- type ${typeName}__reflect = Reflect<${typeName}>;
264
- export type ${typeName}__state = Inspect<${typeName}__reflect["state"]>;
265
- export type ${typeName}__update = Inspect<${typeName}__reflect["update"]>;
266
-
267
- type ${typeName}__builder = BuilderReflect<${typeName}>;
268
- export type ${typeName}__input = Inspect<FilterAny<${typeName}__builder["input"]>>;
269
- export type ${typeName}__output = Inspect<FilterAny<${typeName}__builder["output"]>>;
270
- export type ${typeName}__config = Inspect<FilterAny<${typeName}__builder["config"]>>;
271
- `,
272
- ];
273
- }),
274
- ].join("\n\n");
275
-
276
- return {
277
- files: [
278
- [sourceFilePath, sourceContents],
279
- [inferFilePath, inferContents],
280
- ],
281
- exports: typeExports,
282
- };
283
- };
284
-
285
- protected findSubgraphIdentifiers = (
286
- acc: { node: ts.Node; name: string }[],
287
- node: ts.Node,
288
- ) => {
289
- if (ts.isIdentifier(node)) {
290
- const smb = this.checker.getSymbolAtLocation(node);
291
-
292
- if (
293
- smb?.valueDeclaration &&
294
- ts.isVariableDeclaration(smb.valueDeclaration)
295
- ) {
296
- const target = smb.valueDeclaration;
297
- const targetType = this.checker.getTypeAtLocation(target);
298
-
299
- if (this.isGraphOrPregelType(targetType)) {
300
- acc.push({ name: this.getText(target.name), node: target });
301
- }
302
- }
303
-
304
- if (smb?.declarations) {
305
- const target = smb.declarations.find(ts.isImportSpecifier);
306
- if (target) {
307
- const targetType = this.checker.getTypeAtLocation(target);
308
- if (this.isGraphOrPregelType(targetType)) {
309
- acc.push({ name: this.getText(target.name), node: target });
310
- }
311
- }
312
- }
313
- }
314
-
315
- return acc;
316
- };
317
-
318
- protected isGraphOrPregelType = (type: ts.Type) => {
319
- return (
320
- this.checker.isTypeAssignableTo(type, this.anyPregelType) ||
321
- this.checker.isTypeAssignableTo(type, this.anyGraphType)
322
- );
323
- };
324
-
325
- protected getText(node: ts.Node) {
326
- return node.getText(this.sourceFile);
327
- }
328
-
329
- protected reduceChildren<Acc>(
330
- node: ts.Node,
331
- fn: (acc: Acc, node: ts.Node) => Acc,
332
- initial: Acc,
333
- ): Acc {
334
- let acc = initial;
335
- function it(node: ts.Node) {
336
- acc = fn(acc, node);
337
- // @ts-expect-error
338
- ts.forEachChild(node, it.bind(this));
339
- }
340
-
341
- ts.forEachChild(node, it.bind(this));
342
- return acc;
343
- }
344
-
345
- static extractSchemas(
346
- target:
347
- | string
348
- | {
349
- contents: string;
350
- files?: [fileName: string, contents: string][];
351
- },
352
- name: string,
353
- options?: { strict?: boolean },
354
- ) {
355
- const dirname =
356
- typeof target === "string" ? path.dirname(target) : __dirname;
357
-
358
- // This API is not well made for Windows, ensure that the paths are UNIX slashes
359
- const fsMap = new Map<string, string>();
360
- const system = vfs.createFSBackedSystem(fsMap, dirname, ts);
361
-
362
- // TODO: investigate if we should create a PR in @typescript/vfs
363
- const oldReadFile = system.readFile.bind(system);
364
- system.readFile = (fileName) =>
365
- oldReadFile(fileName) ?? "// Non-existent file";
366
-
367
- const vfsPath = (inputPath: string) => {
368
- if (process.platform === "win32") return inputPath.replace(/\\/g, "/");
369
- return inputPath;
370
- };
371
-
372
- const vfsHost = vfs.createVirtualCompilerHost(system, compilerOptions, ts);
373
- const host = vfsHost.compilerHost;
374
-
375
- const targetPath =
376
- typeof target === "string"
377
- ? target
378
- : path.resolve(dirname, "./__langgraph__target.mts");
379
-
380
- const inferTemplatePath = path.resolve(
381
- __dirname,
382
- "./schema/types.template.mts",
383
- );
384
-
385
- if (typeof target !== "string") {
386
- fsMap.set(vfsPath(targetPath), target.contents);
387
- for (const [name, contents] of target.files ?? []) {
388
- fsMap.set(vfsPath(path.resolve(dirname, name)), contents);
389
- }
390
- }
391
-
392
- const moduleCache = ts.createModuleResolutionCache(dirname, (x) => x);
393
- host.resolveModuleNameLiterals = (
394
- entries,
395
- containingFile,
396
- redirectedReference,
397
- options,
398
- ) =>
399
- entries.flatMap((entry) => {
400
- const specifier = entry.text;
401
-
402
- // Force module resolution to use @langchain/langgraph from the local project
403
- // rather than from API/CLI.
404
- let targetFile = containingFile;
405
- if (OVERRIDE_RESOLVE.some((regex) => regex.test(specifier))) {
406
- // check if we're not already importing from node_modules
407
- if (!containingFile.split(path.sep).includes("node_modules")) {
408
- // Doesn't matter if the file exists, only used to nudge `ts.resolveModuleName`
409
- targetFile = path.resolve(dirname, "__langgraph__resolve.mts");
410
- }
411
- }
412
-
413
- return [
414
- ts.resolveModuleName(
415
- specifier,
416
- targetFile,
417
- options,
418
- host,
419
- moduleCache,
420
- redirectedReference,
421
- ),
422
- ];
423
- });
424
-
425
- const research = ts.createProgram({
426
- rootNames: [inferTemplatePath, targetPath],
427
- options: compilerOptions,
428
- host,
429
- });
430
-
431
- const extractor = new SubgraphExtractor(
432
- research,
433
- research.getSourceFile(targetPath)!,
434
- research.getSourceFile(inferTemplatePath)!,
435
- options,
436
- );
437
-
438
- const { files, exports } = extractor.getAugmentedSourceFile(name);
439
- for (const [name, source] of files) {
440
- system.writeFile(vfsPath(path.resolve(dirname, name)), source);
441
- }
442
-
443
- const extract = ts.createProgram({
444
- rootNames: [path.resolve(dirname, "./__langraph__infer.mts")],
445
- options: compilerOptions,
446
- host,
447
- });
448
-
449
- const schemaGenerator = buildGenerator(extract);
450
- const trySymbol = (schema: typeof schemaGenerator, symbol: string) => {
451
- try {
452
- return schema?.getSchemaForSymbol(symbol) ?? undefined;
453
- } catch (e) {
454
- console.warn(
455
- `Failed to obtain symbol "${symbol}":`,
456
- (e as Error)?.message,
457
- );
458
- }
459
- return undefined;
460
- };
461
-
462
- return Object.fromEntries(
463
- exports.map(({ typeName, graphName }) => [
464
- graphName,
465
- {
466
- input: trySymbol(schemaGenerator, `${typeName}__input`),
467
- output: trySymbol(schemaGenerator, `${typeName}__output`),
468
- state: trySymbol(schemaGenerator, `${typeName}__update`),
469
- config: trySymbol(schemaGenerator, `${typeName}__config`),
470
- },
471
- ]),
472
- );
473
- }
474
- }
@@ -1,12 +0,0 @@
1
- import { tsImport } from "tsx/esm/api";
2
- import { parentPort } from "node:worker_threads";
3
-
4
- parentPort?.on("message", async (payload) => {
5
- const { SubgraphExtractor } = await tsImport("./parser.mts", import.meta.url);
6
- const result = SubgraphExtractor.extractSchemas(
7
- payload.sourceFile,
8
- payload.exportSymbol,
9
- { strict: false },
10
- );
11
- parentPort?.postMessage(result);
12
- });