langgraph-api 0.4.18__tar.gz → 0.4.20__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.
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/PKG-INFO +1 -1
- langgraph_api-0.4.20/langgraph_api/__init__.py +1 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/api/runs.py +163 -117
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/serde.py +4 -0
- langgraph_api-0.4.18/langgraph_api/__init__.py +0 -1
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/.gitignore +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/LICENSE +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/Makefile +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/README.md +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/benchmark/.gitignore +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/benchmark/Makefile +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/benchmark/README.md +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/benchmark/burst.js +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/benchmark/clean.js +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/benchmark/graphs.js +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/benchmark/package.json +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/benchmark/ramp.js +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/benchmark/update-revision.js +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/benchmark/weather.js +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/constraints.txt +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/forbidden.txt +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/healthcheck.py +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/api/__init__.py +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/api/a2a.py +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/api/assistants.py +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/api/mcp.py +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/api/meta.py +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/api/openapi.py +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/api/store.py +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/api/threads.py +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/api/ui.py +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/asgi_transport.py +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/asyncio.py +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/auth/__init__.py +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/auth/custom.py +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/auth/langsmith/__init__.py +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/auth/langsmith/backend.py +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/auth/langsmith/client.py +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/auth/middleware.py +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/auth/noop.py +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/auth/studio_user.py +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/cli.py +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/command.py +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/config.py +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/cron_scheduler.py +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/errors.py +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/executor_entrypoint.py +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/feature_flags.py +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/graph.py +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/http.py +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/http_metrics.py +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/js/.gitignore +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/js/.prettierrc +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/js/__init__.py +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/js/base.py +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/js/build.mts +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/js/client.http.mts +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/js/client.mts +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/js/errors.py +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/js/global.d.ts +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/js/package.json +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/js/remote.py +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/js/schema.py +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/js/src/graph.mts +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/js/src/load.hooks.mjs +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/js/src/preload.mjs +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/js/src/utils/files.mts +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/js/src/utils/importMap.mts +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/js/src/utils/pythonSchemas.mts +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/js/src/utils/serde.mts +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/js/sse.py +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/js/traceblock.mts +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/js/tsconfig.json +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/js/ui.py +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/js/yarn.lock +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/logging.py +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/metadata.py +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/middleware/__init__.py +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/middleware/http_logger.py +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/middleware/private_network.py +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/middleware/request_id.py +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/models/__init__.py +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/models/run.py +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/patch.py +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/queue_entrypoint.py +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/route.py +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/schema.py +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/server.py +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/sse.py +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/state.py +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/store.py +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/stream.py +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/thread_ttl.py +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/traceblock.py +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/tunneling/cloudflare.py +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/utils/__init__.py +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/utils/cache.py +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/utils/config.py +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/utils/future.py +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/utils/headers.py +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/utils/retriable_client.py +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/utils/uuids.py +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/validation.py +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/webhook.py +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_api/worker.py +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_license/__init__.py +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_license/validation.py +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_runtime/__init__.py +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_runtime/checkpoint.py +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_runtime/database.py +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_runtime/lifespan.py +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_runtime/metrics.py +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_runtime/ops.py +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_runtime/queue.py +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_runtime/retry.py +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/langgraph_runtime/store.py +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/logging.json +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/openapi.json +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/pyproject.toml +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/scripts/create_license.py +0 -0
- {langgraph_api-0.4.18 → langgraph_api-0.4.20}/uv.lock +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.4.20"
|
|
@@ -104,7 +104,9 @@ async def stream_run(
|
|
|
104
104
|
payload = await request.json(RunCreateStateful)
|
|
105
105
|
on_disconnect = payload.get("on_disconnect", "continue")
|
|
106
106
|
run_id = uuid7()
|
|
107
|
-
|
|
107
|
+
|
|
108
|
+
sub = await Runs.Stream.subscribe(run_id, thread_id)
|
|
109
|
+
try:
|
|
108
110
|
async with connect() as conn:
|
|
109
111
|
run = await create_valid_run(
|
|
110
112
|
conn,
|
|
@@ -114,20 +116,32 @@ async def stream_run(
|
|
|
114
116
|
run_id=run_id,
|
|
115
117
|
request_start_time=request.scope.get("request_start_time_ms"),
|
|
116
118
|
)
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
119
|
+
except Exception:
|
|
120
|
+
# Clean up the pubsub on errors
|
|
121
|
+
await sub.__aexit__(None, None, None)
|
|
122
|
+
raise
|
|
123
|
+
|
|
124
|
+
async def body():
|
|
125
|
+
try:
|
|
126
|
+
async for event, message, stream_id in Runs.Stream.join(
|
|
120
127
|
run["run_id"],
|
|
121
128
|
thread_id=thread_id,
|
|
122
129
|
cancel_on_disconnect=on_disconnect == "cancel",
|
|
123
130
|
stream_channel=sub,
|
|
124
131
|
last_event_id=None,
|
|
125
|
-
)
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
132
|
+
):
|
|
133
|
+
yield event, message, stream_id
|
|
134
|
+
finally:
|
|
135
|
+
# Make sure to always clean up the pubsub
|
|
136
|
+
await sub.__aexit__(None, None, None)
|
|
137
|
+
|
|
138
|
+
return EventSourceResponse(
|
|
139
|
+
body(),
|
|
140
|
+
headers={
|
|
141
|
+
"Location": f"/threads/{thread_id}/runs/{run['run_id']}/stream",
|
|
142
|
+
"Content-Location": f"/threads/{thread_id}/runs/{run['run_id']}",
|
|
143
|
+
},
|
|
144
|
+
)
|
|
131
145
|
|
|
132
146
|
|
|
133
147
|
async def stream_run_stateless(
|
|
@@ -139,7 +153,9 @@ async def stream_run_stateless(
|
|
|
139
153
|
on_disconnect = payload.get("on_disconnect", "continue")
|
|
140
154
|
run_id = uuid7()
|
|
141
155
|
thread_id = uuid4()
|
|
142
|
-
|
|
156
|
+
|
|
157
|
+
sub = await Runs.Stream.subscribe(run_id, thread_id)
|
|
158
|
+
try:
|
|
143
159
|
async with connect() as conn:
|
|
144
160
|
run = await create_valid_run(
|
|
145
161
|
conn,
|
|
@@ -150,21 +166,33 @@ async def stream_run_stateless(
|
|
|
150
166
|
request_start_time=request.scope.get("request_start_time_ms"),
|
|
151
167
|
temporary=True,
|
|
152
168
|
)
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
169
|
+
except Exception:
|
|
170
|
+
# Clean up the pubsub on errors
|
|
171
|
+
await sub.__aexit__(None, None, None)
|
|
172
|
+
raise
|
|
173
|
+
|
|
174
|
+
async def body():
|
|
175
|
+
try:
|
|
176
|
+
async for event, message, stream_id in Runs.Stream.join(
|
|
156
177
|
run["run_id"],
|
|
157
178
|
thread_id=run["thread_id"],
|
|
158
179
|
ignore_404=True,
|
|
159
180
|
cancel_on_disconnect=on_disconnect == "cancel",
|
|
160
181
|
stream_channel=sub,
|
|
161
182
|
last_event_id=None,
|
|
162
|
-
)
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
183
|
+
):
|
|
184
|
+
yield event, message, stream_id
|
|
185
|
+
finally:
|
|
186
|
+
# Make sure to always clean up the pubsub
|
|
187
|
+
await sub.__aexit__(None, None, None)
|
|
188
|
+
|
|
189
|
+
return EventSourceResponse(
|
|
190
|
+
body(),
|
|
191
|
+
headers={
|
|
192
|
+
"Location": f"/runs/{run['run_id']}/stream",
|
|
193
|
+
"Content-Location": f"/runs/{run['run_id']}",
|
|
194
|
+
},
|
|
195
|
+
)
|
|
168
196
|
|
|
169
197
|
|
|
170
198
|
@retry_db
|
|
@@ -174,7 +202,8 @@ async def wait_run(request: ApiRequest):
|
|
|
174
202
|
payload = await request.json(RunCreateStateful)
|
|
175
203
|
on_disconnect = payload.get("on_disconnect", "continue")
|
|
176
204
|
run_id = uuid7()
|
|
177
|
-
|
|
205
|
+
sub = await Runs.Stream.subscribe(run_id, thread_id)
|
|
206
|
+
try:
|
|
178
207
|
async with connect() as conn:
|
|
179
208
|
run = await create_valid_run(
|
|
180
209
|
conn,
|
|
@@ -184,43 +213,44 @@ async def wait_run(request: ApiRequest):
|
|
|
184
213
|
run_id=run_id,
|
|
185
214
|
request_start_time=request.scope.get("request_start_time_ms"),
|
|
186
215
|
)
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
216
|
+
except Exception:
|
|
217
|
+
# Clean up the pubsub on errors
|
|
218
|
+
await sub.__aexit__(None, None, None)
|
|
219
|
+
raise
|
|
220
|
+
|
|
221
|
+
last_chunk = ValueEvent()
|
|
222
|
+
|
|
223
|
+
async def consume():
|
|
224
|
+
vchunk: bytes | None = None
|
|
225
|
+
async for mode, chunk, _ in Runs.Stream.join(
|
|
226
|
+
run["run_id"],
|
|
227
|
+
thread_id=run["thread_id"],
|
|
228
|
+
stream_channel=sub,
|
|
229
|
+
cancel_on_disconnect=on_disconnect == "cancel",
|
|
230
|
+
):
|
|
231
|
+
if mode == b"values" or mode == b"updates" and b"__interrupt__" in chunk:
|
|
232
|
+
vchunk = chunk
|
|
233
|
+
elif mode == b"error":
|
|
234
|
+
vchunk = orjson.dumps({"__error__": orjson.Fragment(chunk)})
|
|
235
|
+
if vchunk is not None:
|
|
236
|
+
last_chunk.set(vchunk)
|
|
237
|
+
else:
|
|
238
|
+
async with connect() as conn:
|
|
239
|
+
thread_iter = await Threads.get(conn, thread_id)
|
|
240
|
+
try:
|
|
241
|
+
thread = await anext(thread_iter)
|
|
242
|
+
last_chunk.set(thread["values"])
|
|
243
|
+
except StopAsyncIteration:
|
|
244
|
+
await logger.awarning(
|
|
245
|
+
f"No checkpoint found for thread {thread_id}",
|
|
246
|
+
thread_id=thread_id,
|
|
247
|
+
)
|
|
248
|
+
last_chunk.set(b"{}")
|
|
249
|
+
|
|
250
|
+
# keep the connection open by sending whitespace every 5 seconds
|
|
251
|
+
# leading whitespace will be ignored by json parsers
|
|
252
|
+
async def body() -> AsyncIterator[bytes]:
|
|
253
|
+
try:
|
|
224
254
|
stream = asyncio.create_task(consume())
|
|
225
255
|
while True:
|
|
226
256
|
try:
|
|
@@ -235,15 +265,18 @@ async def wait_run(request: ApiRequest):
|
|
|
235
265
|
stream.cancel()
|
|
236
266
|
await stream
|
|
237
267
|
raise
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
268
|
+
finally:
|
|
269
|
+
# Make sure to always clean up the pubsub
|
|
270
|
+
await sub.__aexit__(None, None, None)
|
|
271
|
+
|
|
272
|
+
return StreamingResponse(
|
|
273
|
+
body(),
|
|
274
|
+
media_type="application/json",
|
|
275
|
+
headers={
|
|
276
|
+
"Location": f"/threads/{thread_id}/runs/{run['run_id']}/join",
|
|
277
|
+
"Content-Location": f"/threads/{thread_id}/runs/{run['run_id']}",
|
|
278
|
+
},
|
|
279
|
+
)
|
|
247
280
|
|
|
248
281
|
|
|
249
282
|
@retry_db
|
|
@@ -254,7 +287,9 @@ async def wait_run_stateless(request: ApiRequest):
|
|
|
254
287
|
on_disconnect = payload.get("on_disconnect", "continue")
|
|
255
288
|
run_id = uuid7()
|
|
256
289
|
thread_id = uuid4()
|
|
257
|
-
|
|
290
|
+
|
|
291
|
+
sub = await Runs.Stream.subscribe(run_id, thread_id)
|
|
292
|
+
try:
|
|
258
293
|
async with connect() as conn:
|
|
259
294
|
run = await create_valid_run(
|
|
260
295
|
conn,
|
|
@@ -265,40 +300,41 @@ async def wait_run_stateless(request: ApiRequest):
|
|
|
265
300
|
request_start_time=request.scope.get("request_start_time_ms"),
|
|
266
301
|
temporary=True,
|
|
267
302
|
)
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
303
|
+
except Exception:
|
|
304
|
+
# Clean up the pubsub on errors
|
|
305
|
+
await sub.__aexit__(None, None, None)
|
|
306
|
+
raise
|
|
307
|
+
|
|
308
|
+
last_chunk = ValueEvent()
|
|
309
|
+
|
|
310
|
+
async def consume():
|
|
311
|
+
vchunk: bytes | None = None
|
|
312
|
+
async for mode, chunk, _ in Runs.Stream.join(
|
|
313
|
+
run["run_id"],
|
|
314
|
+
thread_id=run["thread_id"],
|
|
315
|
+
stream_channel=sub,
|
|
316
|
+
ignore_404=True,
|
|
317
|
+
cancel_on_disconnect=on_disconnect == "cancel",
|
|
318
|
+
):
|
|
319
|
+
if mode == b"values" or mode == b"updates" and b"__interrupt__" in chunk:
|
|
320
|
+
vchunk = chunk
|
|
321
|
+
elif mode == b"error":
|
|
322
|
+
vchunk = orjson.dumps({"__error__": orjson.Fragment(chunk)})
|
|
323
|
+
if vchunk is not None:
|
|
324
|
+
last_chunk.set(vchunk)
|
|
325
|
+
else:
|
|
326
|
+
# we can't fetch the thread (it was deleted), so just return empty values
|
|
327
|
+
await logger.awarning(
|
|
328
|
+
"No checkpoint emitted for stateless run",
|
|
329
|
+
run_id=run["run_id"],
|
|
275
330
|
thread_id=run["thread_id"],
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
and b"__interrupt__" in chunk
|
|
284
|
-
):
|
|
285
|
-
vchunk = chunk
|
|
286
|
-
elif mode == b"error":
|
|
287
|
-
vchunk = orjson.dumps({"__error__": orjson.Fragment(chunk)})
|
|
288
|
-
if vchunk is not None:
|
|
289
|
-
last_chunk.set(vchunk)
|
|
290
|
-
else:
|
|
291
|
-
# we can't fetch the thread (it was deleted), so just return empty values
|
|
292
|
-
await logger.awarning(
|
|
293
|
-
"No checkpoint emitted for stateless run",
|
|
294
|
-
run_id=run["run_id"],
|
|
295
|
-
thread_id=run["thread_id"],
|
|
296
|
-
)
|
|
297
|
-
last_chunk.set(b"{}")
|
|
298
|
-
|
|
299
|
-
# keep the connection open by sending whitespace every 5 seconds
|
|
300
|
-
# leading whitespace will be ignored by json parsers
|
|
301
|
-
async def body() -> AsyncIterator[bytes]:
|
|
331
|
+
)
|
|
332
|
+
last_chunk.set(b"{}")
|
|
333
|
+
|
|
334
|
+
# keep the connection open by sending whitespace every 5 seconds
|
|
335
|
+
# leading whitespace will be ignored by json parsers
|
|
336
|
+
async def body() -> AsyncIterator[bytes]:
|
|
337
|
+
try:
|
|
302
338
|
stream = asyncio.create_task(consume())
|
|
303
339
|
while True:
|
|
304
340
|
try:
|
|
@@ -313,15 +349,18 @@ async def wait_run_stateless(request: ApiRequest):
|
|
|
313
349
|
stream.cancel("Run stream cancelled")
|
|
314
350
|
await stream
|
|
315
351
|
raise
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
352
|
+
finally:
|
|
353
|
+
# Make sure to always clean up the pubsub
|
|
354
|
+
await sub.__aexit__(None, None, None)
|
|
355
|
+
|
|
356
|
+
return StreamingResponse(
|
|
357
|
+
body(),
|
|
358
|
+
media_type="application/json",
|
|
359
|
+
headers={
|
|
360
|
+
"Location": f"/threads/{run['thread_id']}/runs/{run['run_id']}/join",
|
|
361
|
+
"Content-Location": f"/threads/{run['thread_id']}/runs/{run['run_id']}",
|
|
362
|
+
},
|
|
363
|
+
)
|
|
325
364
|
|
|
326
365
|
|
|
327
366
|
@retry_db
|
|
@@ -402,14 +441,21 @@ async def join_run_stream(request: ApiRequest):
|
|
|
402
441
|
validate_uuid(run_id, "Invalid run ID: must be a UUID")
|
|
403
442
|
stream_mode = request.query_params.get("stream_mode") or []
|
|
404
443
|
last_event_id = request.headers.get("last-event-id") or None
|
|
444
|
+
|
|
445
|
+
async def body():
|
|
446
|
+
async with await Runs.Stream.subscribe(run_id, thread_id) as sub:
|
|
447
|
+
async for event, message, stream_id in Runs.Stream.join(
|
|
448
|
+
run_id,
|
|
449
|
+
thread_id=thread_id,
|
|
450
|
+
cancel_on_disconnect=cancel_on_disconnect,
|
|
451
|
+
stream_channel=sub,
|
|
452
|
+
stream_mode=stream_mode,
|
|
453
|
+
last_event_id=last_event_id,
|
|
454
|
+
):
|
|
455
|
+
yield event, message, stream_id
|
|
456
|
+
|
|
405
457
|
return EventSourceResponse(
|
|
406
|
-
|
|
407
|
-
run_id,
|
|
408
|
-
thread_id=thread_id,
|
|
409
|
-
cancel_on_disconnect=cancel_on_disconnect,
|
|
410
|
-
stream_mode=stream_mode,
|
|
411
|
-
last_event_id=last_event_id,
|
|
412
|
-
),
|
|
458
|
+
body(),
|
|
413
459
|
)
|
|
414
460
|
|
|
415
461
|
|
|
@@ -31,6 +31,10 @@ class Fragment(NamedTuple):
|
|
|
31
31
|
buf: bytes
|
|
32
32
|
|
|
33
33
|
|
|
34
|
+
def fragment_loads(data: bytes) -> Fragment:
|
|
35
|
+
return Fragment(data)
|
|
36
|
+
|
|
37
|
+
|
|
34
38
|
def decimal_encoder(dec_value: Decimal) -> int | float:
|
|
35
39
|
"""
|
|
36
40
|
Encodes a Decimal as int of there's no exponent, otherwise float
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.4.18"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|