langgraph-api 0.2.32__tar.gz → 0.2.35__tar.gz

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 (92) hide show
  1. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/Makefile +3 -0
  2. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/PKG-INFO +2 -2
  3. langgraph_api-0.2.35/langgraph_api/__init__.py +1 -0
  4. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/langgraph_api/api/meta.py +2 -2
  5. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/langgraph_api/api/runs.py +29 -9
  6. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/langgraph_api/graph.py +7 -7
  7. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/langgraph_api/js/package.json +1 -1
  8. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/langgraph_api/js/yarn.lock +4 -4
  9. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/langgraph_api/models/run.py +3 -0
  10. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/langgraph_api/sse.py +12 -4
  11. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/langgraph_api/stream.py +10 -3
  12. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/langgraph_api/worker.py +5 -2
  13. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/openapi.json +129 -59
  14. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/pyproject.toml +1 -1
  15. langgraph_api-0.2.32/langgraph_api/__init__.py +0 -1
  16. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/.gitignore +0 -0
  17. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/LICENSE +0 -0
  18. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/README.md +0 -0
  19. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/benchmark/weather.js +0 -0
  20. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/constraints.txt +0 -0
  21. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/forbidden.txt +0 -0
  22. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/healthcheck.py +0 -0
  23. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/langgraph_api/api/__init__.py +0 -0
  24. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/langgraph_api/api/assistants.py +0 -0
  25. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/langgraph_api/api/mcp.py +0 -0
  26. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/langgraph_api/api/openapi.py +0 -0
  27. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/langgraph_api/api/store.py +0 -0
  28. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/langgraph_api/api/threads.py +0 -0
  29. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/langgraph_api/api/ui.py +0 -0
  30. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/langgraph_api/asgi_transport.py +0 -0
  31. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/langgraph_api/asyncio.py +0 -0
  32. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/langgraph_api/auth/__init__.py +0 -0
  33. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/langgraph_api/auth/custom.py +0 -0
  34. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/langgraph_api/auth/langsmith/__init__.py +0 -0
  35. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/langgraph_api/auth/langsmith/backend.py +0 -0
  36. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/langgraph_api/auth/langsmith/client.py +0 -0
  37. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/langgraph_api/auth/middleware.py +0 -0
  38. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/langgraph_api/auth/noop.py +0 -0
  39. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/langgraph_api/auth/studio_user.py +0 -0
  40. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/langgraph_api/cli.py +0 -0
  41. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/langgraph_api/command.py +0 -0
  42. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/langgraph_api/config.py +0 -0
  43. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/langgraph_api/cron_scheduler.py +0 -0
  44. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/langgraph_api/errors.py +0 -0
  45. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/langgraph_api/http.py +0 -0
  46. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/langgraph_api/js/.gitignore +0 -0
  47. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/langgraph_api/js/.prettierrc +0 -0
  48. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/langgraph_api/js/__init__.py +0 -0
  49. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/langgraph_api/js/base.py +0 -0
  50. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/langgraph_api/js/build.mts +0 -0
  51. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/langgraph_api/js/client.http.mts +0 -0
  52. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/langgraph_api/js/client.mts +0 -0
  53. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/langgraph_api/js/errors.py +0 -0
  54. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/langgraph_api/js/global.d.ts +0 -0
  55. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/langgraph_api/js/remote.py +0 -0
  56. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/langgraph_api/js/schema.py +0 -0
  57. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/langgraph_api/js/src/graph.mts +0 -0
  58. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/langgraph_api/js/src/load.hooks.mjs +0 -0
  59. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/langgraph_api/js/src/preload.mjs +0 -0
  60. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/langgraph_api/js/src/utils/files.mts +0 -0
  61. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/langgraph_api/js/src/utils/importMap.mts +0 -0
  62. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/langgraph_api/js/src/utils/pythonSchemas.mts +0 -0
  63. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/langgraph_api/js/src/utils/serde.mts +0 -0
  64. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/langgraph_api/js/sse.py +0 -0
  65. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/langgraph_api/js/tsconfig.json +0 -0
  66. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/langgraph_api/js/ui.py +0 -0
  67. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/langgraph_api/logging.py +0 -0
  68. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/langgraph_api/metadata.py +0 -0
  69. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/langgraph_api/middleware/__init__.py +0 -0
  70. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/langgraph_api/middleware/http_logger.py +0 -0
  71. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/langgraph_api/middleware/private_network.py +0 -0
  72. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/langgraph_api/middleware/request_id.py +0 -0
  73. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/langgraph_api/models/__init__.py +0 -0
  74. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/langgraph_api/patch.py +0 -0
  75. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/langgraph_api/queue_entrypoint.py +0 -0
  76. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/langgraph_api/route.py +0 -0
  77. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/langgraph_api/schema.py +0 -0
  78. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/langgraph_api/serde.py +0 -0
  79. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/langgraph_api/server.py +0 -0
  80. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/langgraph_api/state.py +0 -0
  81. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/langgraph_api/store.py +0 -0
  82. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/langgraph_api/thread_ttl.py +0 -0
  83. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/langgraph_api/tunneling/cloudflare.py +0 -0
  84. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/langgraph_api/utils.py +0 -0
  85. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/langgraph_api/validation.py +0 -0
  86. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/langgraph_api/webhook.py +0 -0
  87. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/langgraph_license/__init__.py +0 -0
  88. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/langgraph_license/validation.py +0 -0
  89. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/langgraph_runtime/__init__.py +0 -0
  90. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/logging.json +0 -0
  91. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/scripts/create_license.py +0 -0
  92. {langgraph_api-0.2.32 → langgraph_api-0.2.35}/uv.lock +0 -0
