langgraph-api 0.2.40__tar.gz → 0.2.43__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.2.40 → langgraph_api-0.2.43}/PKG-INFO +1 -1
- langgraph_api-0.2.43/langgraph_api/__init__.py +1 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/langgraph_api/api/runs.py +7 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/langgraph_api/logging.py +0 -18
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/langgraph_api/metadata.py +1 -12
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/langgraph_api/middleware/request_id.py +2 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/langgraph_api/models/run.py +15 -2
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/langgraph_api/stream.py +6 -1
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/langgraph_api/worker.py +67 -20
- langgraph_api-0.2.40/langgraph_api/__init__.py +0 -1
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/.gitignore +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/LICENSE +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/Makefile +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/README.md +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/benchmark/weather.js +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/constraints.txt +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/forbidden.txt +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/healthcheck.py +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/langgraph_api/api/__init__.py +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/langgraph_api/api/assistants.py +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/langgraph_api/api/mcp.py +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/langgraph_api/api/meta.py +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/langgraph_api/api/openapi.py +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/langgraph_api/api/store.py +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/langgraph_api/api/threads.py +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/langgraph_api/api/ui.py +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/langgraph_api/asgi_transport.py +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/langgraph_api/asyncio.py +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/langgraph_api/auth/__init__.py +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/langgraph_api/auth/custom.py +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/langgraph_api/auth/langsmith/__init__.py +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/langgraph_api/auth/langsmith/backend.py +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/langgraph_api/auth/langsmith/client.py +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/langgraph_api/auth/middleware.py +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/langgraph_api/auth/noop.py +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/langgraph_api/auth/studio_user.py +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/langgraph_api/cli.py +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/langgraph_api/command.py +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/langgraph_api/config.py +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/langgraph_api/cron_scheduler.py +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/langgraph_api/errors.py +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/langgraph_api/graph.py +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/langgraph_api/http.py +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/langgraph_api/js/.gitignore +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/langgraph_api/js/.prettierrc +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/langgraph_api/js/__init__.py +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/langgraph_api/js/base.py +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/langgraph_api/js/build.mts +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/langgraph_api/js/client.http.mts +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/langgraph_api/js/client.mts +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/langgraph_api/js/errors.py +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/langgraph_api/js/global.d.ts +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/langgraph_api/js/package.json +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/langgraph_api/js/remote.py +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/langgraph_api/js/schema.py +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/langgraph_api/js/src/graph.mts +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/langgraph_api/js/src/load.hooks.mjs +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/langgraph_api/js/src/preload.mjs +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/langgraph_api/js/src/utils/files.mts +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/langgraph_api/js/src/utils/importMap.mts +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/langgraph_api/js/src/utils/pythonSchemas.mts +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/langgraph_api/js/src/utils/serde.mts +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/langgraph_api/js/sse.py +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/langgraph_api/js/tsconfig.json +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/langgraph_api/js/ui.py +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/langgraph_api/js/yarn.lock +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/langgraph_api/middleware/__init__.py +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/langgraph_api/middleware/http_logger.py +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/langgraph_api/middleware/private_network.py +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/langgraph_api/models/__init__.py +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/langgraph_api/patch.py +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/langgraph_api/queue_entrypoint.py +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/langgraph_api/route.py +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/langgraph_api/schema.py +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/langgraph_api/serde.py +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/langgraph_api/server.py +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/langgraph_api/sse.py +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/langgraph_api/state.py +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/langgraph_api/store.py +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/langgraph_api/thread_ttl.py +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/langgraph_api/tunneling/cloudflare.py +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/langgraph_api/utils.py +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/langgraph_api/validation.py +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/langgraph_api/webhook.py +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/langgraph_license/__init__.py +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/langgraph_license/validation.py +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/langgraph_runtime/__init__.py +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/logging.json +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/openapi.json +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/pyproject.toml +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/scripts/create_license.py +0 -0
- {langgraph_api-0.2.40 → langgraph_api-0.2.43}/uv.lock +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.2.43"
|
|
@@ -38,6 +38,7 @@ async def create_run(request: ApiRequest):
|
|
|
38
38
|
thread_id,
|
|
39
39
|
payload,
|
|
40
40
|
request.headers,
|
|
41
|
+
request_start_time=request.scope.get("request_start_time_ms"),
|
|
41
42
|
)
|
|
42
43
|
return ApiResponse(
|
|
43
44
|
run,
|
|
@@ -55,6 +56,7 @@ async def create_stateless_run(request: ApiRequest):
|
|
|
55
56
|
None,
|
|
56
57
|
payload,
|
|
57
58
|
request.headers,
|
|
59
|
+
request_start_time=request.scope.get("request_start_time_ms"),
|
|
58
60
|
)
|
|
59
61
|
return ApiResponse(
|
|
60
62
|
run,
|
|
@@ -75,6 +77,7 @@ async def create_stateless_run_batch(request: ApiRequest):
|
|
|
75
77
|
payload,
|
|
76
78
|
request.headers,
|
|
77
79
|
barrier,
|
|
80
|
+
request_start_time=request.scope.get("request_start_time_ms"),
|
|
78
81
|
)
|
|
79
82
|
for payload in batch_payload
|
|
80
83
|
]
|
|
@@ -100,6 +103,7 @@ async def stream_run(
|
|
|
100
103
|
payload,
|
|
101
104
|
request.headers,
|
|
102
105
|
run_id=run_id,
|
|
106
|
+
request_start_time=request.scope.get("request_start_time_ms"),
|
|
103
107
|
)
|
|
104
108
|
except Exception:
|
|
105
109
|
if not sub.cancelled():
|
|
@@ -139,6 +143,7 @@ async def stream_run_stateless(
|
|
|
139
143
|
payload,
|
|
140
144
|
request.headers,
|
|
141
145
|
run_id=run_id,
|
|
146
|
+
request_start_time=request.scope.get("request_start_time_ms"),
|
|
142
147
|
)
|
|
143
148
|
except Exception:
|
|
144
149
|
if not sub.cancelled():
|
|
@@ -178,6 +183,7 @@ async def wait_run(request: ApiRequest):
|
|
|
178
183
|
payload,
|
|
179
184
|
request.headers,
|
|
180
185
|
run_id=run_id,
|
|
186
|
+
request_start_time=request.scope.get("request_start_time_ms"),
|
|
181
187
|
)
|
|
182
188
|
except Exception:
|
|
183
189
|
if not sub.cancelled():
|
|
@@ -250,6 +256,7 @@ async def wait_run_stateless(request: ApiRequest):
|
|
|
250
256
|
payload,
|
|
251
257
|
request.headers,
|
|
252
258
|
run_id=run_id,
|
|
259
|
+
request_start_time=request.scope.get("request_start_time_ms"),
|
|
253
260
|
)
|
|
254
261
|
except Exception:
|
|
255
262
|
if not sub.cancelled():
|
|
@@ -81,23 +81,6 @@ class JSONRenderer:
|
|
|
81
81
|
LEVELS = logging.getLevelNamesMapping()
|
|
82
82
|
|
|
83
83
|
|
|
84
|
-
class TapForMetadata:
|
|
85
|
-
def __call__(
|
|
86
|
-
self, logger: logging.Logger, method_name: str, event_dict: EventDict
|
|
87
|
-
) -> str:
|
|
88
|
-
"""
|
|
89
|
-
Tap WARN and above logs for metadata. Exclude user loggers.
|
|
90
|
-
"""
|
|
91
|
-
if (
|
|
92
|
-
event_dict["logger"].startswith("langgraph")
|
|
93
|
-
and LEVELS[event_dict["level"].upper()] > LEVELS["INFO"]
|
|
94
|
-
):
|
|
95
|
-
from langgraph_api.metadata import append_log
|
|
96
|
-
|
|
97
|
-
append_log(event_dict.copy())
|
|
98
|
-
return event_dict
|
|
99
|
-
|
|
100
|
-
|
|
101
84
|
# shared config, for both logging and structlog
|
|
102
85
|
|
|
103
86
|
shared_processors = [
|
|
@@ -136,7 +119,6 @@ class Formatter(structlog.stdlib.ProcessorFormatter):
|
|
|
136
119
|
super().__init__(
|
|
137
120
|
processors=[
|
|
138
121
|
structlog.stdlib.ProcessorFormatter.remove_processors_meta,
|
|
139
|
-
TapForMetadata(),
|
|
140
122
|
renderer,
|
|
141
123
|
],
|
|
142
124
|
foreign_pre_chain=shared_processors,
|
|
@@ -35,7 +35,6 @@ else:
|
|
|
35
35
|
PLAN = "enterprise" if plus_features_enabled() else "developer"
|
|
36
36
|
USER_API_URL = os.getenv("LANGGRAPH_API_URL", None)
|
|
37
37
|
|
|
38
|
-
LOGS: list[dict] = []
|
|
39
38
|
RUN_COUNTER = 0
|
|
40
39
|
NODE_COUNTER = 0
|
|
41
40
|
FROM_TIMESTAMP = datetime.now(UTC).isoformat()
|
|
@@ -59,14 +58,6 @@ def incr_nodes(_, *, incr: int = 1) -> None:
|
|
|
59
58
|
NODE_COUNTER += incr
|
|
60
59
|
|
|
61
60
|
|
|
62
|
-
def append_log(log: dict) -> None:
|
|
63
|
-
if not LANGGRAPH_CLOUD_LICENSE_KEY and not LANGSMITH_API_KEY:
|
|
64
|
-
return
|
|
65
|
-
|
|
66
|
-
global LOGS
|
|
67
|
-
LOGS.append(log)
|
|
68
|
-
|
|
69
|
-
|
|
70
61
|
async def metadata_loop() -> None:
|
|
71
62
|
try:
|
|
72
63
|
from langgraph_api import __version__
|
|
@@ -91,8 +82,6 @@ async def metadata_loop() -> None:
|
|
|
91
82
|
to_timestamp = datetime.now(UTC).isoformat()
|
|
92
83
|
nodes = NODE_COUNTER
|
|
93
84
|
runs = RUN_COUNTER
|
|
94
|
-
logs = LOGS.copy()
|
|
95
|
-
LOGS.clear()
|
|
96
85
|
RUN_COUNTER = 0
|
|
97
86
|
NODE_COUNTER = 0
|
|
98
87
|
FROM_TIMESTAMP = to_timestamp
|
|
@@ -123,7 +112,7 @@ async def metadata_loop() -> None:
|
|
|
123
112
|
"langgraph.platform.runs": runs,
|
|
124
113
|
"langgraph.platform.nodes": nodes,
|
|
125
114
|
},
|
|
126
|
-
"logs":
|
|
115
|
+
"logs": [],
|
|
127
116
|
}
|
|
128
117
|
try:
|
|
129
118
|
await http_request(
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""Middleware to handle setting request IDs for logging."""
|
|
2
2
|
|
|
3
3
|
import re
|
|
4
|
+
import time
|
|
4
5
|
import uuid
|
|
5
6
|
|
|
6
7
|
from starlette.types import ASGIApp, Receive, Scope, Send
|
|
@@ -27,4 +28,5 @@ class RequestIdMiddleware:
|
|
|
27
28
|
if request_id is None:
|
|
28
29
|
request_id = str(uuid.uuid4()).encode()
|
|
29
30
|
scope["headers"].append((b"x-request-id", request_id))
|
|
31
|
+
scope["request_start_time_ms"] = int(time.time() * 1000)
|
|
30
32
|
await self.app(scope, receive, send)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import functools
|
|
3
3
|
import re
|
|
4
|
+
import time
|
|
4
5
|
import urllib.parse
|
|
5
6
|
import uuid
|
|
6
7
|
from collections.abc import Mapping, Sequence
|
|
@@ -249,6 +250,7 @@ async def create_valid_run(
|
|
|
249
250
|
headers: Mapping[str, str],
|
|
250
251
|
barrier: asyncio.Barrier | None = None,
|
|
251
252
|
run_id: UUID | None = None,
|
|
253
|
+
request_start_time: float | None = None,
|
|
252
254
|
) -> Run:
|
|
253
255
|
request_id = headers.get("x-request-id") # Will be null in the crons scheduler.
|
|
254
256
|
(
|
|
@@ -293,6 +295,11 @@ async def create_valid_run(
|
|
|
293
295
|
user_id = None
|
|
294
296
|
if not configurable.get("langgraph_request_id"):
|
|
295
297
|
configurable["langgraph_request_id"] = request_id
|
|
298
|
+
if request_start_time:
|
|
299
|
+
configurable["__request_start_time_ms__"] = request_start_time
|
|
300
|
+
after_seconds = payload.get("after_seconds", 0)
|
|
301
|
+
configurable["__after_seconds__"] = after_seconds
|
|
302
|
+
put_time_start = time.time()
|
|
296
303
|
run_coro = Runs.put(
|
|
297
304
|
conn,
|
|
298
305
|
assistant_id,
|
|
@@ -317,7 +324,7 @@ async def create_valid_run(
|
|
|
317
324
|
run_id=run_id,
|
|
318
325
|
multitask_strategy=multitask_strategy,
|
|
319
326
|
prevent_insert_if_inflight=prevent_insert_if_inflight,
|
|
320
|
-
after_seconds=
|
|
327
|
+
after_seconds=after_seconds,
|
|
321
328
|
if_not_exists=payload.get("if_not_exists", "reject"),
|
|
322
329
|
)
|
|
323
330
|
run_ = await run_coro
|
|
@@ -344,8 +351,14 @@ async def create_valid_run(
|
|
|
344
351
|
multitask_strategy=multitask_strategy,
|
|
345
352
|
stream_mode=stream_mode,
|
|
346
353
|
temporary=temporary,
|
|
347
|
-
after_seconds=
|
|
354
|
+
after_seconds=after_seconds,
|
|
348
355
|
if_not_exists=payload.get("if_not_exists", "reject"),
|
|
356
|
+
run_create_ms=(
|
|
357
|
+
int(time.time() * 1_000) - request_start_time
|
|
358
|
+
if request_start_time
|
|
359
|
+
else None
|
|
360
|
+
),
|
|
361
|
+
run_put_ms=int((time.time() - put_time_start) * 1_000),
|
|
349
362
|
)
|
|
350
363
|
# inserted, proceed
|
|
351
364
|
if multitask_strategy in ("interrupt", "rollback") and inflight_runs:
|
|
@@ -207,11 +207,16 @@ async def astream_state(
|
|
|
207
207
|
elif "events" in stream_mode:
|
|
208
208
|
yield "events", event
|
|
209
209
|
else:
|
|
210
|
+
output_keys = kwargs.pop("output_keys", graph.output_channels)
|
|
210
211
|
async with (
|
|
211
212
|
stack,
|
|
212
213
|
aclosing(
|
|
213
214
|
graph.astream(
|
|
214
|
-
input,
|
|
215
|
+
input,
|
|
216
|
+
config,
|
|
217
|
+
stream_mode=list(stream_modes_set),
|
|
218
|
+
output_keys=output_keys,
|
|
219
|
+
**kwargs,
|
|
215
220
|
)
|
|
216
221
|
) as stream,
|
|
217
222
|
):
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
+
import time
|
|
2
3
|
from collections.abc import AsyncGenerator
|
|
3
4
|
from contextlib import AsyncExitStack, asynccontextmanager
|
|
4
5
|
from datetime import UTC, datetime
|
|
@@ -74,8 +75,21 @@ async def worker(
|
|
|
74
75
|
exception: Exception | None = None
|
|
75
76
|
status: str | None = None
|
|
76
77
|
webhook = run["kwargs"].get("webhook", None)
|
|
77
|
-
|
|
78
|
+
request_created_at: int | None = run["kwargs"]["config"]["configurable"].get(
|
|
79
|
+
"__request_start_time_ms__"
|
|
80
|
+
)
|
|
81
|
+
after_seconds = run["kwargs"]["config"]["configurable"].get("__after_seconds__", 0)
|
|
78
82
|
run_ended_at: str | None = None
|
|
83
|
+
run_started_at = datetime.now(UTC)
|
|
84
|
+
# Note that "created_at" is inclusive of the `after_seconds`
|
|
85
|
+
run_creation_ms = (
|
|
86
|
+
int(
|
|
87
|
+
((run["created_at"].timestamp() - after_seconds) * 1_000)
|
|
88
|
+
- request_created_at
|
|
89
|
+
)
|
|
90
|
+
if request_created_at is not None
|
|
91
|
+
else None
|
|
92
|
+
)
|
|
79
93
|
|
|
80
94
|
async with (
|
|
81
95
|
connect() as conn,
|
|
@@ -95,11 +109,13 @@ async def worker(
|
|
|
95
109
|
"request_id": _get_request_id(run),
|
|
96
110
|
}
|
|
97
111
|
)
|
|
98
|
-
|
|
112
|
+
run_stream_started_at = datetime.now(UTC)
|
|
99
113
|
await logger.ainfo(
|
|
100
114
|
"Starting background run",
|
|
101
115
|
run_started_at=run_started_at.isoformat(),
|
|
116
|
+
run_creation_ms=run_creation_ms,
|
|
102
117
|
run_queue_ms=ms(run_started_at, run["created_at"]),
|
|
118
|
+
run_stream_start_ms=ms(run_stream_started_at, run_started_at),
|
|
103
119
|
)
|
|
104
120
|
|
|
105
121
|
def on_checkpoint(checkpoint_arg: CheckpointPayload):
|
|
@@ -115,7 +131,15 @@ async def worker(
|
|
|
115
131
|
|
|
116
132
|
try:
|
|
117
133
|
if attempt > BG_JOB_MAX_RETRIES:
|
|
118
|
-
await logger.aerror(
|
|
134
|
+
await logger.aerror(
|
|
135
|
+
"Run exceeded max attempts",
|
|
136
|
+
run_id=run["run_id"],
|
|
137
|
+
run_completed_in_ms=(
|
|
138
|
+
int((time.time() * 1_000) - request_created_at)
|
|
139
|
+
if request_created_at is not None
|
|
140
|
+
else None
|
|
141
|
+
),
|
|
142
|
+
)
|
|
119
143
|
|
|
120
144
|
error_message = (
|
|
121
145
|
f"Run {run['run_id']} exceeded max attempts ({BG_JOB_MAX_RETRIES}).\n\n"
|
|
@@ -131,13 +155,12 @@ async def worker(
|
|
|
131
155
|
)
|
|
132
156
|
|
|
133
157
|
raise RuntimeError(error_message)
|
|
158
|
+
exit_stack = AsyncExitStack()
|
|
134
159
|
if temporary:
|
|
135
|
-
stream = astream_state(
|
|
136
|
-
AsyncExitStack(), conn, cast(Run, run), attempt, done
|
|
137
|
-
)
|
|
160
|
+
stream = astream_state(exit_stack, conn, cast(Run, run), attempt, done)
|
|
138
161
|
else:
|
|
139
162
|
stream = astream_state(
|
|
140
|
-
|
|
163
|
+
exit_stack,
|
|
141
164
|
conn,
|
|
142
165
|
cast(Run, run),
|
|
143
166
|
attempt,
|
|
@@ -149,7 +172,8 @@ async def worker(
|
|
|
149
172
|
consume(stream, run_id, resumable),
|
|
150
173
|
BG_JOB_TIMEOUT_SECS,
|
|
151
174
|
)
|
|
152
|
-
|
|
175
|
+
run_ended_at_dt = datetime.now(UTC)
|
|
176
|
+
run_ended_at = run_ended_at_dt.isoformat()
|
|
153
177
|
await logger.ainfo(
|
|
154
178
|
"Background run succeeded",
|
|
155
179
|
run_id=str(run_id),
|
|
@@ -157,7 +181,12 @@ async def worker(
|
|
|
157
181
|
run_created_at=run_created_at,
|
|
158
182
|
run_started_at=run_started_at.isoformat(),
|
|
159
183
|
run_ended_at=run_ended_at,
|
|
160
|
-
run_exec_ms=ms(
|
|
184
|
+
run_exec_ms=ms(run_ended_at_dt, run_started_at),
|
|
185
|
+
run_completed_in_ms=(
|
|
186
|
+
int((run_ended_at_dt.timestamp() * 1_000) - request_created_at)
|
|
187
|
+
if request_created_at is not None
|
|
188
|
+
else None
|
|
189
|
+
),
|
|
161
190
|
)
|
|
162
191
|
status = "success"
|
|
163
192
|
await Runs.set_status(conn, run_id, "success")
|
|
@@ -173,13 +202,21 @@ async def worker(
|
|
|
173
202
|
run_started_at=run_started_at.isoformat(),
|
|
174
203
|
run_ended_at=run_ended_at,
|
|
175
204
|
run_exec_ms=ms(datetime.now(UTC), run_started_at),
|
|
205
|
+
run_completed_in_ms=(
|
|
206
|
+
int((run_ended_at_dt.timestamp() * 1_000) - request_created_at)
|
|
207
|
+
if request_created_at is not None
|
|
208
|
+
else None
|
|
209
|
+
),
|
|
176
210
|
)
|
|
177
211
|
await Runs.set_status(conn, run_id, "timeout")
|
|
178
212
|
except UserRollback as e:
|
|
179
213
|
exception = e
|
|
180
214
|
status = "rollback"
|
|
181
|
-
|
|
215
|
+
run_ended_at_dt = datetime.now(UTC)
|
|
216
|
+
run_ended_at = run_ended_at_dt.isoformat()
|
|
182
217
|
try:
|
|
218
|
+
# Need to close the pipeline to re-use the connection
|
|
219
|
+
await exit_stack.aclose()
|
|
183
220
|
await Runs.delete(conn, run_id, thread_id=run["thread_id"])
|
|
184
221
|
await logger.ainfo(
|
|
185
222
|
"Background run rolled back",
|
|
@@ -188,7 +225,12 @@ async def worker(
|
|
|
188
225
|
run_created_at=run_created_at,
|
|
189
226
|
run_started_at=run_started_at.isoformat(),
|
|
190
227
|
run_ended_at=run_ended_at,
|
|
191
|
-
run_exec_ms=ms(
|
|
228
|
+
run_exec_ms=ms(run_ended_at_dt, run_started_at),
|
|
229
|
+
run_completed_in_ms=(
|
|
230
|
+
int((run_ended_at_dt.timestamp() * 1_000) - request_created_at)
|
|
231
|
+
if request_created_at is not None
|
|
232
|
+
else None
|
|
233
|
+
),
|
|
192
234
|
)
|
|
193
235
|
|
|
194
236
|
except InFailedSqlTransaction as e:
|
|
@@ -199,9 +241,6 @@ async def worker(
|
|
|
199
241
|
run_created_at=run_created_at,
|
|
200
242
|
exc=str(e),
|
|
201
243
|
)
|
|
202
|
-
# We need to clean up the transaction early if we want to
|
|
203
|
-
# update the thread status with the same connection
|
|
204
|
-
await exit.aclose()
|
|
205
244
|
except HTTPException as e:
|
|
206
245
|
if e.status_code == 404:
|
|
207
246
|
await logger.ainfo(
|
|
@@ -217,7 +256,8 @@ async def worker(
|
|
|
217
256
|
except UserInterrupt as e:
|
|
218
257
|
exception = e
|
|
219
258
|
status = "interrupted"
|
|
220
|
-
|
|
259
|
+
run_ended_at_dt = datetime.now(UTC)
|
|
260
|
+
run_ended_at = run_ended_at_dt.isoformat()
|
|
221
261
|
await logger.ainfo(
|
|
222
262
|
"Background run interrupted",
|
|
223
263
|
run_id=str(run_id),
|
|
@@ -225,13 +265,19 @@ async def worker(
|
|
|
225
265
|
run_created_at=run_created_at,
|
|
226
266
|
run_started_at=run_started_at.isoformat(),
|
|
227
267
|
run_ended_at=run_ended_at,
|
|
228
|
-
run_exec_ms=ms(
|
|
268
|
+
run_exec_ms=ms(run_ended_at_dt, run_started_at),
|
|
269
|
+
run_completed_in_ms=(
|
|
270
|
+
int((run_ended_at_dt.timestamp() * 1_000) - request_created_at)
|
|
271
|
+
if request_created_at is not None
|
|
272
|
+
else None
|
|
273
|
+
),
|
|
229
274
|
)
|
|
230
275
|
await Runs.set_status(conn, run_id, "interrupted")
|
|
231
276
|
except RETRIABLE_EXCEPTIONS as e:
|
|
232
277
|
exception = e
|
|
233
278
|
status = "retry"
|
|
234
|
-
|
|
279
|
+
run_ended_at_dt = datetime.now(UTC)
|
|
280
|
+
run_ended_at = run_ended_at_dt.isoformat()
|
|
235
281
|
await logger.awarning(
|
|
236
282
|
f"Background run failed, will retry. Exception: {e}",
|
|
237
283
|
exc_info=True,
|
|
@@ -240,14 +286,15 @@ async def worker(
|
|
|
240
286
|
run_created_at=run_created_at,
|
|
241
287
|
run_started_at=run_started_at.isoformat(),
|
|
242
288
|
run_ended_at=run_ended_at,
|
|
243
|
-
run_exec_ms=ms(
|
|
289
|
+
run_exec_ms=ms(run_ended_at_dt, run_started_at),
|
|
244
290
|
)
|
|
245
291
|
await Runs.set_status(conn, run_id, "pending")
|
|
246
292
|
raise
|
|
247
293
|
except Exception as exc:
|
|
248
294
|
exception = exc
|
|
249
295
|
status = "error"
|
|
250
|
-
|
|
296
|
+
run_ended_at_dt = datetime.now(UTC)
|
|
297
|
+
run_ended_at = run_ended_at_dt.isoformat()
|
|
251
298
|
await logger.aexception(
|
|
252
299
|
f"Background run failed. Exception: {exc}",
|
|
253
300
|
exc_info=not isinstance(exc, RemoteException),
|
|
@@ -256,7 +303,7 @@ async def worker(
|
|
|
256
303
|
run_created_at=run_created_at,
|
|
257
304
|
run_started_at=run_started_at.isoformat(),
|
|
258
305
|
run_ended_at=run_ended_at,
|
|
259
|
-
run_exec_ms=ms(
|
|
306
|
+
run_exec_ms=ms(run_ended_at_dt, run_started_at),
|
|
260
307
|
)
|
|
261
308
|
await Runs.set_status(conn, run_id, "error")
|
|
262
309
|
set_auth_ctx(None, None)
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.2.40"
|
|
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
|