langgraph-api 0.0.26__py3-none-any.whl → 0.0.28rc1__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 (53) hide show
  1. langgraph_api/api/__init__.py +2 -0
  2. langgraph_api/api/assistants.py +43 -13
  3. langgraph_api/api/meta.py +2 -1
  4. langgraph_api/api/runs.py +14 -1
  5. langgraph_api/api/ui.py +68 -0
  6. langgraph_api/asyncio.py +43 -4
  7. langgraph_api/auth/middleware.py +2 -2
  8. langgraph_api/cli.py +72 -57
  9. langgraph_api/config.py +23 -1
  10. langgraph_api/cron_scheduler.py +1 -1
  11. langgraph_api/graph.py +5 -0
  12. langgraph_api/http.py +24 -7
  13. langgraph_api/js/.gitignore +2 -0
  14. langgraph_api/js/build.mts +49 -3
  15. langgraph_api/js/client.mts +84 -40
  16. langgraph_api/js/global.d.ts +1 -0
  17. langgraph_api/js/package.json +15 -7
  18. langgraph_api/js/remote.py +662 -16
  19. langgraph_api/js/src/graph.mts +5 -4
  20. langgraph_api/js/sse.py +138 -0
  21. langgraph_api/js/tests/api.test.mts +28 -0
  22. langgraph_api/js/tests/compose-postgres.yml +2 -2
  23. langgraph_api/js/tests/graphs/agent.css +1 -0
  24. langgraph_api/js/tests/graphs/agent.ui.tsx +10 -0
  25. langgraph_api/js/tests/graphs/package.json +2 -2
  26. langgraph_api/js/tests/graphs/yarn.lock +13 -13
  27. langgraph_api/js/yarn.lock +710 -1187
  28. langgraph_api/lifespan.py +15 -5
  29. langgraph_api/logging.py +9 -0
  30. langgraph_api/metadata.py +5 -1
  31. langgraph_api/middleware/http_logger.py +1 -1
  32. langgraph_api/patch.py +2 -0
  33. langgraph_api/queue_entrypoint.py +63 -0
  34. langgraph_api/schema.py +2 -0
  35. langgraph_api/stream.py +1 -0
  36. langgraph_api/webhook.py +42 -0
  37. langgraph_api/{queue.py → worker.py} +52 -166
  38. {langgraph_api-0.0.26.dist-info → langgraph_api-0.0.28rc1.dist-info}/METADATA +8 -8
  39. {langgraph_api-0.0.26.dist-info → langgraph_api-0.0.28rc1.dist-info}/RECORD +49 -46
  40. langgraph_storage/database.py +8 -22
  41. langgraph_storage/inmem_stream.py +108 -0
  42. langgraph_storage/ops.py +80 -57
  43. langgraph_storage/queue.py +126 -103
  44. langgraph_storage/retry.py +5 -1
  45. langgraph_storage/store.py +5 -1
  46. openapi.json +3 -3
  47. langgraph_api/js/client.new.mts +0 -861
  48. langgraph_api/js/remote_new.py +0 -694
  49. langgraph_api/js/remote_old.py +0 -667
  50. langgraph_api/js/server_sent_events.py +0 -126
  51. {langgraph_api-0.0.26.dist-info → langgraph_api-0.0.28rc1.dist-info}/LICENSE +0 -0
  52. {langgraph_api-0.0.26.dist-info → langgraph_api-0.0.28rc1.dist-info}/WHEEL +0 -0
  53. {langgraph_api-0.0.26.dist-info → langgraph_api-0.0.28rc1.dist-info}/entry_points.txt +0 -0
@@ -9,6 +9,7 @@ import {
9
9
  resolveGraph,
10
10
  runGraphSchemaWorker,
11
11
  } from "./src/graph.mts";
12
+ import { build } from "@langchain/langgraph-api/ui/bundler";
12
13
 
13
14
  const __dirname = new URL(".", import.meta.url).pathname;
14
15
 
@@ -17,7 +18,7 @@ async function main() {
17
18
  z.record(z.string()).parse(JSON.parse(process.env.LANGSERVE_GRAPHS))
18
19
  );
19
20
 
20
- const GRAPH_SCHEMAS: Record<string, Record<string, GraphSchema>> = {};
21
+ const GRAPH_SCHEMAS: Record<string, Record<string, GraphSchema> | false> = {};
21
22
 
22
23
  try {
23
24
  await Promise.all(
@@ -29,9 +30,12 @@ async function main() {
29
30
 
30
31
  try {
31
32
  console.info(`[${graphId}]: Extracting schema`);
32
- GRAPH_SCHEMAS[graphId] = await runGraphSchemaWorker(spec);
33
+ GRAPH_SCHEMAS[graphId] = await runGraphSchemaWorker(spec, {
34
+ timeoutMs: 120_000,
35
+ });
33
36
  } catch (error) {
34
37
  console.error(`[${graphId}]: Error extracting schema: ${error}`);
38
+ GRAPH_SCHEMAS[graphId] = false;
35
39
  }
36
40
  })
37
41
  );