@@ -67,6 +67,7 @@ start:
67
67
  --reload \
68
68
  --port 9123 \
69
69
  --reload-dir langgraph_api \
70
+ --reload-dir ../runtime_inmem \
70
71
  --no-access-log
71
72
 
72
73
 
@@ -89,6 +90,7 @@ start-auth-jwt:
89
90
  --reload \
90
91
  --port 9123 \
91
92
  --reload-dir langgraph_api \
93
+ --reload-dir ../runtime_inmem \
92
94
  --no-access-log
93
95
 
94
96
  start-auth-fastapi-jwt:
@@ -106,6 +108,7 @@ start-auth-fastapi-jwt:
106
108
  --reload \
107
109
  --port 9123 \
108
110
  --reload-dir langgraph_api \
111
+ --reload-dir ../runtime_inmem \
109
112
  --no-access-log
110
113
 
111
114
  VERSION_KIND ?= patch
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: langgraph-api
3
- Version: 0.2.32
3
+ Version: 0.2.35
4
4
  Author-email: Nuno Campos <nuno@langchain.dev>, Will Fu-Hinthorn <will@langchain.dev>
5
5
  License: Elastic-2.0
6
6
  License-File: LICENSE
@@ -11,7 +11,7 @@ Requires-Dist: httpx>=0.25.0
11
11
  Requires-Dist: jsonschema-rs<0.30,>=0.20.0
12
12
  Requires-Dist: langchain-core>=0.2.38
13
13
  Requires-Dist: langgraph-checkpoint>=2.0.23
14
- Requires-Dist: langgraph-runtime-inmem<0.2,>=0.1.0
14
+ Requires-Dist: langgraph-runtime-inmem<0.3,>=0.2.0
15
15
  Requires-Dist: langgraph-sdk>=0.1.66
16
16
  Requires-Dist: langgraph>=0.3.27
17
17
  Requires-Dist: langsmith>=0.1.112
@@ -0,0 +1 @@
1
+ __version__ = "0.2.35"
@@ -54,10 +54,10 @@ async def meta_metrics(request: ApiRequest):
54
54
  }
55
55
  )
56
56
  elif format == "prometheus":
57
- # LANGSMITH_HOST_PROJECT_ID and HOSTED_LANGSERVE_REVISION_ID are injected
57
+ # LANGSMITH_HOST_PROJECT_ID and LANGSMITH_HOST_REVISION_ID are injected
58
58
  # into the deployed image by host-backend.
59
59
  project_id = os.getenv("LANGSMITH_HOST_PROJECT_ID")
60
- revision_id = os.getenv("HOSTED_LANGSERVE_REVISION_ID")
60
+ revision_id = os.getenv("LANGSMITH_HOST_REVISION_ID")
61
61
 
