langgraph-api 0.0.38__py3-none-any.whl → 0.0.40__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.

@@ -34,7 +34,7 @@ services:
34
34
  ADD . /deps/graphs
35
35
  WORKDIR /deps/graphs
36
36
  RUN yarn install --frozen-lockfile
37
- ENV LANGSERVE_GRAPHS='{"agent":"./agent.mts:graph", "nested": "./nested.mts:graph", "weather": "./weather.mts:graph", "error": "./error.mts:graph", "delay": "./delay.mts:graph"}'
37
+ ENV LANGSERVE_GRAPHS='{"agent":"./agent.mts:graph", "nested": "./nested.mts:graph", "weather": "./weather.mts:graph", "error": "./error.mts:graph", "delay": "./delay.mts:graph", "dynamic": "./dynamic.mts:graph"}'
38
38
  ENV LANGGRAPH_CONFIG='{"agent": {"configurable": {"model_name": "openai"}}}'
39
39
  RUN tsx /api/langgraph_api/js/build.mts
40
40
  depends_on:
@@ -0,0 +1,24 @@
1
+ import { StateGraph, Annotation } from "@langchain/langgraph";
2
+
3
+ export const graph = async (config: {
4
+ configurable?: { nodeName: string };
5
+ }) => {
6
+ const node = config.configurable?.nodeName ?? "default";
7
+
8
+ const state = Annotation.Root({
9
+ node: Annotation<string>,
10
+ messages: Annotation<string[]>({
11
+ default: () => [],
12
+ reducer: (a: string[], b: string[] | string) => {
13
+ if (Array.isArray(b)) return [...a, ...b];
14
+ return [...a, b];
15
+ },
16
+ }),
17
+ });
18
+
19
+ return new StateGraph(state)
20
+ .addNode(node, () => ({ node, messages: [node] }))
21
+ .addEdge("__start__", node)
22
+ .addEdge(node, "__end__")
23
+ .compile();
24
+ };
@@ -4,6 +4,7 @@
4
4
  "agent": "./agent.mts:graph",
5
5
  "subgraph": "./subgraph.mts:graph",
6
6
  "nested": "./nested.mts:graph",
7
+ "dynamic": "./dynamic.mts:graph",
7
8
  "delay": "./delay.mts:graph"
8
9
  }
9
10
  }
@@ -1,17 +1,17 @@
1
1
  export async function gatherIterator<T>(
2
- i: AsyncIterable<T> | Promise<AsyncIterable<T>>,
2
+ i: AsyncIterable<T> | Promise<AsyncIterable<T>>
3
3
  ): Promise<Array<T>> {
4
4
  const out: T[] = [];
5
5
  for await (const item of await i) out.push(item);
6
6
  return out;
7
7
  }
8
8
 