@@ -46,7 +50,49 @@ async function main() {
46
50
  process.exit(1);
47
51
  }
48
52
 
49
- console.info("All graphs resolved");
53
+ const uiSpecs = z
54
+ .record(z.string())
55
+ .parse(JSON.parse(process.env.LANGGRAPH_UI || "{}"));
56
+
57
+ if (Object.keys(uiSpecs).length > 0) {
58
+ try {
59
+ const schemas: Record<string, { assets: string[]; name: string }> = {};
60
+ await Promise.all(
61
+ Object.entries(uiSpecs).map(async ([graphId, uiUserPath]) => {
62
+ console.info(`[${graphId}]: Building UI`);
63
+ const userPath = path.resolve(process.cwd(), uiUserPath);
64
+ const files = await build(graphId, userPath);
65
+ await Promise.all([
66
+ ...files.map(async (item) => {
67
+ const folder = path.resolve(__dirname, "ui", graphId);
68
+ const source = path.resolve(folder, item.basename);
69
+
70
+ await fs.mkdir(path.dirname(source), { recursive: true });
71
+ await fs.writeFile(source, item.contents);
72
+
73
+ schemas[graphId] ??= { assets: [], name: graphId };
74
+
75
+ const relative = path.relative(
76
+ path.resolve(__dirname, "ui", graphId),
77
+ source
78
+ );
79
+
80
+ schemas[graphId].assets.push(relative);
81
+ }),
82
+ ]);
83
+ })
84
+ );
85
+
86
+ await fs.writeFile(
87
+ path.resolve(__dirname, "client.ui.schemas.json"),
88
+ JSON.stringify(schemas),
89
+ { encoding: "utf-8" }
90
+ );
91
+ } catch (error) {
92
+ console.error(`Error building UI: ${error}`);
93
+ process.exit(1);
94
+ }
95
+ }
50
96
  }
51
97
 
52
98
  main();
@@ -4,9 +4,9 @@ import { z } from "zod";
4
4
  import { Context, Hono } from "hono";
5
5
  import { serve } from "@hono/node-server";
6
6
  import { zValidator } from "@hono/zod-validator";
7
- import { streamSSE } from "hono/streaming";
7
+ import { streamSSE, stream } from "hono/streaming";
8
8
  import { HTTPException } from "hono/http-exception";
9
- import { Agent, fetch } from "undici";
9
+ import { fetch } from "undici";
10
10
  import pRetry from "p-retry";