62
62
  metrics = [
63
63
  "# HELP lg_api_workers_max The maximum number of workers available.",
@@ -39,7 +39,10 @@ async def create_run(request: ApiRequest):
39
39
  payload,
40
40
  request.headers,
41
41
  )
42
- return ApiResponse(run)
42
+ return ApiResponse(
43
+ run,
44
+ headers={"Content-Location": f"/threads/{thread_id}/runs/{run['run_id']}"},
45
+ )
43
46
 
44
47
 
45
48
  @retry_db
@@ -53,7 +56,10 @@ async def create_stateless_run(request: ApiRequest):
53
56
  payload,
54
57
  request.headers,
55
58
  )
56
- return ApiResponse(run)
59
+ return ApiResponse(
60
+ run,
61
+ headers={"Content-Location": f"/runs/{run['run_id']}"},
62
+ )
57
63
 
58
64
 
59
65
  async def create_stateless_run_batch(request: ApiRequest):
@@ -107,8 +113,12 @@ async def stream_run(
107
113
  thread_id=thread_id,
108
114
  cancel_on_disconnect=on_disconnect == "cancel",
109
115
  stream_mode=await sub,
116
+ last_event_id=None,
110
117
  ),
111
- headers={"Location": f"/threads/{thread_id}/runs/{run['run_id']}/stream"},
118
+ headers={
119
+ "Location": f"/threads/{thread_id}/runs/{run['run_id']}/stream",
120
+ "Content-Location": f"/threads/{thread_id}/runs/{run['run_id']}",
121
+ },
112
122
  )
113
123
 
114
124
 
@@ -143,9 +153,11 @@ async def stream_run_stateless(
143
153
  ignore_404=True,
144
154
  cancel_on_disconnect=on_disconnect == "cancel",
145
155
  stream_mode=await sub,
156
+ last_event_id=None,
146
157
  ),
147
158
  headers={
148
- "Location": f"/threads/{run['thread_id']}/runs/{run['run_id']}/stream"
159
+ "Location": f"/runs/{run['run_id']}/stream",
160
+ "Content-Location": f"/runs/{run['run_id']}",
149
161
  },
150
162
  )
151
163
 
@@ -182,7 +194,7 @@ async def wait_run(request: ApiRequest):
182
194
  run["run_id"], thread_id=run["thread_id"], stream_mode=await sub
183
195
  )
184
196
  ) as stream:
185
- async for mode, chunk in stream:
197
+ async for mode, chunk, _ in stream:
186
198
  if mode == b"values":
187
199
  vchunk = chunk
188
200
  elif mode == b"error":
@@ -216,7 +228,10 @@ async def wait_run(request: ApiRequest):
216
228
  return StreamingResponse(
217
229
  body(),
218
230
  media_type="application/json",
219
- headers={"Location": f"/threads/{thread_id}/runs/{run['run_id']}/join"},
231
+ headers={
232
+ "Location": f"/threads/{thread_id}/runs/{run['run_id']}/join",
233
+ "Content-Location": f"/threads/{thread_id}/runs/{run['run_id']}",
234
+ },
220
235
  )
221
236
 
222
237
 
@@ -251,7 +266,7 @@ async def wait_run_stateless(request: ApiRequest):
251
266
  run["run_id"], thread_id=run["thread_id"], stream_mode=await sub
252
267
  )
253
268
  ) as stream:
254
- async for mode, chunk in stream:
269
+ async for mode, chunk, _ in stream:
255
270
  if mode == b"values":
256
271
  vchunk = chunk
257
272
  elif mode == b"error":
@@ -280,7 +295,10 @@ async def wait_run_stateless(request: ApiRequest):
280
295
  return StreamingResponse(
281
296
  body(),
282
297
  media_type="application/json",
283
- headers={"Location": f"/threads/{run['thread_id']}/runs/{run['run_id']}/join"},
298
+ headers={
299
+ "Location": f"/threads/{run['thread_id']}/runs/{run['run_id']}/join",
300
+ "Content-Location": f"/threads/{run['thread_id']}/runs/{run['run_id']}",
301
+ },
284
302
  )
285
303
 
286
304
 
@@ -358,13 +376,15 @@ async def join_run_stream(request: ApiRequest):
358
376
  validate_uuid(thread_id, "Invalid thread ID: must be a UUID")
