langgraph-api 0.0.1__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.

Files changed (86) hide show
  1. LICENSE +93 -0
  2. langgraph_api/__init__.py +0 -0
  3. langgraph_api/api/__init__.py +63 -0
  4. langgraph_api/api/assistants.py +326 -0
  5. langgraph_api/api/meta.py +71 -0
  6. langgraph_api/api/openapi.py +32 -0
  7. langgraph_api/api/runs.py +463 -0
  8. langgraph_api/api/store.py +116 -0
  9. langgraph_api/api/threads.py +263 -0
  10. langgraph_api/asyncio.py +201 -0
  11. langgraph_api/auth/__init__.py +0 -0
  12. langgraph_api/auth/langsmith/__init__.py +0 -0
  13. langgraph_api/auth/langsmith/backend.py +67 -0
  14. langgraph_api/auth/langsmith/client.py +145 -0
  15. langgraph_api/auth/middleware.py +41 -0
  16. langgraph_api/auth/noop.py +14 -0
  17. langgraph_api/cli.py +209 -0
  18. langgraph_api/config.py +70 -0
  19. langgraph_api/cron_scheduler.py +60 -0
  20. langgraph_api/errors.py +52 -0
  21. langgraph_api/graph.py +314 -0
  22. langgraph_api/http.py +168 -0
  23. langgraph_api/http_logger.py +89 -0
  24. langgraph_api/js/.gitignore +2 -0
  25. langgraph_api/js/build.mts +49 -0
  26. langgraph_api/js/client.mts +849 -0
  27. langgraph_api/js/global.d.ts +6 -0
  28. langgraph_api/js/package.json +33 -0
  29. langgraph_api/js/remote.py +673 -0
  30. langgraph_api/js/server_sent_events.py +126 -0
  31. langgraph_api/js/src/graph.mts +88 -0
  32. langgraph_api/js/src/hooks.mjs +12 -0
  33. langgraph_api/js/src/parser/parser.mts +443 -0
  34. langgraph_api/js/src/parser/parser.worker.mjs +12 -0
  35. langgraph_api/js/src/schema/types.mts +2136 -0
  36. langgraph_api/js/src/schema/types.template.mts +74 -0
  37. langgraph_api/js/src/utils/importMap.mts +85 -0
  38. langgraph_api/js/src/utils/pythonSchemas.mts +28 -0
  39. langgraph_api/js/src/utils/serde.mts +21 -0
  40. langgraph_api/js/tests/api.test.mts +1566 -0
  41. langgraph_api/js/tests/compose-postgres.yml +56 -0
  42. langgraph_api/js/tests/graphs/.gitignore +1 -0
  43. langgraph_api/js/tests/graphs/agent.mts +127 -0
  44. langgraph_api/js/tests/graphs/error.mts +17 -0
  45. langgraph_api/js/tests/graphs/langgraph.json +8 -0
  46. langgraph_api/js/tests/graphs/nested.mts +44 -0
  47. langgraph_api/js/tests/graphs/package.json +7 -0
  48. langgraph_api/js/tests/graphs/weather.mts +57 -0
  49. langgraph_api/js/tests/graphs/yarn.lock +159 -0
  50. langgraph_api/js/tests/parser.test.mts +870 -0
  51. langgraph_api/js/tests/utils.mts +17 -0
  52. langgraph_api/js/yarn.lock +1340 -0
  53. langgraph_api/lifespan.py +41 -0
  54. langgraph_api/logging.py +121 -0
  55. langgraph_api/metadata.py +101 -0
  56. langgraph_api/models/__init__.py +0 -0
  57. langgraph_api/models/run.py +229 -0
  58. langgraph_api/patch.py +42 -0
  59. langgraph_api/queue.py +245 -0
  60. langgraph_api/route.py +118 -0
  61. langgraph_api/schema.py +190 -0
  62. langgraph_api/serde.py +124 -0
  63. langgraph_api/server.py +48 -0
  64. langgraph_api/sse.py +118 -0
  65. langgraph_api/state.py +67 -0
  66. langgraph_api/stream.py +289 -0
  67. langgraph_api/utils.py +60 -0
  68. langgraph_api/validation.py +141 -0
  69. langgraph_api-0.0.1.dist-info/LICENSE +93 -0
  70. langgraph_api-0.0.1.dist-info/METADATA +26 -0
  71. langgraph_api-0.0.1.dist-info/RECORD +86 -0
  72. langgraph_api-0.0.1.dist-info/WHEEL +4 -0
  73. langgraph_api-0.0.1.dist-info/entry_points.txt +3 -0
  74. langgraph_license/__init__.py +0 -0
  75. langgraph_license/middleware.py +21 -0
  76. langgraph_license/validation.py +11 -0
  77. langgraph_storage/__init__.py +0 -0
  78. langgraph_storage/checkpoint.py +94 -0
  79. langgraph_storage/database.py +190 -0
  80. langgraph_storage/ops.py +1523 -0
  81. langgraph_storage/queue.py +108 -0
  82. langgraph_storage/retry.py +27 -0
  83. langgraph_storage/store.py +28 -0
  84. langgraph_storage/ttl_dict.py +54 -0
  85. logging.json +22 -0
  86. openapi.json +4304 -0