9
- export function findLast<T>(
9
+ export function findLast<T, S extends T>(
10
10
  lst: Array<T>,
11
- predicate: (item: T) => boolean,
12
- ): T | undefined {
11
+ predicate: (item: T) => item is S
12
+ ): S | undefined {
13
13
  for (let i = lst.length - 1; i >= 0; i--) {
14
- if (predicate(lst[i])) return lst[i];
14
+ if (predicate(lst[i])) return lst[i] as S;
15
15
  }
16
16
  return undefined;
17
17
  }
@@ -198,10 +198,10 @@
198
198
  zod "^3.22.4"
199
199
  zod-to-json-schema "^3.22.3"
200
200
 
201
- "@langchain/langgraph-api@~0.0.14":
202
- version "0.0.14"
203
- resolved "https://registry.yarnpkg.com/@langchain/langgraph-api/-/langgraph-api-0.0.14.tgz#fcecea45c22ca193631702d21197c38bf3f95026"
204
- integrity sha512-/lh6ug9kXBhL5zrX56MA4xxNt99kzLQqNuYqQRd2PWflVNATMRJNMfWhLjh91Hbn0yf3CWQoIX/6mPQiwCfrKg==
201
+ "@langchain/langgraph-api@~0.0.19":
202
+ version "0.0.19"
203
+ resolved "https://registry.yarnpkg.com/@langchain/langgraph-api/-/langgraph-api-0.0.19.tgz#e4b8054a1f4e7943bc53787fb6335470b55f4b1d"
204
+ integrity sha512-vsWnVUMVdr3i0ooI3Pcq4tbRKyRiDFGtUyyusCs2zZ/AHNIOV1ieHVTYubhZ04F+jNEwe0SwSMqtM0/Vg+cbTw==
205
205
  dependencies:
206
206
  "@babel/code-frame" "^7.26.2"
207
207
  "@hono/node-server" "^1.12.0"
@@ -238,10 +238,10 @@
238
238
  dependencies:
239
239
  uuid "^10.0.0"
240
240
 
241
- "@langchain/langgraph-sdk@^0.0.31":
242
- version "0.0.31"
243
- resolved "https://registry.yarnpkg.com/@langchain/langgraph-sdk/-/langgraph-sdk-0.0.31.tgz#186cc2d31bb504d301e227dc7448dd22a8ee1933"
244
- integrity sha512-oYZWoC3x7vH9bAL1Y30XjtuWnic1j3knXD4BbldsY0chFLxwIT5i6/GMThNy3Oiwb4SB+c6gvaSuxBNDkp7dkw==
241
+ "@langchain/langgraph-sdk@^0.0.60":
242
+ version "0.0.60"
243
+ resolved "https://registry.yarnpkg.com/@langchain/langgraph-sdk/-/langgraph-sdk-0.0.60.tgz#eee259632f362298b23712ccf393903daf8affc9"
244
+ integrity sha512-7ndeAdw1afVY72HpKEGw7AyuDlD7U3e4jxaJflxA+PXaFPiE0d/hQYvlPT84YmvqNzJN605hv7YcrOju2573bQ==
245
245
  dependencies:
246
246
  "@types/json-schema" "^7.0.15"
247
247
  p-queue "^6.6.2"
langgraph_api/worker.py CHANGED
@@ -10,6 +10,7 @@ from starlette.exceptions import HTTPException
10
10
 
11
11
  from langgraph_api.auth.custom import SimpleUser, normalize_user
12
12
  from langgraph_api.config import (
13
+ BG_JOB_ISOLATED_LOOPS,
13
14
  BG_JOB_MAX_RETRIES,
14
15
  BG_JOB_TIMEOUT_SECS,
15
16
  )
@@ -34,6 +35,11 @@ try:
34
35
  except ImportError:
35
36
  InFailedSqlTransaction = ()
36
37
 
38
+ try:
39
+ from blockbuster import BlockingError
40
+ except ImportError:
41
+ BlockingError = ()
42
+
37
43
  logger = structlog.stdlib.get_logger(__name__)
38
44
 
39
45
 
@@ -110,7 +116,22 @@ async def worker(
110
116
 
111
117
  try:
112
118
  if attempt > BG_JOB_MAX_RETRIES:
113
- raise RuntimeError(f"Run {run['run_id']} exceeded max attempts")
119
+ await logger.aerror("Run exceeded max attempts", run_id=run["run_id"])
120
+
121
+ error_message = (
122
+ f"Run {run['run_id']} exceeded max attempts ({BG_JOB_MAX_RETRIES}).\n\n"
123
+ "This may happen if your code blocks the event loop with synchronous I/O bound calls (network requests, database queries, etc.).\n\n"
124
+ "If that is the case, your issues may be resolved by converting synchronous operations to async (e.g., use aiohttp instead of requests).\n\n"
125
+ )
126
+
127
+ if not BG_JOB_ISOLATED_LOOPS:
128
+ error_message += (
129
+ "Also consider setting BG_JOB_ISOLATED_LOOPS=true in your environment. This will isolate I/O-bound operations to avoid"
130
+ " blocking the main API server.\n\n"
131
+ "See: https://langchain-ai.github.io/langgraph/cloud/reference/env_var/#bg_job_isolated_loops\n\n"
132
+ )
133
+
134
+ raise RuntimeError(error_message)
114
135
  if temporary:
115
136
  stream = astream_state(
116
137
  AsyncExitStack(), conn, cast(Run, run), attempt, done
@@ -150,6 +171,7 @@ async def worker(
150
171
  run_started_at=run_started_at.isoformat(),
151
172
  run_ended_at=run_ended_at,
152
173
  run_exec_ms=ms(datetime.now(UTC), run_started_at),
174
+ graph_id=_get_graph_id(run),
153
175
  )
154
176
  await Runs.set_status(conn, run_id, "timeout")
155
177
  except UserRollback as e:
@@ -166,6 +188,7 @@ async def worker(
166
188
  run_started_at=run_started_at.isoformat(),
167
189
  run_ended_at=run_ended_at,
168
190
  run_exec_ms=ms(datetime.now(UTC), run_started_at),
191
+ graph_id=_get_graph_id(run),
169
192
  )
170
193
 
171
194
  except InFailedSqlTransaction as e:
@@ -203,6 +226,7 @@ async def worker(
203
226
  run_started_at=run_started_at.isoformat(),
204
227
  run_ended_at=run_ended_at,
205
228
  run_exec_ms=ms(datetime.now(UTC), run_started_at),
229
+ graph_id=_get_graph_id(run),
206
230
  )
207
231
  await Runs.set_status(conn, run_id, "interrupted")
208
232
  except RETRIABLE_EXCEPTIONS as e:
@@ -218,10 +242,28 @@ async def worker(
218
242
  run_started_at=run_started_at.isoformat(),
219
243
  run_ended_at=run_ended_at,
220
244
  run_exec_ms=ms(datetime.now(UTC), run_started_at),
245
+ graph_id=_get_graph_id(run),
221
246
  )
222
247
  await Runs.set_status(conn, run_id, "pending")
223
248
  raise
224
249
  except Exception as exc:
250
+ if isinstance(exc, BlockingError):
251
+ # Only occurs in the dev server
252
+ exc.add_note(
253
+ "Heads up! LangGraph identified a synchronous blocking call in your code. "
254
+ "When running in an ASGI web server, blocking calls can degrade performance for everyone since they tie up the event loop.\n\n"
255
+ "Here are your options to fix this:\n\n"
256
+ "1. Best approach: Convert any blocking code to use async/await patterns\n"
257
+ " For example, use 'await aiohttp.get()' instead of 'requests.get()'\n\n"
258
+ "2. Quick fix: Move blocking operations to a separate thread\n"
259
+ " Example: 'await asyncio.to_thread(your_blocking_function)'\n\n"
260
+ "3. Override (if you can't change the code):\n"
261
+ " - For development: Run 'langgraph dev --allow-blocking'\n"
262
+ " - For deployment: Set 'BG_JOB_ISOLATED_LOOPS=true' environment variable\n\n"
263
+ "These blocking operations can prevent health checks and slow down other runs in your deployment. "
264
+ "Following these recommendations will help keep your LangGraph application running smoothly!"
265
+ )
266
+
225
267
  exception = exc
226
268
  status = "error"
227
269
  run_ended_at = datetime.now(UTC).isoformat()
@@ -234,6 +276,7 @@ async def worker(
234
276
  run_started_at=run_started_at.isoformat(),
235
277
  run_ended_at=run_ended_at,
236
278
  run_exec_ms=ms(datetime.now(UTC), run_started_at),
279
+ graph_id=_get_graph_id(run),
237
280
  )
238
281
  await Runs.set_status(conn, run_id, "error")
239
282
  set_auth_ctx(None, None)
@@ -266,3 +309,11 @@ async def worker(
266
309
 
267
310
  def ms(after: datetime, before: datetime) -> int:
268
311
  return int((after - before).total_seconds() * 1000)
312
+
313
+
314
+ def _get_graph_id(run: Run) -> str | None:
315
+ try:
316
+ return run["kwargs"]["config"]["configurable"]["graph_id"]
317
+ except Exception:
318
+ logger.info(f"Failed to get graph_id from run {run['run_id']}")
319
+ return "Unknown"
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.3
2
2
  Name: langgraph-api
3
- Version: 0.0.38
3
+ Version: 0.0.40
4
4
  Summary:
5
5
  License: Elastic-2.0
6
6
  Author: Nuno Campos
@@ -10,6 +10,8 @@ Classifier: License :: Other/Proprietary License
10
10
  Classifier: Programming Language :: Python :: 3
11
11
  Classifier: Programming Language :: Python :: 3.11
12
12
  Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: Programming Language :: Python :: 3.13
14
+ Requires-Dist: blockbuster (>=1.5.24,<2.0.0)
13
15
  Requires-Dist: cloudpickle (>=3.0.0,<4.0.0)
14
16
  Requires-Dist: cryptography (>=43.0.3,<44.0.0)
15
17
  Requires-Dist: httpx (>=0.25.0)
@@ -1,6 +1,6 @@
1
1
  LICENSE,sha256=ZPwVR73Biwm3sK6vR54djCrhaRiM4cAD2zvOQZV8Xis,3859
2
2
  langgraph_api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
- langgraph_api/api/__init__.py,sha256=iDxgg0arqzZ0-roCSuWo3f8zXmKcl4fuU3Jw0-lqtZs,5447
3
+ langgraph_api/api/__init__.py,sha256=qNcg8QJydef0gM-vYJlxITMRZw-9r1vw8zqm2raqqYE,5493
4
4
  langgraph_api/api/assistants.py,sha256=nU6tnbgdr_6Utlq0A9nw2a6xxpUM_DNuCFI42_Kcs_o,14233
5
5
  langgraph_api/api/mcp.py,sha256=KbR19dtFCpJEiKYj3IfepAuJij8YZVELuVp7JY_yu_o,13754
6
6
  langgraph_api/api/meta.py,sha256=ifJ_Ki0Qf2DYbmY6OKlqKhLGxbt55gm0lEqH1A0cJbw,2790
@@ -18,23 +18,23 @@ langgraph_api/auth/langsmith/client.py,sha256=eKchvAom7hdkUXauD8vHNceBDDUijrFgdT
18
18
  langgraph_api/auth/middleware.py,sha256=jU8aDSIZHdzCGdifejRF7ndHkSjBtqIHcBwFIuUdHEA,1875
19
19
  langgraph_api/auth/noop.py,sha256=Bk6Nf3p8D_iMVy_OyfPlyiJp_aEwzL-sHrbxoXpCbac,586
20
20
  langgraph_api/auth/studio_user.py,sha256=FzFQRROKDlA9JjtBuwyZvk6Mbwno5M9RVYjDO6FU3F8,186
21
- langgraph_api/cli.py,sha256=76wJLx1ekaRmXR5DxGy1_RuA0lQq2jBUzHAAVUKV2Ho,12163
21
+ langgraph_api/cli.py,sha256=X76TYnErQPjRkZhK-n6gthCUmtItkwqVrRPvwZswF5Y,12335
22
22
  langgraph_api/command.py,sha256=3O9v3i0OPa96ARyJ_oJbLXkfO8rPgDhLCswgO9koTFA,768
23
- langgraph_api/config.py,sha256=MwTyJUr2wwZdUbL0gIvol_cCGi24Py9DvBnSozE1ai8,9342
23
+ langgraph_api/config.py,sha256=y-dmkmd929WDosIO0nUmcQicZN2XozJrVHzeBGtLhUg,9418
24
24
  langgraph_api/cron_scheduler.py,sha256=9yzbbGxzNgJdIg4ZT7yu2oTwT_wRuPxD1c2sbbd52xs,2630
25
25
  langgraph_api/errors.py,sha256=Bu_i5drgNTyJcLiyrwVE_6-XrSU50BHf9TDpttki9wQ,1690
26
- langgraph_api/graph.py,sha256=5Lo9FghptdPIFJoeTDkLjoIQLuNy49RWtAHJmzOCrZI,17209
26
+ langgraph_api/graph.py,sha256=jpMMMgn8uHqDc3gPu02tF7B8af0TV153t2F_kodcBvI,18799
27
27
  langgraph_api/http.py,sha256=gYbxxjY8aLnsXeJymcJ7G7Nj_yToOGpPYQqmZ1_ggfA,5240
28
28
  langgraph_api/js/.gitignore,sha256=l5yI6G_V6F1600I1IjiUKn87f4uYIrBAYU1MOyBBhg4,59
29
- langgraph_api/js/base.py,sha256=BpE8-xkUp8HFPRjSKx1tfUQubvoV4jYl6OwZdre3veI,209
30
- langgraph_api/js/build.mts,sha256=ArAEn74HHY_KXt0JCne76FTyGP34VQCHMe6OfPyxLM8,2915
31
- langgraph_api/js/client.mts,sha256=2dptAX8fMowV9OC4DU4khjpZUgALBLVBTu3jTQbeUJY,24394
29
+ langgraph_api/js/base.py,sha256=xkBp5bwRrbpMFaAMViEU-qIlnsJuu3X_G8sa1pqNZK0,227
30
+ langgraph_api/js/build.mts,sha256=PZGeFTOhmRIBxkbFaaUOpTacqg1Z7kUkZWTU2l9a7FY,3077
31
+ langgraph_api/js/client.mts,sha256=n6ecWwJBP2wp1jCaam55GxffH-YesVrV-7ZTml-k4Oc,26137
32
32
  langgraph_api/js/errors.py,sha256=Cm1TKWlUCwZReDC5AQ6SgNIVGD27Qov2xcgHyf8-GXo,361
33
- langgraph_api/js/global.d.ts,sha256=cLJRZfYVGmgQ6o_xFevVNNTIi918ZUdxVRnpLVSjiAY,133
34
- langgraph_api/js/package.json,sha256=j6DMoVgwRqWqTwdd7R1f-kvmiTUAbO3HaUhM8K64lbE,1224
35
- langgraph_api/js/remote.py,sha256=g0H2x3W7kejxswhgZfFaljzl4Y7agMCnO5BdNVj1rDY,23474
33
+ langgraph_api/js/global.d.ts,sha256=yDusqAyzVYhxfwqqcERUzucu2Pw9ma3-ug4DFyUvQfs,167
34
+ langgraph_api/js/package.json,sha256=uA_2KV86yMJmqfQd9Xe4w2PPjH6DmttBpSxi1PlSq3U,1224
35
+ langgraph_api/js/remote.py,sha256=jtzzkCBZ6eUE8g1JCn1lguaKF1RkpUmKbdblI6-NWgQ,24607
36
36
  langgraph_api/js/schema.py,sha256=7idnv7URlYUdSNMBXQcw7E4SxaPxCq_Oxwnlml8q5ik,408
37
- langgraph_api/js/src/graph.mts,sha256=mRyMUp03Fwd5DlmNIFl3RiUCQuJ5XwmFp1AfAeKDfVc,3169
37
+ langgraph_api/js/src/graph.mts,sha256=otgztTNzNJpeF2IOrpNuuwbSbpAy4eFE5dHtUd7eQwU,3742
38
38
  langgraph_api/js/src/hooks.mjs,sha256=XtktgmIHlls_DsknAuwib-z7TqCm0haRoTXvnkgzMuo,601
39
39
  langgraph_api/js/src/parser/parser.mts,sha256=c7WLEtMMwtVzir9O8VpJFVHKuLjtI86rde-In2lvaM0,13217
40
40
  langgraph_api/js/src/parser/parser.worker.mjs,sha256=2K6D0GlUmkk7LE39I8mryB8VZVE3-N9Cblji-ArPhFo,386
@@ -44,22 +44,23 @@ langgraph_api/js/src/utils/importMap.mts,sha256=pX4TGOyUpuuWF82kXcxcv3-8mgusRezO
44
44
  langgraph_api/js/src/utils/pythonSchemas.mts,sha256=98IW7Z_VP7L_CHNRMb3_MsiV3BgLE2JsWQY_PQcRR3o,685
45
45
  langgraph_api/js/src/utils/serde.mts,sha256=OuyyO9btvwWd55rU_H4x91dFEJiaPxL-lL9O6Zgo908,742
46
46
  langgraph_api/js/sse.py,sha256=lsfp4nyJyA1COmlKG9e2gJnTttf_HGCB5wyH8OZBER8,4105
47
- langgraph_api/js/tests/api.test.mts,sha256=RTJb9ZMX_IhqUPcShK61LZG9v_-xzdIZ0oEmOIjbJ4c,57709
48
- langgraph_api/js/tests/compose-postgres.yml,sha256=wV1Kws7WwUWVIudPkB--v58MOPL9hOcV0MUK-cvNrpA,1738
47
+ langgraph_api/js/tests/api.test.mts,sha256=GlCTOTuSGc2m2vdsw6PSZrDPgfcvJi0A5v4A9xGaZi4,60004
48
+ langgraph_api/js/tests/compose-postgres.yml,sha256=pV1dW6aCgTTJ1WHSDeCqlVgFE9PbyWW5WbwrsiJcgoA,1772
49
49
  langgraph_api/js/tests/graphs/.gitignore,sha256=26J8MarZNXh7snXD5eTpV3CPFTht5Znv8dtHYCLNfkw,12
50
50
  langgraph_api/js/tests/graphs/agent.css,sha256=QgcOC0W7IBsrg4pSqqpull-WTgtULZfx_lF_5ZxLdag,23
51
51
  langgraph_api/js/tests/graphs/agent.mts,sha256=E9WMv0alMv0njUEECqEsqoRk9NXJUgXW7SyQJ3GOZ8k,5396
52
52
  langgraph_api/js/tests/graphs/agent.ui.tsx,sha256=JDFJdpdIS6rglkXTaROSb1Is0j1kt5wN9ML8W4cuht8,175
53
53
  langgraph_api/js/tests/graphs/delay.mts,sha256=CFneKxqI4bGGK0lYjSbe80QirowPQlsRSuhDUKfydhk,703
54
+ langgraph_api/js/tests/graphs/dynamic.mts,sha256=Wf_-keF7lkEfp_iyI45nlFGCeU8ARLQ8axc0LXh7TyE,659
54
55
  langgraph_api/js/tests/graphs/error.mts,sha256=l4tk89449dj1BnEF_0ZcfPt0Ikk1gl8L1RaSnRfr3xo,487
55
- langgraph_api/js/tests/graphs/langgraph.json,sha256=frxd7ZWILdeMYSZgUBH6UO-IR7I2YJSOfOlx2mnO1sI,189
56
+ langgraph_api/js/tests/graphs/langgraph.json,sha256=iZL7XpAy3-QnCUHCRSj__Fxp3A-JPuYBJ_XQIxeyQfU,227
56
57
  langgraph_api/js/tests/graphs/nested.mts,sha256=4G7jSOSaFVQAza-_ARbK-Iai1biLlF2DIPDZXf7PLIY,1245
57
58
  langgraph_api/js/tests/graphs/package.json,sha256=Kv2kdlTNeWl00vYQAhngorQ6rLab4SMc7g1AgZslrHQ,118
58
59
  langgraph_api/js/tests/graphs/weather.mts,sha256=A7mLK3xW8h5B-ZyJNAyX2M2fJJwzPJzXs4DYesJwreQ,1655
59
60
  langgraph_api/js/tests/graphs/yarn.lock,sha256=i2AAIgXA3XBLM8-oU45wgUefCSG-Tne4ghWHmUCUKVk,10407
60
61
  langgraph_api/js/tests/parser.test.mts,sha256=dEC8KTqKygeb1u39ZvpPqCT4HtfPD947nLmITt2buxA,27883
61
- langgraph_api/js/tests/utils.mts,sha256=2kTybJ3O7Yfe1q3ehDouqV54ibXkNzsPZ_wBZLJvY-4,421
62
- langgraph_api/js/yarn.lock,sha256=W89dVYZMThcec08lJMcYnvEEnQK7VM5cPglvwpIdRv0,82773
62
+ langgraph_api/js/tests/utils.mts,sha256=q1V9gvT63v95onlfK9W4iv3n9ZJO3h-0RD9TdDYuRyY,439
63
+ langgraph_api/js/yarn.lock,sha256=JtMDtRVX9kr9lK0rbWRQsGHYbkQSmzNa4NwTunBv58U,82773
63
64
  langgraph_api/lifespan.py,sha256=0F4Xn6IC4wUfvBWbm6KCJ-jiF0S7iWDOHJYJZ-A3o3s,2739
64
65
  langgraph_api/logging.py,sha256=JJIzbNIgLCN6ClQ3tA-Mm5ffuBGvpRDSZsEvnIlsuu4,3693
65
66
  langgraph_api/metadata.py,sha256=5Mu3MUtUc-iIocU3X2SZDoGIqnUmTdT3517MhP94npI,3495
@@ -81,23 +82,23 @@ langgraph_api/thread_ttl.py,sha256=ubkWMymTR7p9nGWd60-WKEOQ20ZgIkWB6WGstQUmRS4,1
81
82
  langgraph_api/utils.py,sha256=92mSti9GfGdMRRWyESKQW5yV-75Z9icGHnIrBYvdypU,3619
82
83
  langgraph_api/validation.py,sha256=Qo3EkSRXzIRe3GRuqRWbElTcUXRMXMyA1w0VbMvdwME,4934
83
84
  langgraph_api/webhook.py,sha256=1ncwO0rIZcj-Df9sxSnFEzd1gP1bfS4okeZQS8NSRoE,1382
84
- langgraph_api/worker.py,sha256=zlkk2yg1tOBQHYwN-WHjc9NJgd3CAvsl8ET0-7tqQIk,9838
85
+ langgraph_api/worker.py,sha256=CLmohk3B03OTsKHq_ZPhujMHFSuc694qeEL4VNSxHvg,12679
85
86
  langgraph_license/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
86
87
  langgraph_license/middleware.py,sha256=_ODIYzQkymr6W9_Fp9wtf1kAQspnpsmr53xuzyF2GA0,612
87
88
  langgraph_license/validation.py,sha256=Uu_G8UGO_WTlLsBEY0gTVWjRR4czYGfw5YAD3HLZoj0,203
88
89
  langgraph_storage/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
89
90
  langgraph_storage/checkpoint.py,sha256=Qq0y6vdh27qdF3h5nOLT5CcX9Rj2bcFqkVOMeCaGoK4,4036
90
- langgraph_storage/database.py,sha256=I0AgFeJ-NSTT34vxKxQBUf1z2syFP0S8QpKCqTixrzY,5652
91
- langgraph_storage/inmem_stream.py,sha256=8bxkILIuFpr7P7RQ37SQAxrpRKvmbHdRB_nbfFiomlk,3263
92
- langgraph_storage/ops.py,sha256=S0QHbnquEXvAM63NQMgqtyTjm-8nlq0pOvgJgFD38Z8,75312
93
- langgraph_storage/queue.py,sha256=UDgsUTtUMfBSRDrQ8Onis-FJO4n7KTsX6sdpbY8Hs0A,5055
91
+ langgraph_storage/database.py,sha256=ys2glLtQaspfIY-_EgqEOT3b7a5WqBwuE_9I5VAh6FM,5821
92
+ langgraph_storage/inmem_stream.py,sha256=LjJSAxsh_E0ywqEMzdWJk8Hy_Jn9oQByzycss-fANng,3264
93
+ langgraph_storage/ops.py,sha256=6NxeirfuDAKghHc328zejnbTsijg6U80Rlt5rXTC2dc,75392
94
+ langgraph_storage/queue.py,sha256=wyBcY1V_8zah0Z2e5hpovs_dsIPPPfUJQCly2rxFWvo,7039
94
95
  langgraph_storage/retry.py,sha256=XmldOP4e_H5s264CagJRVnQMDFcEJR_dldVR1Hm5XvM,763
95
96
  langgraph_storage/store.py,sha256=JB9jZ87GE19MVN9wgl3-esgR2eIkeipws9q6qsPWkgc,3399
96
97
  langgraph_storage/ttl_dict.py,sha256=FlpEY8EANeXWKo_G5nmIotPquABZGyIJyk6HD9u6vqY,1533
97
98
  logging.json,sha256=3RNjSADZmDq38eHePMm1CbP6qZ71AmpBtLwCmKU9Zgo,379
98
99
  openapi.json,sha256=-25y3NRQ88e_944UXo76Goa34HJhC7pj6I9tjYUwvuE,131492
99
- langgraph_api-0.0.38.dist-info/LICENSE,sha256=ZPwVR73Biwm3sK6vR54djCrhaRiM4cAD2zvOQZV8Xis,3859
100
- langgraph_api-0.0.38.dist-info/METADATA,sha256=kv_c0bBL9Vp6pM0NQOUv1uyruDkf5K67X74nSICrekw,4071
101
- langgraph_api-0.0.38.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
102
- langgraph_api-0.0.38.dist-info/entry_points.txt,sha256=3EYLgj89DfzqJHHYGxPH4A_fEtClvlRbWRUHaXO7hj4,77
103
- langgraph_api-0.0.38.dist-info/RECORD,,
100
+ langgraph_api-0.0.40.dist-info/LICENSE,sha256=ZPwVR73Biwm3sK6vR54djCrhaRiM4cAD2zvOQZV8Xis,3859
101
+ langgraph_api-0.0.40.dist-info/METADATA,sha256=CsN_X-9ths2igUx9E7DdiRHbrqRUFgBa7TnlqHDjTC0,4167
102
+ langgraph_api-0.0.40.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
103
+ langgraph_api-0.0.40.dist-info/entry_points.txt,sha256=3EYLgj89DfzqJHHYGxPH4A_fEtClvlRbWRUHaXO7hj4,77
104
+ langgraph_api-0.0.40.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 1.9.0
2
+ Generator: poetry-core 2.1.2
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -161,12 +161,12 @@ async def start_pool() -> None:
161
161
  "\n - Pulled updates that modified class definitions in a way that's incompatible with the cache"
162
162
  "\n\nRemoving invalid cache data stored at path: .langgraph_api"
163
163
  )
164
- os.remove(OPS_FILENAME)
165
- os.remove(RETRY_COUNTER_FILENAME)
164
+ await asyncio.to_thread(os.remove, OPS_FILENAME)
165
+ await asyncio.to_thread(os.remove, RETRY_COUNTER_FILENAME)
166
166
  except Exception as e:
167
167
  logger.error("Failed to load cached data: %s", str(e))
168
- os.remove(OPS_FILENAME)
169
- os.remove(RETRY_COUNTER_FILENAME)
168
+ await asyncio.to_thread(os.remove, OPS_FILENAME)
169
+ await asyncio.to_thread(os.remove, RETRY_COUNTER_FILENAME)
170
170
  for k in ["runs", "threads", "assistants", "assistant_versions"]:
171
171
  if not GLOBAL_STORE.get(k):
172
172
  GLOBAL_STORE[k] = []
@@ -177,12 +177,12 @@ async def start_pool() -> None:
177
177
 
178
178
 
179
179
  async def stop_pool() -> None:
180
- GLOBAL_STORE.close()
181
- GLOBAL_RETRY_COUNTER.close()
180
+ await asyncio.to_thread(GLOBAL_STORE.close)
181
+ await asyncio.to_thread(GLOBAL_RETRY_COUNTER.close)
182
182
  from langgraph_storage.checkpoint import Checkpointer
183
183
  from langgraph_storage.store import STORE
184
184
 
185
- STORE.close()
185
+ await asyncio.to_thread(STORE.close)
186
186
 
187
187
  async with Checkpointer():
188
188
  pass
@@ -92,6 +92,7 @@ async def stop_stream() -> None:
92
92
  # Send 'done' message to all active queues before clearing
93
93
  for run_id in list(stream_manager.queues.keys()):
94
94
  control_message = Message(topic=f"run:{run_id}:control".encode(), data=b"done")
95
+
95
96
  for queue in stream_manager.queues[run_id]:
96
97
  try:
97
98
  await queue.put(control_message)
langgraph_storage/ops.py CHANGED
@@ -985,7 +985,9 @@ class Threads(Authenticated):
985
985
  ctx: Auth.types.BaseAuthContext | None = None,
986
986
  ) -> StateSnapshot:
987
987
  """Get state for a thread."""
988
- checkpointer = Checkpointer(conn, unpack_hook=_msgpack_ext_hook_to_json)
988
+ checkpointer = await asyncio.to_thread(
989
+ Checkpointer, conn, unpack_hook=_msgpack_ext_hook_to_json
990
+ )
989
991
  thread_id = _ensure_uuid(config["configurable"]["thread_id"])
990
992
  # Auth will be applied here so no need to use filters downstream
991
993
  thread_iter = await Threads.get(conn, thread_id, ctx=ctx)
@@ -1206,8 +1208,8 @@ class Threads(Authenticated):
1206
1208
  async with get_graph(
1207
1209
  graph_id,
1208
1210
  thread_config,
1209
- checkpointer=Checkpointer(
1210
- conn, unpack_hook=_msgpack_ext_hook_to_json
1211
+ checkpointer=await asyncio.to_thread(
1212
+ Checkpointer, conn, unpack_hook=_msgpack_ext_hook_to_json
1211
1213
  ),
1212
1214
  ) as graph:
1213
1215
  # Convert before parameter if it's a string
@@ -1,6 +1,9 @@
1
1
  import asyncio
2
+ import os
2
3
 
3
4
  import structlog
5
+ from blockbuster import BlockBuster
6
+ from langsmith import env as ls_env
4
7
 
5
8
  from langgraph_api.config import (
6
9
  BG_JOB_HEARTBEAT,
@@ -27,6 +30,20 @@ async def queue():
27
30
  last_sweep_secs: int | None = None
28
31
  semaphore = asyncio.Semaphore(concurrency)
29
32
  WEBHOOKS: set[asyncio.Task] = set()
33
+ enable_blocking = os.getenv("LANGGRAPH_ALLOW_BLOCKING", "false").lower() == "true"
34
+
35
+ # raise exceptions when a blocking call is detected inside an async function
36
+ if enable_blocking:
37
+ bb = None
38
+ await logger.awarning(
39
+ "Heads up: You've set --allow-blocking, which allows synchronous blocking I/O operations."
40
+ " Be aware that blocking code in one run may tie up the shared event loop"
41
+ " and slow down ALL other server operations. For best performance, either convert blocking"
42
+ " code to async patterns or set BG_JOB_ISOLATED_LOOPS=true in production"
43
+ " to isolate each run in its own event loop."
44
+ )
45
+ else:
46
+ bb = _enable_blockbuster()
30
47
 
31
48
  def cleanup(task: asyncio.Task):
32
49
  WORKERS.remove(task)
@@ -120,6 +137,8 @@ async def queue():
120
137
  semaphore.release()
121
138
  await exit.aclose()
122
139
  finally:
140
+ if bb:
141
+ bb.deactivate()
123
142
  logger.info("Shutting down background workers")
124
143
  for task in WORKERS:
125
144
  task.cancel()
@@ -129,3 +148,28 @@ async def queue():
129
148
  asyncio.gather(*WORKERS, *WEBHOOKS, return_exceptions=True),
130
149
  SHUTDOWN_GRACE_PERIOD_SECS,
131
150
  )
151
+
152
+
153
+ def _enable_blockbuster() -> BlockBuster:
154
+ ls_env.get_runtime_environment() # this gets cached
155
+ bb = BlockBuster(excluded_modules=["logging"])
156
+ bb.activate()
157
+ # Note, we've cached this call in langsmith==0.3.21 so it shouldn't raise anyway
158
+ # but we don't want to raise teh minbound just for that.
159
+ for func in ["os.stat", "io.TextIOWrapper.read"]:
160
+ bb.functions[func].can_block_in("langsmith/client.py", "_default_retry_config")
161
+ # Only triggers in python 3.11 for getting subgraphs
162
+ # Will be unnecessary once we cache the assistant schemas
163
+ bb.functions[func].can_block_in(
164
+ "langgraph/pregel/utils.py", "get_function_nonlocals"
165
+ )
166
+ for module, func in [
167
+ ("uvicorn/lifespan/on.py", "startup"),
168
+ ("uvicorn/lifespan/on.py", "shutdown"),
169
+ ("ansitowin32.py", "write_plain_text"),
170
+ ("logging/__init__.py", "flush"),
171
+ ("logging/__init__.py", "emit"),
172
+ ]:
173
+ bb.functions["io.TextIOWrapper.write"].can_block_in(module, func)
174
+ bb.functions["io.BufferedWriter.write"].can_block_in(module, func)
175
+ return bb