359
377
  validate_uuid(run_id, "Invalid run ID: must be a UUID")
360
378
  stream_mode = request.query_params.get("stream_mode") or None
379
+ last_event_id = request.headers.get("last-event-id") or None
361
380
  return EventSourceResponse(
362
381
  Runs.Stream.join(
363
382
  run_id,
364
383
  thread_id=thread_id,
365
384
  cancel_on_disconnect=cancel_on_disconnect,
366
385
  stream_mode=stream_mode,
367
- )
386
+ last_event_id=last_event_id,
387
+ ),
368
388
  )
369
389
 
370
390
 
@@ -18,7 +18,7 @@ import structlog
18
18
  from langchain_core.runnables.config import run_in_executor, var_child_runnable_config
19
19
  from langgraph.checkpoint.base import BaseCheckpointSaver
20
20
  from langgraph.constants import CONFIG_KEY_CHECKPOINTER, CONFIG_KEY_STORE
21
- from langgraph.graph import Graph
21
+ from langgraph.graph import StateGraph
22
22
  from langgraph.pregel import Pregel
23
23
  from langgraph.store.base import BaseStore
24
24
  from langgraph.utils.config import ensure_config
@@ -34,8 +34,8 @@ if TYPE_CHECKING:
34
34
 
35
35
  logger = structlog.stdlib.get_logger(__name__)
36
36
 
37
- GraphFactoryFromConfig = Callable[[Config], Pregel | Graph]
38
- GraphFactory = Callable[[], Pregel | Graph]
37
+ GraphFactoryFromConfig = Callable[[Config], Pregel | StateGraph]
38
+ GraphFactory = Callable[[], Pregel | StateGraph]
39
39
  GraphValue = Pregel | GraphFactory
40
40
 
41
41
 