11
11
  import {
12
12
  BaseStore,
@@ -43,6 +43,8 @@ import {
43
43
  GraphSpec,
44
44
  filterValidGraphSpecs,
45
45
  } from "./src/graph.mts";
46
+ import { asyncExitHook, gracefulExit } from "exit-hook";
47
+ import { awaitAllCallbacks } from "@langchain/core/callbacks/promises";
46
48
 
47
49
  const logger = createLogger({
48
50
  level: "debug",
@@ -76,7 +78,7 @@ const logger = createLogger({
76
78
  ],
77
79
  });
78
80
 
79
- let GRAPH_SCHEMA: Record<string, Record<string, GraphSchema>> = {};
81
+ let GRAPH_SCHEMA: Record<string, Record<string, GraphSchema> | false> = {};
80
82
  const GRAPH_RESOLVED: Record<string, CompiledGraph<string>> = {};
81
83
  const GRAPH_SPEC: Record<string, GraphSpec> = {};
82
84
 
@@ -92,9 +94,28 @@ async function getOrExtractSchema(graphId: string) {
92
94
  }
93
95
 
94
96
  if (!GRAPH_SCHEMA[graphId]) {
97
+ // This is only set during build phase
98
+ if (GRAPH_SCHEMA[graphId] === false) {
99
+ throw new Error(`Failed to locate schema for "${graphId}"`);
100
+ }
101
+
95
102
  try {
96
103
  const timer = logger.startTimer();
97
- GRAPH_SCHEMA[graphId] = await runGraphSchemaWorker(GRAPH_SPEC[graphId]);
104
+
105
+ let timeoutMs: number | undefined = undefined;
106
+ try {
107
+ timeoutMs = Number.parseInt(
108
+ process.env.LANGGRAPH_SCHEMA_RESOLVE_TIMEOUT_MS || "30000",
109
+ 10
110
+ );
111
+ if (Number.isNaN(timeoutMs) || timeoutMs <= 0) timeoutMs = undefined;
112
+ } catch {
113
+ // ignore
114
+ }
115
+
116
+ GRAPH_SCHEMA[graphId] = await runGraphSchemaWorker(GRAPH_SPEC[graphId], {
117
+ timeoutMs,
118
+ });
98
119
  timer.done({ message: `Extracting schema for ${graphId} finished` });
99
120
  } catch (error) {
100
121
  throw new Error(`Failed to extract schema for "${graphId}": ${error}`);
@@ -104,11 +125,8 @@ async function getOrExtractSchema(graphId: string) {
104
125
  return GRAPH_SCHEMA[graphId];
105
126
  }
106
127
 
107
- const GRAPH_SOCKET = "./graph.sock";
108
- const REMOTE_SOCKET = "./checkpointer.sock";
109
- const remoteDispatcher = new Agent({
110
- connect: { socketPath: REMOTE_SOCKET },
111
- });
128
+ const GRAPH_PORT = 5556;
129
+ const REMOTE_PORT = 5555;
112
130
 
113
131
  const RunnableConfigSchema = z.object({
114
132
  tags: z.array(z.string()).optional(),
@@ -165,8 +183,7 @@ async function sendRecv<T = any>(
165
183
  method: `${"checkpointer" | "store"}_${string}`,
166
184
  data: unknown
167
185
  ): Promise<T> {
168
- const res = await tryFetch(`http://remote/${method}`, {
169
- dispatcher: remoteDispatcher,
186
+ const res = await tryFetch(`http://localhost:${REMOTE_PORT}/${method}`, {
170
187
  method: "POST",
171
188
  headers: { "Content-Type": "application/json" },
172
189
  body: JSON.stringify(data),
@@ -180,6 +197,7 @@ async function sendRecv<T = any>(
180
197
  })) as T;
181
198
  }
182
199
 
200
+ const HEARTBEAT_MS = 5_000;
183
201
  const handleInvoke = <T extends z.ZodType<any>>(
184
202
  name: string,
185
203
  _schema: T,
@@ -188,8 +206,29 @@ const handleInvoke = <T extends z.ZodType<any>>(
188
206
  return async (c: Context<any, any, { in: z.infer<T>; out: any }>) => {
189
207
  const graphId = c.req.param("graphId");
190
208
  const body = c.req.valid("json") as any;
191
- const response = await handler({ graph_id: graphId, ...body });
192
- return c.json(response);
209
+
210
+ // send heartbeat every HEARTBEAT_INTERVAL_MS
211
+ // to prevent connection from timing out
212
+ c.header("Content-Type", "application/json");
213
+ return stream(c, async (stream) => {
214
+ let resolved: Promise<any> = Promise.resolve();
215
+ const enqueueWrite = (payload: Uint8Array | string) => {
216
+ resolved = stream.write(payload);
217
+ return resolved;
218
+ };
219
+
220
+ // orjson.loads(...) does ignore the
221
+ // whitespace prefix, so we can use that
222
+ // as a heartbeat
223
+ let interval = setInterval(() => enqueueWrite(" "), HEARTBEAT_MS);
224
+
225
+ const response = JSON.stringify(
226
+ await handler({ graph_id: graphId, ...body })
227
+ );
228
+
229
+ clearInterval(interval);
230
+ await enqueueWrite(response);
231
+ });
193
232
  };
194
233
  };
195
234
 
@@ -202,25 +241,35 @@ const handleStream = <T extends z.ZodType<any>>(
202
241
  const graphId = c.req.param("graphId");
203
242
  const body = c.req.valid("json") as any;
204
243
  return streamSSE(c, async (stream) => {
244
+ let resolve: Promise<any> = Promise.resolve();
245
+ let timer: NodeJS.Timeout | undefined;
246
+ const sendHeartbeat = () => {
247
+ clearTimeout(timer);
248
+ resolve = stream.writeln(": heartbeat");
249
+ timer = setTimeout(sendHeartbeat, HEARTBEAT_MS);
250
+ return resolve;
251
+ };
252
+
253
+ const sendSSE = (event: string, data: unknown) => {
254
+ clearTimeout(timer);
255
+ resolve = stream.writeSSE({ data: serialiseAsDict(data), event });
256
+ timer = setTimeout(sendHeartbeat, HEARTBEAT_MS);
257
+ return resolve;
258
+ };
259
+
205
260
  try {
206
261
  for await (const data of handler({ graph_id: graphId, ...body })) {
207
- await stream.writeSSE({
208
- data: Buffer.from(serialiseAsDict(data)).toString("base64"),
209
- event: name,
210
- });
262
+ await sendSSE(name, data);
211
263
  }
212
264
  } catch (error) {
213
265
  // Still print out the error, as the stack
214
266
  // trace is not carried over in Python
215
267
  logger.error(error);
216
-
217
- await stream.writeSSE({
218
- event: "error",
219
- data: Buffer.from(serialiseAsDict(serializeError(error))).toString(
220
- "base64"
221
- ),
222
- });
268
+ await sendSSE("error", serializeError(error));
223
269
  }
270
+
271
+ clearTimeout(timer);
272
+ await resolve;
224
273
  });
225
274
  };
226
275
  };
@@ -604,12 +653,12 @@ const GetGraphPayload = z.object({
604
653
  async function getGraphRequest(rawPayload: z.infer<typeof GetGraphPayload>) {
605
654
  const { graph_id: graphId, ...payload } = rawPayload;
606
655
  const graph = getGraph(graphId);
607
- return graph
608
- .getGraph({
609
- ...getRunnableConfig(payload.config),
610
- xray: payload.xray ?? undefined,
611
- })
612
- .toJSON();
656
+
657
+ const drawable = await graph.getGraphAsync({
658
+ ...getRunnableConfig(payload.config),
659
+ xray: payload.xray ?? undefined,
660
+ });
661
+ return drawable.toJSON();
613
662
  }
614
663
 
615
664
  const GetSubgraphsPayload = z.object({
@@ -630,7 +679,7 @@ async function getSubgraphsRequest(
630
679
 
631
680
  if (!rootGraphId) throw new Error("Failed to find root graph");
632
681
 
633
- for (const [name] of graph.getSubgraphs(
682
+ for await (const [name] of graph.getSubgraphsAsync(
634
683
  payload.namespace ?? undefined,
635
684
  payload.recurse ?? undefined
636
685
  )) {
@@ -818,20 +867,15 @@ async function main() {
818
867
  return c.text(message || "Internal server error", 500);
819
868
  });
820
869
 
821
- await fs.unlink(GRAPH_SOCKET).catch(() => void 0);
822
- serve(
823
- {
824
- fetch: app.fetch,
825
- hostname: "localhost",
826
- port: GRAPH_SOCKET as any,
827
- },
828
- (c) => logger.info(`Listening to ${c}`)
870
+ serve({ fetch: app.fetch, hostname: "localhost", port: GRAPH_PORT }, (c) =>
871
+ logger.info(`Listening to ${c.address}:${c.port}`)
829
872
  );
830
873
  }
831
874
 
832
875
  process.on("uncaughtExceptionMonitor", (error) => {
833
876
  logger.error(error);
834
- process.exit(1);
877
+ gracefulExit();
835
878
  });
836
879
 
880
+ asyncExitHook(() => awaitAllCallbacks(), { wait: 3_000 });
837
881
  main();
@@ -1,6 +1,7 @@
1
1
  declare namespace NodeJS {
2
2
  interface ProcessEnv {
3
3
  LANGSERVE_GRAPHS: string;
4
+ LANGGRAPH_UI?: string;
4
5
  PORT: string;
5
6
  }
6
7
  }
@@ -9,27 +9,35 @@
9
9
  "dependencies": {
10
10
  "@hono/node-server": "^1.12.0",
11
11
  "@hono/zod-validator": "^0.2.2",
12
- "@langchain/core": "^0.3.36",
13
- "@langchain/langgraph": "^0.2.43",
12
+ "@langchain/core": "^0.3.40",
13
+ "@langchain/langgraph": "^0.2.49",
14
+ "@langchain/langgraph-checkpoint": "^0.0.15",
14
15
  "@types/json-schema": "^7.0.15",
15
16
  "@typescript/vfs": "^1.6.0",
16
17
  "dedent": "^1.5.3",
18
+ "exit-hook": "^4.0.0",
17
19
  "hono": "^4.5.4",
18
20
  "p-queue": "^8.0.1",
19
21
  "p-retry": "^6.2.0",
20
- "tsx": "^4.19.1",
22
+ "tsx": "^4.19.3",
21
23
  "typescript": "^5.5.4",
22
24
  "undici": "^6.21.1",
23
25
  "uuid": "^10.0.0",
24
- "winston": "^3.15.0",
25
- "zeromq": "^6.3.0",
26
+ "winston": "^3.17.0",
27
+ "@langchain/langgraph-api": "~0.0.14",
26
28
  "zod": "^3.23.8"
27
29
  },
30
+ "resolutions": {
31
+ "esbuild": "^0.25.0"
32
+ },
28
33
  "devDependencies": {
29
34
  "@langchain/langgraph-sdk": "^0.0.31",
35
+ "@types/react": "^19.0.8",
36
+ "@types/react-dom": "^19.0.3",
30
37
  "@types/node": "^22.2.0",
31
38
  "postgres": "^3.4.4",
32
39
  "prettier": "^3.3.3",
33
40
  "vitest": "^3.0.4"
34
- }
35
- }
41
+ },
42
+ "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
43
+ }