@@ -0,0 +1,870 @@
1
+ import { describe, expect, test } from "vitest";
2
+
3
+ import { SubgraphExtractor } from "../src/parser/parser.mjs";
4
+ import dedent from "dedent";
5
+
6
+ describe.concurrent("graph factories", () => {
7
+ const common = dedent`
8
+ import { HumanMessage } from "@langchain/core/messages";
9
+ import { MessagesAnnotation, StateGraph } from "@langchain/langgraph";
10
+
11
+ const builder = new StateGraph(MessagesAnnotation)
12
+ .addNode("parent", () => {
13
+ return { messages: [new HumanMessage("parent")] };
14
+ })
15
+ .addNode("child", async (state) => {
16
+ return { messages: [new HumanMessage("child")] };
17
+ })
18
+ .addEdge("__start__", "parent")
19
+ .addEdge("parent", "child")
20
+ .addEdge("child", "__end__");
21
+ `;
22
+
23
+ const MessagesSchema = {
24
+ type: "object",
25
+ properties: {
26
+ messages: {
27
+ type: "array",
28
+ items: { $ref: "#/definitions/BaseMessage" },
29
+ },
30
+ },
31
+ definitions: {
32
+ BaseMessage: {
33
+ oneOf: expect.arrayContaining([
34
+ { $ref: "#/definitions/BaseMessageChunk" },
35
+ ]),
36
+ },
37
+ },
38
+ $schema: "http://json-schema.org/draft-07/schema#",
39
+ };
40
+
41
+ const ConfigSchema = {
42
+ type: "object",
43
+ $schema: "http://json-schema.org/draft-07/schema#",
44
+ };
45
+
46
+ test.concurrent.for([
47
+ ["builder.compile()"], // CompiledGraph
48
+ ["() => builder.compile()"], // () => CompiledGraph
49
+ ["builder"], // Graph
50
+ ["() => builder"], // () => Graph
51
+
52
+ ["(async () => builder)()"], // Promise<CompiledGraph>
53
+ ["async () => builder.compile()"], // () => Promise<CompiledGraph>
54
+ ["(async () => builder)()"], // Promise<Graph>
55
+ ["async () => builder"], // () => Promise<Graph>
56
+ ])("%s", ([prop]) => {
57
+ const schemas = SubgraphExtractor.extractSchemas(
58
+ { contents: `${common}\n\nexport const graph = ${prop};` },
59
+ "graph"
60
+ );
61
+
62
+ expect(schemas.graph.input).toMatchObject(MessagesSchema);
63
+ expect(schemas.graph.output).toMatchObject(MessagesSchema);
64
+ expect(schemas.graph.state).toMatchObject(MessagesSchema);
65
+ expect(schemas.graph.config).toEqual(ConfigSchema);
66
+ });
67
+ });
68
+
69
+ describe.concurrent("subgraphs", () => {
70
+ test.concurrent.for([
71
+ ["subgraph"],
72
+ ["(state) => subgraph.invoke(state)"],
73
+ ["async (state) => await subgraph.invoke(state)"],
74
+ ])(`basic: %s`, ([nodeDef]) => {
75
+ const schemas = SubgraphExtractor.extractSchemas(
76
+ {
77
+ contents: dedent`
78
+ import { HumanMessage } from "@langchain/core/messages";
79
+ import {
80
+ Annotation,
81
+ MessagesAnnotation,
82
+ StateGraph,
83
+ } from "@langchain/langgraph";
84
+
85
+ const ParentSchema = MessagesAnnotation;
86
+
87
+ const SubgraphSchema = Annotation.Root({
88
+ ...MessagesAnnotation.spec,
89
+ kind: Annotation<"weather" | "other">,
90
+ });
91
+
92
+ const subgraph = new StateGraph(SubgraphSchema)
93
+ .addNode("child", () => {
94
+ return { messages: [new HumanMessage("Hello from child")] };
95
+ })
96
+ .addEdge("__start__", "child")
97
+ .compile();
98
+
99
+ export const graph = new StateGraph(ParentSchema)
100
+ .addNode("parent", () => {
101
+ return { messages: [new HumanMessage("Hello from child")] };
102
+ })
103
+ .addNode("child", ${nodeDef})
104
+ .addEdge("__start__", "parent")
105
+ .addEdge("parent", "child")
106
+ .addEdge("child", "__end__")
107
+ .compile();
108
+ `,
109
+ },
110
+ "graph"
111
+ );
112
+ expect(schemas["graph|child"].input).toMatchObject({
113
+ type: "object",
114
+ properties: {
115
+ kind: {
116
+ type: "string",
117
+ enum: expect.arrayContaining(["weather", "other"]),
118
+ },
119
+ messages: {
120
+ type: "array",
121
+ items: { $ref: "#/definitions/BaseMessage" },
122
+ },
123
+ },
124
+ definitions: {
125
+ BaseMessage: {
126
+ oneOf: expect.arrayContaining([
127
+ { $ref: "#/definitions/BaseMessageChunk" },
128
+ ]),
129
+ },
130
+ },
131
+ $schema: "http://json-schema.org/draft-07/schema#",
132
+ });
133
+
134
+ expect(schemas["graph|child"].output).toMatchObject({
135
+ type: "object",
136
+ properties: {
137
+ kind: {
138
+ type: "string",
139
+ enum: expect.arrayContaining(["weather", "other"]),
140
+ },
141
+ messages: {
142
+ type: "array",
143
+ items: { $ref: "#/definitions/BaseMessage" },
144
+ },
145
+ },
146
+ definitions: {
147
+ BaseMessage: {
148
+ oneOf: expect.arrayContaining([
149
+ { $ref: "#/definitions/BaseMessageChunk" },
150
+ ]),
151
+ },
152
+ },
153
+ $schema: "http://json-schema.org/draft-07/schema#",
154
+ });
155
+
156
+ expect(schemas["graph|child"].state).toMatchObject({
157
+ type: "object",
158
+ properties: {
159
+ kind: {
160
+ type: "string",
161
+ enum: expect.arrayContaining(["weather", "other"]),
162
+ },
163
+ messages: {
164
+ type: "array",
165
+ items: { $ref: "#/definitions/BaseMessage" },
166
+ },
167
+ },
168
+ definitions: {
169
+ BaseMessage: {
170
+ oneOf: expect.arrayContaining([
171
+ { $ref: "#/definitions/BaseMessageChunk" },
172
+ ]),
173
+ },
174
+ },
175
+ $schema: "http://json-schema.org/draft-07/schema#",
176
+ });
177
+
178
+ expect(schemas["graph|child"].config).toMatchObject({
179
+ type: "object",
180
+ $schema: "http://json-schema.org/draft-07/schema#",
181
+ });
182
+ });
183
+
184
+ test.concurrent("nested subgraphs", () => {
185
+ const schemas = SubgraphExtractor.extractSchemas(
186
+ {
187
+ contents: dedent`
188
+ import { HumanMessage } from "@langchain/core/messages";
189
+ import {
190
+ Annotation,
191
+ MessagesAnnotation,
192
+ StateGraph,
193
+ } from "@langchain/langgraph";
194
+
195
+ const ParentSchema = MessagesAnnotation;
196
+
197
+ const ChildSchema = Annotation.Root({
198
+ ...MessagesAnnotation.spec,
199
+ child: Annotation<"alpha" | "beta">,
200
+ });
201
+
202
+ const SubchildSchema = Annotation.Root({
203
+ ...MessagesAnnotation.spec,
204
+ subchild: Annotation<"one" | "two">,
205
+ });
206
+
207
+ const subchild = new StateGraph(SubchildSchema)
208
+ .addNode("subchild_one", () => ({ messages: [new HumanMessage("subchild_one")] }))
209
+ .addNode("subchild_two", () => ({ messages: [new HumanMessage("subchild_two")] }))
210
+ .addEdge("__start__", "subchild_one")
211
+ .addEdge("subchild_one", "subchild_two")
212
+ .compile();
213
+
214
+ const child = new StateGraph(ChildSchema)
215
+ .addNode("child_one", () => ({ messages: [new HumanMessage("child_one")] }))
216
+ .addNode("child_two", subchild)
217
+ .addEdge("__start__", "child_one")
218
+ .addEdge("child_one", "child_two")
219
+ .compile();
220
+
221
+ export const parent = new StateGraph(ParentSchema)
222
+ .addNode("parent_one", () => ({ messages: [new HumanMessage("parent_one")] }))
223
+ .addNode("parent_two", child)
224
+ .addEdge("__start__", "parent_one")
225
+ .addEdge("parent_one", "parent_two")
226
+ .compile();
227
+ `,
228
+ },
229
+ "parent"
230
+ );
231
+
232
+ expect(Object.keys(schemas)).toEqual(
233
+ expect.arrayContaining([
234
+ "parent",
235
+ "parent|parent_two",
236
+ "parent|parent_two|child_two",
237
+ ])
238
+ );
239
+
240
+ expect(schemas.parent.state).toMatchObject({
241
+ type: "object",
242
+ properties: {
243
+ messages: {
244
+ type: "array",
245
+ items: { $ref: "#/definitions/BaseMessage" },
246
+ },
247
+ },
248
+ definitions: {
249
+ BaseMessage: {
250
+ oneOf: expect.arrayContaining([
251
+ { $ref: "#/definitions/BaseMessageChunk" },
252
+ ]),
253
+ },
254
+ },
255
+ $schema: "http://json-schema.org/draft-07/schema#",
256
+ });
257
+
258
+ expect(schemas["parent|parent_two"].state).toMatchObject({
259
+ type: "object",
260
+ properties: {
261
+ child: {
262
+ type: "string",
263
+ enum: expect.arrayContaining(["alpha", "beta"]),
264
+ },
265
+ messages: {
266
+ type: "array",
267
+ items: { $ref: "#/definitions/BaseMessage" },
268
+ },
269
+ },
270
+ definitions: {
271
+ BaseMessage: {
272
+ oneOf: expect.arrayContaining([
273
+ { $ref: "#/definitions/BaseMessageChunk" },
274
+ ]),
275
+ },
276
+ },
277
+ $schema: "http://json-schema.org/draft-07/schema#",
278
+ });
279
+
280
+ expect(schemas["parent|parent_two|child_two"].state).toMatchObject({
281
+ type: "object",
282
+ properties: {
283
+ subchild: {
284
+ type: "string",
285
+ enum: expect.arrayContaining(["one", "two"]),
286
+ },
287
+ messages: {
288
+ type: "array",
289
+ items: { $ref: "#/definitions/BaseMessage" },
290
+ },
291
+ },
292
+ definitions: {
293
+ BaseMessage: {
294
+ oneOf: expect.arrayContaining([
295
+ { $ref: "#/definitions/BaseMessageChunk" },
296
+ ]),
297
+ },
298
+ },
299
+ $schema: "http://json-schema.org/draft-07/schema#",
300
+ });
301
+ });
302
+
303
+ test.concurrent("multiple subgraphs within a single node", () => {
304
+ expect(() => {
305
+ SubgraphExtractor.extractSchemas(
306
+ {
307
+ contents: dedent`
308
+ import { HumanMessage } from "@langchain/core/messages";
309
+ import {
310
+ Annotation,
311
+ MessagesAnnotation,
312
+ StateGraph,
313
+ } from "@langchain/langgraph";
314
+
315
+ const ParentSchema = MessagesAnnotation;
316
+
317
+ const ChildSchema = Annotation.Root({
318
+ ...MessagesAnnotation.spec,
319
+ child: Annotation<"alpha" | "beta">,
320
+ });
321
+
322
+ const SubchildSchema = Annotation.Root({
323
+ ...MessagesAnnotation.spec,
324
+ subchild: Annotation<"one" | "two">,
325
+ });
326
+
327
+ const subchild = new StateGraph(SubchildSchema)
328
+ .addNode("subchild_one", () => ({ messages: [new HumanMessage("subchild_one")] }))
329
+ .addNode("subchild_two", () => ({ messages: [new HumanMessage("subchild_two")] }))
330
+ .addEdge("__start__", "subchild_one")
331
+ .addEdge("subchild_one", "subchild_two")
332
+ .compile();
333
+
334
+ const child = new StateGraph(ChildSchema)
335
+ .addNode("child_one", () => ({ messages: [new HumanMessage("child_one")] }))
336
+ .addNode("child_two", () => ({ messages: [new HumanMessage("child_two")] }))
337
+ .addEdge("__start__", "child_one")
338
+ .addEdge("child_one", "child_two")
339
+ .compile();
340
+
341
+ export const parent = new StateGraph(ParentSchema)
342
+ .addNode("parent_one", async (schema) => {
343
+ const messages = []
344
+ messages.concat((await child.invoke(schema)).messages)
345
+ messages.concat((await subchild.invoke(schema)).messages)
346
+ return { messages }
347
+ })
348
+ .addNode("parent_two", child)
349
+ .addEdge("__start__", "parent_one")
350
+ .addEdge("parent_one", "parent_two")
351
+ .compile();
352
+ `,
353
+ },
354
+ "parent",
355
+ { strict: true }
356
+ );
357
+ }).toThrowError(
358
+ `Multiple unique subgraph invocations found for "parent|parent_one"`
359
+ );
360
+ });
361
+
362
+ test.concurrent("imported subgraphs", () => {
363
+ const schemas = SubgraphExtractor.extractSchemas(
364
+ {
365
+ contents: dedent`
366
+ import { HumanMessage } from "@langchain/core/messages";
367
+ import { subgraph } from "./subgraph.mts";
368
+ import {
369
+ Annotation,
370
+ MessagesAnnotation,
371
+ StateGraph,
372
+ } from "@langchain/langgraph";
373
+
374
+ const ParentSchema = MessagesAnnotation;
375
+
376
+ const SubgraphSchema = Annotation.Root({
377
+ ...MessagesAnnotation.spec,
378
+ kind: Annotation<"weather" | "other">,
379
+ });
380
+
381
+ export const graph = new StateGraph(ParentSchema)
382
+ .addNode("parent", () => {
383
+ return { messages: [new HumanMessage("Hello from child")] };
384
+ })
385
+ .addNode("child", subgraph)
386
+ .addEdge("__start__", "parent")
387
+ .addEdge("parent", "child")
388
+ .addEdge("child", "__end__")
389
+ .compile();
390
+ `,
391
+ files: [
392
+ [
393
+ "./subgraph.mts",
394
+ dedent`
395
+ import { HumanMessage } from "@langchain/core/messages";
396
+ import {
397
+ Annotation,
398
+ MessagesAnnotation,
399
+ StateGraph,
400
+ } from "@langchain/langgraph";
401
+
402
+ const SubgraphSchema = Annotation.Root({
403
+ ...MessagesAnnotation.spec,
404
+ kind: Annotation<"weather" | "other">,
405
+ });
406
+
407
+ export const subgraph = new StateGraph(SubgraphSchema)
408
+ .addNode("child", () => {
409
+ return { messages: [new HumanMessage("Hello from child")] };
410
+ })
411
+ .addEdge("__start__", "child")
412
+ .compile();
413
+ `,
414
+ ],
415
+ ],
416
+ },
417
+ "graph"
418
+ );
419
+
420
+ expect(Object.keys(schemas)).toEqual(
421
+ expect.arrayContaining(["graph", "graph|child"])
422
+ );
423
+
424
+ expect(schemas["graph|child"].input).toMatchObject({
425
+ type: "object",
426
+ properties: {
427
+ kind: {
428
+ type: "string",
429
+ enum: expect.arrayContaining(["weather", "other"]),
430
+ },
431
+ messages: {
432
+ type: "array",
433
+ items: { $ref: "#/definitions/BaseMessage" },
434
+ },
435
+ },
436
+ definitions: {
437
+ BaseMessage: {
438
+ oneOf: expect.arrayContaining([
439
+ { $ref: "#/definitions/BaseMessageChunk" },
440
+ ]),
441
+ },
442
+ },
443
+ $schema: "http://json-schema.org/draft-07/schema#",
444
+ });
445
+
446
+ expect(schemas["graph|child"].output).toMatchObject({
447
+ type: "object",
448
+ properties: {
449
+ kind: {
450
+ type: "string",
451
+ enum: expect.arrayContaining(["weather", "other"]),
452
+ },
453
+ messages: {
454
+ type: "array",
455
+ items: { $ref: "#/definitions/BaseMessage" },
456
+ },
457
+ },
458
+ definitions: {
459
+ BaseMessage: {
460
+ oneOf: expect.arrayContaining([
461
+ { $ref: "#/definitions/BaseMessageChunk" },
462
+ ]),
463
+ },
464
+ },
465
+ $schema: "http://json-schema.org/draft-07/schema#",
466
+ });
467
+
468
+ expect(schemas["graph|child"].state).toMatchObject({
469
+ type: "object",
470
+ properties: {
471
+ kind: {
472
+ type: "string",
473
+ enum: expect.arrayContaining(["weather", "other"]),
474
+ },
475
+ messages: {
476
+ type: "array",
477
+ items: { $ref: "#/definitions/BaseMessage" },
478
+ },
479
+ },
480
+ definitions: {
481
+ BaseMessage: {
482
+ oneOf: expect.arrayContaining([
483
+ { $ref: "#/definitions/BaseMessageChunk" },
484
+ ]),
485
+ },
486
+ },
487
+ $schema: "http://json-schema.org/draft-07/schema#",
488
+ });
489
+
490
+ expect(schemas["graph|child"].config).toMatchObject({
491
+ type: "object",
492
+ $schema: "http://json-schema.org/draft-07/schema#",
493
+ });
494
+ });
495
+
496
+ test.concurrent("imported uncompiled subgraphs", () => {
497
+ const schemas = SubgraphExtractor.extractSchemas(
498
+ {
499
+ contents: dedent`
500
+ import { HumanMessage } from "@langchain/core/messages";
501
+ import { subgraph } from "./subgraph.mts";
502
+ import {
503
+ Annotation,
504
+ MessagesAnnotation,
505
+ StateGraph,
506
+ } from "@langchain/langgraph";
507
+
508
+ const ParentSchema = MessagesAnnotation;
509
+
510
+ const SubgraphSchema = Annotation.Root({
511
+ ...MessagesAnnotation.spec,
512
+ kind: Annotation<"weather" | "other">,
513
+ });
514
+
515
+ export const graph = new StateGraph(ParentSchema)
516
+ .addNode("parent", () => {
517
+ return { messages: [new HumanMessage("Hello from child")] };
518
+ })
519
+ .addNode("child", subgraph.compile())
520
+ .addEdge("__start__", "parent")
521
+ .addEdge("parent", "child")
522
+ .addEdge("child", "__end__")
523
+ .compile();
524
+ `,
525
+ files: [
526
+ [
527
+ "./subgraph.mts",
528
+ dedent`
529
+ import { HumanMessage } from "@langchain/core/messages";
530
+ import {
531
+ Annotation,
532
+ MessagesAnnotation,
533
+ StateGraph,
534
+ } from "@langchain/langgraph";
535
+
536
+ const SubgraphSchema = Annotation.Root({
537
+ ...MessagesAnnotation.spec,
538
+ kind: Annotation<"weather" | "other">,
539
+ });
540
+
541
+ export const subgraph = new StateGraph(SubgraphSchema)
542
+ .addNode("child", () => {
543
+ return { messages: [new HumanMessage("Hello from child")] };
544
+ })
545
+ .addEdge("__start__", "child")
546
+ `,
547
+ ],
548
+ ],
549
+ },
550
+ "graph"
551
+ );
552
+
553
+ expect(Object.keys(schemas)).toEqual(
554
+ expect.arrayContaining(["graph", "graph|child"])
555
+ );
556
+
557
+ expect(schemas["graph|child"].input).toMatchObject({
558
+ type: "object",
559
+ properties: {
560
+ kind: {
561
+ type: "string",
562
+ enum: expect.arrayContaining(["weather", "other"]),
563
+ },
564
+ messages: {
565
+ type: "array",
566
+ items: { $ref: "#/definitions/BaseMessage" },
567
+ },
568
+ },
569
+ definitions: {
570
+ BaseMessage: {
571
+ oneOf: expect.arrayContaining([
572
+ { $ref: "#/definitions/BaseMessageChunk" },
573
+ ]),
574
+ },
575
+ },
576
+ $schema: "http://json-schema.org/draft-07/schema#",
577
+ });
578
+
579
+ expect(schemas["graph|child"].output).toMatchObject({
580
+ type: "object",
581
+ properties: {
582
+ kind: {
583
+ type: "string",
584
+ enum: expect.arrayContaining(["weather", "other"]),
585
+ },
586
+ messages: {
587
+ type: "array",
588
+ items: { $ref: "#/definitions/BaseMessage" },
589
+ },
590
+ },
591
+ definitions: {
592
+ BaseMessage: {
593
+ oneOf: expect.arrayContaining([
594
+ { $ref: "#/definitions/BaseMessageChunk" },
595
+ ]),
596
+ },
597
+ },
598
+ $schema: "http://json-schema.org/draft-07/schema#",
599
+ });
600
+
601
+ expect(schemas["graph|child"].state).toMatchObject({
602
+ type: "object",
603
+ properties: {
604
+ kind: {
605
+ type: "string",
606
+ enum: expect.arrayContaining(["weather", "other"]),
607
+ },
608
+ messages: {
609
+ type: "array",
610
+ items: { $ref: "#/definitions/BaseMessage" },
611
+ },
612
+ },
613
+ definitions: {
614
+ BaseMessage: {
615
+ oneOf: expect.arrayContaining([
616
+ { $ref: "#/definitions/BaseMessageChunk" },
617
+ ]),
618
+ },
619
+ },
620
+ $schema: "http://json-schema.org/draft-07/schema#",
621
+ });
622
+
623
+ expect(schemas["graph|child"].config).toMatchObject({
624
+ type: "object",
625
+ $schema: "http://json-schema.org/draft-07/schema#",
626
+ });
627
+ });
628
+
629
+ test.concurrent("indirect", () => {
630
+ const schemas = SubgraphExtractor.extractSchemas(
631
+ {
632
+ contents: dedent`
633
+ import { HumanMessage } from "@langchain/core/messages";
634
+ import {
635
+ Annotation,
636
+ MessagesAnnotation,
637
+ StateGraph,
638
+ } from "@langchain/langgraph";
639
+
640
+ const ParentSchema = MessagesAnnotation;
641
+
642
+ const SubgraphSchema = Annotation.Root({
643
+ ...MessagesAnnotation.spec,
644
+ kind: Annotation<"weather" | "other">,
645
+ });
646
+
647
+ const subgraph = new StateGraph(SubgraphSchema)
648
+ .addNode("child", () => {
649
+ return { messages: [new HumanMessage("Hello from child")] };
650
+ })
651
+ .addEdge("__start__", "child")
652
+ .compile();
653
+
654
+ const parent = new StateGraph(ParentSchema)
655
+ .addNode("parent", () => {
656
+ return { messages: [new HumanMessage("Hello from child")] };
657
+ })
658
+ .addNode("child", subgraph)
659
+ .addEdge("__start__", "parent")
660
+ .addEdge("parent", "child")
661
+ .addEdge("child", "__end__");
662
+
663
+ const indirect1 = parent
664
+ const indirect2 = (() => indirect1)()
665
+ export const graph = parent.compile()
666
+ `,
667
+ },
668
+ "graph"
669
+ );
670
+ expect(schemas["graph|child"].input).toMatchObject({
671
+ type: "object",
672
+ properties: {
673
+ kind: {
674
+ type: "string",
675
+ enum: expect.arrayContaining(["weather", "other"]),
676
+ },
677
+ messages: {
678
+ type: "array",
679
+ items: { $ref: "#/definitions/BaseMessage" },
680
+ },
681
+ },
682
+ definitions: {
683
+ BaseMessage: {
684
+ oneOf: expect.arrayContaining([
685
+ { $ref: "#/definitions/BaseMessageChunk" },
686
+ ]),
687
+ },
688
+ },
689
+ $schema: "http://json-schema.org/draft-07/schema#",
690
+ });
691
+
692
+ expect(schemas["graph|child"].output).toMatchObject({
693
+ type: "object",
694
+ properties: {
695
+ kind: {
696
+ type: "string",
697
+ enum: expect.arrayContaining(["weather", "other"]),
698
+ },
699
+ messages: {
700
+ type: "array",
701
+ items: { $ref: "#/definitions/BaseMessage" },
702
+ },
703
+ },
704
+ definitions: {
705
+ BaseMessage: {
706
+ oneOf: expect.arrayContaining([
707
+ { $ref: "#/definitions/BaseMessageChunk" },
708
+ ]),
709
+ },
710
+ },
711
+ $schema: "http://json-schema.org/draft-07/schema#",
712
+ });
713
+
714
+ expect(schemas["graph|child"].state).toMatchObject({
715
+ type: "object",
716
+ properties: {
717
+ kind: {
718
+ type: "string",
719
+ enum: expect.arrayContaining(["weather", "other"]),
720
+ },
721
+ messages: {
722
+ type: "array",
723
+ items: { $ref: "#/definitions/BaseMessage" },
724
+ },
725
+ },
726
+ definitions: {
727
+ BaseMessage: {
728
+ oneOf: expect.arrayContaining([
729
+ { $ref: "#/definitions/BaseMessageChunk" },
730
+ ]),
731
+ },
732
+ },
733
+ $schema: "http://json-schema.org/draft-07/schema#",
734
+ });
735
+
736
+ expect(schemas["graph|child"].config).toMatchObject({
737
+ type: "object",
738
+ $schema: "http://json-schema.org/draft-07/schema#",
739
+ });
740
+ });
741
+ });
742
+
743
+ test.concurrent("weather", () => {
744
+ const schemas = SubgraphExtractor.extractSchemas(
745
+ {
746
+ contents: dedent`
747
+ import { Annotation, StateGraph, END, START } from "@langchain/langgraph";
748
+ import { MessagesAnnotation } from "@langchain/langgraph";
749
+ import { AIMessage } from "@langchain/core/messages";
750
+
751
+ const state = MessagesAnnotation;
752
+
753
+ const weatherState = Annotation.Root({
754
+ ...state.spec,
755
+ city: Annotation<string>,
756
+ });
757
+
758
+ const routerState = Annotation.Root({
759
+ ...state.spec,
760
+ route: Annotation<"weather" | "other">,
761
+ });
762
+
763
+ const weather = new StateGraph(weatherState)
764
+ .addNode("model_node", (state) => {
765
+ const llm = new AIMessage({
766
+ content: "",
767
+ tool_calls: [
768
+ {
769
+ id: "tool_call123",
770
+ name: "get_weather",
771
+ args: { city: "San Francisco" },
772
+ },
773
+ ],
774
+ });
775
+
776
+ return { city: llm.tool_calls![0].args.city };
777
+ })
778
+ .addNode("weather_node", async (state) => {
779
+ const result = \`It's sunny in $\{state.city}!\`;
780
+ return { messages: [new AIMessage({ content: result })] };
781
+ })
782
+ .addEdge(START, "model_node")
783
+ .addEdge("model_node", "weather_node")
784
+ .addEdge("weather_node", END)
785
+ .compile({ interruptBefore: ["weather_node"] });
786
+
787
+ const router = new StateGraph(routerState)
788
+ .addNode("router_node", async () => ({ route: "weather" }))
789
+ .addNode("normal_llm_node", () => ({ messages: [new AIMessage("Hello")] }))
790
+ .addNode("weather_graph", weather)
791
+ .addEdge(START, "router_node")
792
+ .addConditionalEdges(
793
+ "router_node",
794
+ ({ route }) => {
795
+ if (route === "weather") return "weather_graph";
796
+ return "normal_llm_node";
797
+ },
798
+ ["weather_graph", "normal_llm_node"]
799
+ )
800
+ .addEdge("weather_graph", END)
801
+ .addEdge("normal_llm_node", END);
802
+
803
+ export const graph = router.compile();
804
+ `,
805
+ },
806
+ "graph"
807
+ );
808
+
809
+ expect(Object.keys(schemas)).toEqual(
810
+ expect.arrayContaining(["graph", "graph|weather_graph"])
811
+ );
812
+ });
813
+
814
+ test.concurrent("nested", () => {
815
+ const schemas = SubgraphExtractor.extractSchemas(
816
+ {
817
+ contents: dedent`
818
+ import { Annotation, StateGraph, END, START } from "@langchain/langgraph";
819
+
820
+ const child = new StateGraph(
821
+ Annotation.Root({
822
+ messages: Annotation<string[]>({
823
+ reducer: (a, b) => a.concat(b),
824
+ }),
825
+ child: Annotation<"child_one" | "child_two">,
826
+ })
827
+ )
828
+ .addNode("c_one", () => ({ messages: ["Entered c_one node"] }))
829
+ .addNode("c_two", () => ({ messages: ["Entered c_two node"] }))
830
+ .addEdge(START, "c_one")
831
+ .addEdge("c_one", "c_two")
832
+ .addEdge("c_two", END);
833
+
834
+ const parent = new StateGraph(
835
+ Annotation.Root({
836
+ messages: Annotation<string[]>({
837
+ reducer: (a, b) => a.concat(b),
838
+ }),
839
+ parent: Annotation<"parent_one" | "parent_two">,
840
+ })
841
+ )
842
+ .addNode("p_one", () => ({ messages: ["Entered p_one node"] }))
843
+ .addNode("p_two", child.compile())
844
+ .addEdge(START, "p_one")
845
+ .addEdge("p_one", "p_two")
846
+ .addEdge("p_two", END);
847
+
848
+ const grandParent = new StateGraph(
849
+ Annotation.Root({
850
+ messages: Annotation<string[]>({
851
+ reducer: (a, b) => a.concat(b),
852
+ }),
853
+ })
854
+ )
855
+ .addNode("gp_one", () => ({ messages: ["Entered gp_one node"] }))
856
+ .addNode("gp_two", parent.compile())
857
+ .addEdge(START, "gp_one")
858
+ .addEdge("gp_one", "gp_two")
859
+ .addEdge("gp_two", END);
860
+
861
+ export const graph = grandParent.compile();
862
+ `,
863
+ },
864
+ "graph"
865
+ );
866
+
867
+ expect(Object.keys(schemas)).toEqual(
868
+ expect.arrayContaining(["graph", "graph|gp_two", "graph|gp_two|p_two"])
869
+ );
870
+ });