@@ -130,7 +130,7 @@ async def get_graph(
130
130
  value = value(config) if FACTORY_ACCEPTS_CONFIG[graph_id] else value()
131
131
  try:
132
132
  async with _generate_graph(value) as graph_obj:
133
- if isinstance(graph_obj, Graph):
133
+ if isinstance(graph_obj, StateGraph):
134
134
  graph_obj = graph_obj.compile()
135
135
  if not isinstance(graph_obj, Pregel | BaseRemotePregel):
136
136
  raise HTTPException(
@@ -442,7 +442,7 @@ def _graph_from_spec(spec: GraphSpec) -> GraphValue:
442
442
  likely = [
443
443
  k
444
444
  for k in available
445
- if isinstance(module.__dict__[k], Graph | Pregel)
445
+ if isinstance(module.__dict__[k], StateGraph | Pregel)
446
446
  ]
447
447
  if likely:
448
448
  prefix = spec.module or spec.path
@@ -471,7 +471,7 @@ def _graph_from_spec(spec: GraphSpec) -> GraphValue:
471
471
  raise ValueError(
472
472
  f"Graph factory function '{spec.variable}' in module '{spec.path}' must take exactly one argument, a RunnableConfig"
473
473
  )
474
- elif isinstance(graph, Graph):
474
+ elif isinstance(graph, StateGraph):
475
475
  graph = graph.compile()
476
476
  elif isinstance(graph, Pregel):
477
477
  # We don't want to fail real deployments, but this will help folks catch unnecessary custom components
@@ -513,7 +513,7 @@ def _graph_from_spec(spec: GraphSpec) -> GraphValue:
513
513
  break
514
514
  else:
515
515
  for _, member in inspect.getmembers(module):
516
- if isinstance(member, Graph):
516
+ if isinstance(member, StateGraph):
517
517
  graph = member.compile()
518
518
  break
519
519
  else:
@@ -33,7 +33,7 @@
33
33
  },
34
34
  "devDependencies": {
35
35
  "jose": "^6.0.10",
36
- "@langchain/langgraph-sdk": "^0.0.73",
36
+ "@langchain/langgraph-sdk": "^0.0.77",
37
37
  "@types/react": "^19.0.8",
38
38
  "@types/react-dom": "^19.0.3",
39
39
  "@types/node": "^22.2.0",
@@ -236,10 +236,10 @@
236
236
  dependencies:
237
237
  uuid "^10.0.0"
238
238
 
239
- "@langchain/langgraph-sdk@^0.0.73":
240
- version "0.0.73"
241
- resolved "https://registry.yarnpkg.com/@langchain/langgraph-sdk/-/langgraph-sdk-0.0.73.tgz#6f5cb44fb306461182df55be4cf1d263c5ce3e16"
242
- integrity sha512-V3E7Bd1KNcTpnEglZqKpnQtth62WHf+Dxq5V+0CqMxbzPHnW9I4eynCO4c1/HSY/rHgEtvgj7fMvCAb6I5R+lQ==
239
+ "@langchain/langgraph-sdk@^0.0.77":
240
+ version "0.0.77"
241
+ resolved "https://registry.yarnpkg.com/@langchain/langgraph-sdk/-/langgraph-sdk-0.0.77.tgz#adcd978843e61bbf09b9625d519dda16d27cec09"
242
+ integrity sha512-DMCONENhhaMS+Buw8s2UkKjAa9I6cT1aVJEDOmTO2lpon4Dqz/jiYUVJK7pTlNVSNvSx0E8aOmtT7NgGBcWflg==
243
243
  dependencies:
244
244
  "@types/json-schema" "^7.0.15"
245
245
  p-queue "^6.6.2"
@@ -77,6 +77,8 @@ class RunCreateDict(TypedDict):
77
77
  """
78
78
  stream_subgraphs: bool | None
79
79
  """Stream output from subgraphs. By default, streams only the top graph."""
80
+ stream_resumable: bool | None
81
+ """Whether to persist the stream chunks in order to resume the stream later."""
80
82
  feedback_keys: list[str] | None
81
83
  """Pass one or more feedback_keys if you want to request short-lived signed URLs
82
84
  for submitting feedback to LangSmith with this key for this run."""
@@ -305,6 +307,7 @@ async def create_valid_run(
305
307
  "feedback_keys": payload.get("feedback_keys"),
306
308
  "temporary": temporary,
307
309
  "subgraphs": payload.get("stream_subgraphs", False),
310
+ "resumable": payload.get("stream_resumable", False),
308
311
  "checkpoint_during": payload.get("checkpoint_during", True),
309
312
  },
310
313
  metadata=payload.get("metadata"),
@@ -18,7 +18,9 @@ logger = structlog.stdlib.get_logger(__name__)
18
18
  class EventSourceResponse(sse_starlette.EventSourceResponse):
19
19
  def __init__(
20
20
  self,
21
- content: AsyncIterator[bytes | tuple[bytes, Any | bytes]],
21
+ content: AsyncIterator[
22
+ bytes | tuple[bytes, Any | bytes] | tuple[bytes, Any | bytes, bytes | None]
23
+ ],
22
24
  status_code: int = 200,
23
25
  headers: Mapping[str, str] | None = None,
24
26
  ) -> None:
@@ -101,11 +103,12 @@ async def sse_heartbeat(send: Send) -> None:
101
103
  SEP = b"\r\n"
102
104
  EVENT = b"event: "
103
105
  DATA = b"data: "
106
+ ID = b"id: "
104
107
  BYTES_LIKE = (bytes, bytearray, memoryview)
105
108
 
106
109
 
107
- def json_to_sse(event: bytes, data: Any | bytes) -> bytes:
108
- return b"".join(
110
+ def json_to_sse(event: bytes, data: Any | bytes, id: bytes | None = None) -> bytes:
111
+ result = b"".join(
109
112
  (
110
113
  EVENT,
111
114
  event,
@@ -113,6 +116,11 @@ def json_to_sse(event: bytes, data: Any | bytes) -> bytes:
113
116
  DATA,
114
117
  data if isinstance(data, BYTES_LIKE) else json_dumpb(data),
115
118
  SEP,
116
- SEP,
117
119
  )
118
120
  )
121
+
122
+ if id is not None:
123
+ result += b"".join((ID, id, SEP))
124
+
125
+ result += SEP
126
+ return result
@@ -87,6 +87,7 @@ async def astream_state(
87
87
  # extract args from run
88
88
  kwargs = run["kwargs"].copy()
89
89
  kwargs.pop("webhook", None)
90
+ kwargs.pop("resumable", False)
90
91
  subgraphs = kwargs.get("subgraphs", False)
91
92
  temporary = kwargs.pop("temporary", False)
92
93
  config = kwargs.pop("config")
@@ -278,19 +279,25 @@ async def astream_state(
278
279
  yield "feedback", feedback_urls
279
280
 
280
281
 
281
- async def consume(stream: AnyStream, run_id: str) -> None:
282
+ async def consume(stream: AnyStream, run_id: str, resumable: bool = False) -> None:
282
283
  async with aclosing(stream):
283
284
  try:
284
285
  async for mode, payload in stream:
285
286
  await Runs.Stream.publish(
286
- run_id, mode, await run_in_executor(None, json_dumpb, payload)
287
+ run_id,
288
+ mode,
289
+ await run_in_executor(None, json_dumpb, payload),
290
+ resumable=resumable,
287
291
  )
288
292
  except Exception as e:
289
293
  g = e
290
294
  if isinstance(e, ExceptionGroup):
291
295
  e = e.exceptions[0]
292
296
  await Runs.Stream.publish(
293
- run_id, "error", await run_in_executor(None, json_dumpb, e)
297
+ run_id,
298
+ "error",
299
+ await run_in_executor(None, json_dumpb, e),
300
+ resumable=resumable,
294
301
  )
295
302
  raise e from g
296
303
 
@@ -83,8 +83,8 @@ async def worker(
83
83
  Runs.enter(run_id, main_loop) as done,
84
84
  ):
85
85
  temporary = run["kwargs"].get("temporary", False)
86
+ resumable = run["kwargs"].get("resumable", False)
86
87
  run_created_at = run["created_at"].isoformat()
87
- run["kwargs"]
88
88
  lg_logging.set_logging_context(
89
89
  {
90
90
  "run_id": str(run_id),
@@ -145,7 +145,10 @@ async def worker(
145
145
  on_checkpoint=on_checkpoint,
146
146
  on_task_result=on_task_result,
147
147
  )
148
- await asyncio.wait_for(consume(stream, run_id), BG_JOB_TIMEOUT_SECS)
148
+ await asyncio.wait_for(
149
+ consume(stream, run_id, resumable),
150
+ BG_JOB_TIMEOUT_SECS,
151
+ )
149
152
  run_ended_at = datetime.now(UTC).isoformat()
150
153
  await logger.ainfo(
151
154
  "Background run succeeded",
@@ -886,73 +886,73 @@
886
886
  }
887
887
  }
888
888
  },
889
- "/threads/{thread_id}/state/{checkpoint_id}": {
890
- "get": {
891
- "tags": ["Threads"],
892
- "summary": "Get Thread State At Checkpoint",
893
- "description": "Get state for a thread at a specific checkpoint.",
894
- "operationId": "get_thread_state_at_checkpoint_threads__thread_id__state__checkpoint_id__get",
895
- "parameters": [
896
- {
897
- "description": "The ID of the thread.",
898
- "required": true,
899
- "schema": {
900
- "type": "string",
901
- "format": "uuid",
902
- "title": "Thread Id",
903
- "description": "The ID of the thread."
904
- },
905
- "name": "thread_id",
906
- "in": "path"
907
- },
908
- {
909
- "description": "The ID of the checkpoint.",
910
- "required": true,
911
- "schema": {
912
- "type": "string",
913
- "format": "uuid",
914
- "title": "Checkpoint Id",
915
- "description": "The ID of the checkpoint."
889
+ "/threads/{thread_id}/state/{checkpoint_id}": {
890
+ "get": {
891
+ "tags": ["Threads"],
892
+ "summary": "Get Thread State At Checkpoint",
893
+ "description": "Get state for a thread at a specific checkpoint.",
894
+ "operationId": "get_thread_state_at_checkpoint_threads__thread_id__state__checkpoint_id__get",
895
+ "parameters": [
896
+ {
897
+ "description": "The ID of the thread.",
898
+ "required": true,
899
+ "schema": {
900
+ "type": "string",
901
+ "format": "uuid",
902
+ "title": "Thread Id",
903
+ "description": "The ID of the thread."
904
+ },
905
+ "name": "thread_id",
906
+ "in": "path"
916
907
  },
917
- "name": "checkpoint_id",
918
- "in": "path"
919
- },
920
- {
921
- "description": "Whether to include subgraphs in the response.",
922
- "required": false,
923
- "schema": {
924
- "type": "boolean",
925
- "title": "Subgraphs",
926
- "description": "Whether to include subgraphs in the response."
908
+ {
909
+ "description": "The ID of the checkpoint.",
910
+ "required": true,
911
+ "schema": {
912
+ "type": "string",
913
+ "format": "uuid",
914
+ "title": "Checkpoint Id",
915
+ "description": "The ID of the checkpoint."
916
+ },
917
+ "name": "checkpoint_id",
918
+ "in": "path"
927
919
  },
928
- "name": "subgraphs",
929
- "in": "query"
930
- }
931
- ],
932
- "responses": {
933
- "200": {
934
- "description": "Success",
935
- "content": {
936
- "application/json": {
937
- "schema": {
938
- "$ref": "#/components/schemas/ThreadState"
920
+ {
921
+ "description": "Whether to include subgraphs in the response.",
922
+ "required": false,
923
+ "schema": {
924
+ "type": "boolean",
925
+ "title": "Subgraphs",
926
+ "description": "Whether to include subgraphs in the response."
927
+ },
928
+ "name": "subgraphs",
929
+ "in": "query"
930
+ }
931
+ ],
932
+ "responses": {
933
+ "200": {
934
+ "description": "Success",
935
+ "content": {
936
+ "application/json": {
937
+ "schema": {
938
+ "$ref": "#/components/schemas/ThreadState"
939
+ }
939
940
  }
940
941
  }
941
- }
942
- },
943
- "422": {
944
- "description": "Validation Error",
945
- "content": {
946
- "application/json": {
947
- "schema": {
948
- "$ref": "#/components/schemas/ErrorResponse"
942
+ },
943
+ "422": {
944
+ "description": "Validation Error",
945
+ "content": {
946
+ "application/json": {
947
+ "schema": {
948
+ "$ref": "#/components/schemas/ErrorResponse"
949
+ }
949
950
  }
950
951
  }
951
952
  }
952
953
  }
953
954
  }
954
- }
955
- },
955
+ },
956
956
  "/threads/{thread_id}/state/checkpoint": {
957
957
  "post": {
958
958
  "tags": ["Threads"],
@@ -1483,6 +1483,14 @@
1483
1483
  "$ref": "#/components/schemas/Run"
1484
1484
  }
1485
1485
  }
1486
+ },
1487
+ "headers": {
1488
+ "Content-Location": {
1489
+ "description": "The URL of the run that was created. Can be used to later join the stream.",
1490
+ "schema": {
1491
+ "type": "string"
1492
+ }
1493
+ }
1486
1494
  }
1487
1495
  },
1488
1496
  "404": {
@@ -1622,6 +1630,14 @@
1622
1630
  "description": "The server will send a stream of events in SSE format.\n\n**Example event**:\n\nid: 1\n\nevent: message\n\ndata: {}"
1623
1631
  }
1624
1632
  }
1633
+ },
1634
+ "headers": {
1635
+ "Content-Location": {
1636
+ "description": "The URL of the run that was created. Can be used to later join the stream.",
1637
+ "schema": {
1638
+ "type": "string"
1639
+ }
1640
+ }
1625
1641
  }
1626
1642
  },
1627
1643
  "404": {
@@ -1694,6 +1710,14 @@
1694
1710
  "application/json": {
1695
1711
  "schema": {}
1696
1712
  }
1713
+ },
1714
+ "headers": {
1715
+ "Content-Location": {
1716
+ "description": "The URL of the run that was created. Can be used to later join the stream.",
1717
+ "schema": {
1718
+ "type": "string"
1719
+ }
1720
+ }
1697
1721
  }
1698
1722
  },
1699
1723
  "404": {
@@ -1936,7 +1960,7 @@
1936
1960
  "get": {
1937
1961
  "tags": ["Thread Runs"],
1938
1962
  "summary": "Join Run Stream",
1939
- "description": "Join a run stream. This endpoint streams output in real-time from a run similar to the /threads/__THREAD_ID__/runs/stream endpoint. Only output produced after this endpoint is called will be streamed.",
1963
+ "description": "Join a run stream. This endpoint streams output in real-time from a run similar to the /threads/__THREAD_ID__/runs/stream endpoint. If the run has been created with `stream_resumable=true`, the stream can be resumed from the last seen event ID.",
1940
1964
  "operationId": "stream_run_http_threads__thread_id__runs__run_id__join_get",
1941
1965
  "parameters": [
1942
1966
  {
@@ -1962,6 +1986,16 @@
1962
1986
  },
1963
1987
  "name": "run_id",
1964
1988
  "in": "path"
1989
+ },
1990
+ {
1991
+ "required": false,
1992
+ "schema": {
1993
+ "type": "string",
1994
+ "title": "Last Event ID",
1995
+ "description": "The ID of the last event received. Set to -1 if you want to stream all events. Requires `stream_resumable=true` to be set when creating the run."
1996
+ },
1997
+ "name": "Last-Event-ID",
1998
+ "in": "header"
1965
1999
  }
1966
2000
  ],
1967
2001
  "responses": {
@@ -2204,6 +2238,14 @@
2204
2238
  "description": "The server will send a stream of events in SSE format.\n\n**Example event**:\n\nid: 1\n\nevent: message\n\ndata: {}"
2205
2239
  }
2206
2240
  }
2241
+ },
2242
+ "headers": {
2243
+ "Content-Location": {
2244
+ "description": "The URL of the run that was created. Can be used to later join the stream.",
2245
+ "schema": {
2246
+ "type": "string"
2247
+ }
2248
+ }
2207
2249
  }
2208
2250
  },
2209
2251
  "404": {
@@ -2319,6 +2361,14 @@
2319
2361
  "application/json": {
2320
2362
  "schema": {}
2321
2363
  }
2364
+ },
2365
+ "headers": {
2366
+ "Content-Location": {
2367
+ "description": "The URL of the run that was created. Can be used to later join the stream.",
2368
+ "schema": {
2369
+ "type": "string"
2370
+ }
2371
+ }
2322
2372
  }
2323
2373
  },
2324
2374
  "404": {
@@ -2377,6 +2427,14 @@
2377
2427
  "application/json": {
2378
2428
  "schema": {}
2379
2429
  }
2430
+ },
2431
+ "headers": {
2432
+ "Content-Location": {
2433
+ "description": "The URL of the run that was created. Can be used to later join the stream.",
2434
+ "schema": {
2435
+ "type": "string"
2436
+ }
2437
+ }
2380
2438
  }
2381
2439
  },
2382
2440
  "404": {
@@ -3569,6 +3627,12 @@
3569
3627
  "description": "Whether to stream output from subgraphs.",
3570
3628
  "default": false
3571
3629
  },
3630
+ "stream_resumable": {
3631
+ "type": "boolean",
3632
+ "title": "Stream Resumable",
3633
+ "description": "Whether to persist the stream chunks in order to resume the stream later.",
3634
+ "default": false
3635
+ },
3572
3636
  "on_disconnect": {
3573
3637
  "type": "string",
3574
3638
  "enum": ["cancel", "continue"],
@@ -3791,6 +3855,12 @@
3791
3855
  "description": "Whether to stream output from subgraphs.",
3792
3856
  "default": false
3793
3857
  },
3858
+ "stream_resumable": {
3859
+ "type": "boolean",
3860
+ "title": "Stream Resumable",
3861
+ "description": "Whether to persist the stream chunks in order to resume the stream later.",
3862
+ "default": false
3863
+ },
3794
3864
  "on_completion": {
3795
3865
  "type": "string",
3796
3866
  "enum": ["delete", "keep"],
@@ -31,7 +31,7 @@ dependencies = [
31
31
  "cryptography>=42.0.0,<45.0",
32
32
  "langgraph-sdk>=0.1.66",
33
33
  "cloudpickle>=3.0.0",
34
- "langgraph-runtime-inmem>=0.1.0,<0.2",
34
+ "langgraph-runtime-inmem>=0.2.0,<0.3",
35
35
  "truststore>=0.1",
36
36
  ]
37
37
 
@@ -1 +0,0 @@
1
- __version__ = "0.2.32"
File without changes
File without changes
File without changes