langgraph-api 0.2.78__tar.gz → 0.2.85__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 (108) hide show
  1. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/PKG-INFO +1 -1
  2. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/benchmark/README.md +12 -2
  3. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/benchmark/burst.js +14 -7
  4. langgraph_api-0.2.85/langgraph_api/__init__.py +1 -0
  5. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_api/api/__init__.py +13 -1
  6. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_api/api/meta.py +2 -0
  7. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_api/api/runs.py +7 -1
  8. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_api/cli.py +1 -1
  9. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_api/config.py +1 -1
  10. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_api/js/remote.py +24 -2
  11. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_api/metadata.py +72 -21
  12. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_api/serde.py +13 -7
  13. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_api/worker.py +3 -17
  14. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/uv.lock +54 -58
  15. langgraph_api-0.2.78/langgraph_api/__init__.py +0 -1
  16. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/.gitignore +0 -0
  17. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/LICENSE +0 -0
  18. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/Makefile +0 -0
  19. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/README.md +0 -0
  20. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/benchmark/.gitignore +0 -0
  21. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/benchmark/Makefile +0 -0
  22. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/benchmark/weather.js +0 -0
  23. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/constraints.txt +0 -0
  24. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/forbidden.txt +0 -0
  25. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/healthcheck.py +0 -0
  26. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_api/api/assistants.py +0 -0
  27. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_api/api/mcp.py +0 -0
  28. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_api/api/openapi.py +0 -0
  29. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_api/api/store.py +0 -0
  30. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_api/api/threads.py +0 -0
  31. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_api/api/ui.py +0 -0
  32. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_api/asgi_transport.py +0 -0
  33. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_api/asyncio.py +0 -0
  34. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_api/auth/__init__.py +0 -0
  35. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_api/auth/custom.py +0 -0
  36. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_api/auth/langsmith/__init__.py +0 -0
  37. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_api/auth/langsmith/backend.py +0 -0
  38. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_api/auth/langsmith/client.py +0 -0
  39. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_api/auth/middleware.py +0 -0
  40. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_api/auth/noop.py +0 -0
  41. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_api/auth/studio_user.py +0 -0
  42. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_api/command.py +0 -0
  43. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_api/cron_scheduler.py +0 -0
  44. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_api/errors.py +0 -0
  45. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_api/graph.py +0 -0
  46. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_api/http.py +0 -0
  47. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_api/http_metrics.py +0 -0
  48. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_api/js/.gitignore +0 -0
  49. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_api/js/.prettierrc +0 -0
  50. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_api/js/__init__.py +0 -0
  51. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_api/js/base.py +0 -0
  52. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_api/js/build.mts +0 -0
  53. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_api/js/client.http.mts +0 -0
  54. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_api/js/client.mts +0 -0
  55. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_api/js/errors.py +0 -0
  56. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_api/js/global.d.ts +0 -0
  57. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_api/js/package.json +0 -0
  58. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_api/js/schema.py +0 -0
  59. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_api/js/src/graph.mts +0 -0
  60. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_api/js/src/load.hooks.mjs +0 -0
  61. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_api/js/src/preload.mjs +0 -0
  62. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_api/js/src/utils/files.mts +0 -0
  63. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_api/js/src/utils/importMap.mts +0 -0
  64. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_api/js/src/utils/pythonSchemas.mts +0 -0
  65. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_api/js/src/utils/serde.mts +0 -0
  66. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_api/js/sse.py +0 -0
  67. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_api/js/tsconfig.json +0 -0
  68. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_api/js/ui.py +0 -0
  69. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_api/js/yarn.lock +0 -0
  70. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_api/logging.py +0 -0
  71. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_api/middleware/__init__.py +0 -0
  72. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_api/middleware/http_logger.py +0 -0
  73. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_api/middleware/private_network.py +0 -0
  74. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_api/middleware/request_id.py +0 -0
  75. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_api/models/__init__.py +0 -0
  76. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_api/models/run.py +0 -0
  77. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_api/patch.py +0 -0
  78. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_api/queue_entrypoint.py +0 -0
  79. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_api/route.py +0 -0
  80. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_api/schema.py +0 -0
  81. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_api/server.py +0 -0
  82. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_api/sse.py +0 -0
  83. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_api/state.py +0 -0
  84. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_api/store.py +0 -0
  85. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_api/stream.py +0 -0
  86. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_api/thread_ttl.py +0 -0
  87. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_api/tunneling/cloudflare.py +0 -0
  88. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_api/utils/__init__.py +0 -0
  89. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_api/utils/config.py +0 -0
  90. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_api/utils/future.py +0 -0
  91. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_api/utils.py +0 -0
  92. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_api/validation.py +0 -0
  93. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_api/webhook.py +0 -0
  94. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_license/__init__.py +0 -0
  95. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_license/validation.py +0 -0
  96. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_runtime/__init__.py +0 -0
  97. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_runtime/checkpoint.py +0 -0
  98. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_runtime/database.py +0 -0
  99. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_runtime/lifespan.py +0 -0
  100. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_runtime/metrics.py +0 -0
  101. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_runtime/ops.py +0 -0
  102. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_runtime/queue.py +0 -0
  103. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_runtime/retry.py +0 -0
  104. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/langgraph_runtime/store.py +0 -0
  105. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/logging.json +0 -0
  106. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/openapi.json +0 -0
  107. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/pyproject.toml +0 -0
  108. {langgraph_api-0.2.78 → langgraph_api-0.2.85}/scripts/create_license.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: langgraph-api
3
- Version: 0.2.78
3
+ Version: 0.2.85
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
@@ -6,7 +6,14 @@ K6 is a modern load testing tool that allows you to test the performance and rel
6
6
 
7
7
  ### Available Tests
8
8
 
9
- 1. Burst - Kick off a burst of /run/wait requests.
9
+ We use a local benchmark agent that has a MODE that can be any of the following:
10
+ - `single` - Run a single node
11
+ - `parallel` - Run EXPAND nodes in parallel
12
+ - `sequential` - Run EXPAND nodes in sequence
13
+
14
+ By default, MODE is `single` and EXPAND is 50.
15
+
16
+ 1. Burst - Kick off a burst of /run/wait requests. Default BURST_SIZE is 100.
10
17
 
11
18
  ## Running Tests Locally
12
19
 
@@ -19,12 +26,15 @@ K6 is a modern load testing tool that allows you to test the performance and rel
19
26
  ### Basic Usage
20
27
 
21
28
  ```bash
22
- # Run burst test with default burst size (50)
29
+ # Run burst test with default burst size
23
30
  make benchmark-burst
24
31
 
25
32
  # Run burst test with custom burst size
26
33
  BURST_SIZE=500 make benchmark-burst
27
34
 
35
+ # Run burst test with a different mode and expand size
36
+ MODE='parallel' EXPAND=100 make benchmark-burst
37
+
28
38
  # Run burst test against a deployment
29
39
  BASE_URL=https://jdr-debug-31ac2c83eef557309f21c1e98d822025.us.langgraph.app make benchmark-burst
30
40
 
@@ -15,7 +15,9 @@ const burstSuccessRate = new Rate('burst_success_rate');
15
15
 
16
16
  // URL of your LangGraph server
17
17
  const BASE_URL = __ENV.BASE_URL || 'http://localhost:9123';
18
- const BURST_SIZE = parseInt(__ENV.BURST_SIZE || '50');
18
+ const BURST_SIZE = parseInt(__ENV.BURST_SIZE || '100');
19
+ const MODE = __ENV.MODE || 'single';
20
+ const EXPAND = parseInt(__ENV.EXPAND || '50');
19
21
 
20
22
  // Burst testing configuration
21
23
  export let options = {
@@ -28,8 +30,8 @@ export let options = {
28
30
  },
29
31
  },
30
32
  thresholds: {
31
- 'run_duration': ['p(95)<3500'],
32
- 'burst_success_rate': ['rate>0.99'],
33
+ 'run_duration': ['p(95)<2000'],
34
+ 'burst_success_rate': ['rate>=0.99'],
33
35
  },
34
36
  };
35
37
 
@@ -50,8 +52,10 @@ export default function() {
50
52
  // Create a payload with the LangGraph agent configuration
51
53
  const payload = JSON.stringify({
52
54
  assistant_id: "benchmark",
53
- config: { delay: 0.1 },
54
- input: {delay: 0.1, random_value: 'hello'},
55
+ input: {mode: MODE, expand: EXPAND},
56
+ config: {
57
+ recursion_limit: EXPAND + 2,
58
+ }
55
59
  });
56
60
 
57
61
  // Make a single request to the wait endpoint
@@ -60,15 +64,18 @@ export default function() {
60
64
  timeout: '35s'
61
65
  });
62
66
 
67
+ // Don't include verification in the duration of the request
68
+ const duration = new Date().getTime() - startTime;
69
+
63
70
  // Check the response
71
+ const expected_length = MODE === 'single' ? 1 : EXPAND + 1;
64
72
  const success = check(response, {
65
73
  'Run completed successfully': (r) => r.status === 200,
66
- 'Response contains data': (r) => r.body.length > 0,
74
+ 'Response contains expected number of messages': (r) => JSON.parse(r.body).messages.length === expected_length,
67
75
  });
68
76
 
69
77
  if (success) {
70
78
  // Record success metrics
71
- const duration = new Date().getTime() - startTime;
72
79
  runDuration.add(duration);
73
80
  successfulRuns.add(1);
74
81
  burstSuccessRate.add(1); // 1 = success
@@ -0,0 +1 @@
1
+ __version__ = "0.2.85"
@@ -1,4 +1,5 @@
1
1
  import asyncio
2
+ import functools
2
3
  import importlib
3
4
  import importlib.util
4
5
  import os
@@ -27,7 +28,11 @@ from langgraph_runtime.database import connect, healthcheck
27
28
  logger = structlog.stdlib.get_logger(__name__)
28
29
 
29
30
 
30
- async def ok(request: Request):
31
+ async def ok(request: Request, *, disabled: bool = False):
32
+ if disabled:
33
+ # We still expose an /ok endpoint even if disable_meta is set so that
34
+ # the operator knows the server started up.
35
+ return JSONResponse({"ok": True})
31
36
  check_db = int(request.query_params.get("check_db", "0")) # must be "0" or "1"
32
37
  if check_db:
33
38
  await healthcheck()
@@ -126,6 +131,13 @@ if HTTP_CONFIG:
126
131
  user_router = load_custom_app(router_import)
127
132
  if not HTTP_CONFIG.get("disable_meta"):
128
133
  routes.extend(meta_routes)
134
+ else:
135
+ # Otherwise the deployment will never be considered healthy
136
+ routes.append(
137
+ Route(
138
+ "/ok", functools.partial(ok, disabled=True), methods=["GET"], name="ok"
139
+ )
140
+ )
129
141
  if protected_routes:
130
142
  routes.append(
131
143
  Mount(
@@ -1,3 +1,4 @@
1
+ import langgraph.version
1
2
  from starlette.responses import JSONResponse, PlainTextResponse
2
3
 
3
4
  from langgraph_api import __version__, config, metadata
@@ -16,6 +17,7 @@ async def meta_info(request: ApiRequest):
16
17
  return JSONResponse(
17
18
  {
18
19
  "version": __version__,
20
+ "langgraph_py_version": langgraph.version.__version__,
19
21
  "flags": {
20
22
  "assistants": True,
21
23
  "crons": plus and config.FF_CRONS_ENABLED,
@@ -172,6 +172,7 @@ async def wait_run(request: ApiRequest):
172
172
  """Create a run, wait for the output."""
173
173
  thread_id = request.path_params["thread_id"]
174
174
  payload = await request.json(RunCreateStateful)
175
+ on_disconnect = payload.get("on_disconnect", "continue")
175
176
  run_id = uuid6()
176
177
  sub = asyncio.create_task(Runs.Stream.subscribe(run_id))
177
178
 
@@ -197,7 +198,10 @@ async def wait_run(request: ApiRequest):
197
198
  vchunk: bytes | None = None
198
199
  async with aclosing(
199
200
  Runs.Stream.join(
200
- run["run_id"], thread_id=run["thread_id"], stream_mode=await sub
201
+ run["run_id"],
202
+ thread_id=run["thread_id"],
203
+ stream_mode=await sub,
204
+ cancel_on_disconnect=on_disconnect == "cancel",
201
205
  )
202
206
  ) as stream:
203
207
  async for mode, chunk, _ in stream:
@@ -248,6 +252,7 @@ async def wait_run(request: ApiRequest):
248
252
  async def wait_run_stateless(request: ApiRequest):
249
253
  """Create a stateless run, wait for the output."""
250
254
  payload = await request.json(RunCreateStateless)
255
+ on_disconnect = payload.get("on_disconnect", "continue")
251
256
  run_id = uuid6()
252
257
  sub = asyncio.create_task(Runs.Stream.subscribe(run_id))
253
258
 
@@ -277,6 +282,7 @@ async def wait_run_stateless(request: ApiRequest):
277
282
  thread_id=run["thread_id"],
278
283
  stream_mode=await sub,
279
284
  ignore_404=True,
285
+ cancel_on_disconnect=on_disconnect == "cancel",
280
286
  )
281
287
  ) as stream:
282
288
  async for mode, chunk, _ in stream:
@@ -346,7 +346,7 @@ def run_server(
346
346
  - 📚 API Docs: \033[36m{local_url}/docs\033[0m
347
347
 
348
348
  This in-memory server is designed for development and testing.
349
- For production use, please use LangGraph Cloud.
349
+ For production use, please use LangGraph Platform.
350
350
 
351
351
  """
352
352
  logger.info(welcome)
@@ -155,7 +155,7 @@ POSTGRES_POOL_MAX_SIZE = env("LANGGRAPH_POSTGRES_POOL_MAX_SIZE", cast=int, defau
155
155
  RESUMABLE_STREAM_TTL_SECONDS = env(
156
156
  "RESUMABLE_STREAM_TTL_SECONDS",
157
157
  cast=int,
158
- default=3600, # 1 hour
158
+ default=120, # 2 minutes
159
159
  )
160
160
 
161
161
 
@@ -1,6 +1,8 @@
1
1
  import asyncio
2
+ import json
2
3
  import logging
3
4
  import os
5
+ import re
4
6
  import shutil
5
7
  import ssl
6
8
  from collections import deque
@@ -452,6 +454,26 @@ async def run_js_http_process(paths_str: str, http_config: dict, watch: bool = F
452
454
  attempt += 1
453
455
 
454
456
 
457
+ _BAD_SURROGATE_RE = re.compile(r"\\u[dD][89a-fA-F][0-9a-fA-F]{2}")
458
+ _BAD_ESCAPE_RE = re.compile(r"\\(?![\"\\/bfnrtu])")
459
+
460
+
461
+ def _safe_json_loads(data: bytes):
462
+ """Attempt *orjson.loads* first; if it fails, repair common escape issues.
463
+
464
+ For a time, we had a bug in our surrogate cleanup in serde.py, which
465
+ allowed sequences containing a stray backslash to be stored which would
466
+ then fail upon loading. This function attempts to repair those sequences.
467
+ """
468
+ try:
469
+ return orjson.loads(data)
470
+ except orjson.JSONDecodeError:
471
+ txt = data.decode("utf-8", "replace")
472
+ txt = _BAD_ESCAPE_RE.sub(r"\\\\", txt)
473
+ txt = _BAD_SURROGATE_RE.sub("", txt)
474
+ return json.loads(txt)
475
+
476
+
455
477
  class PassthroughSerialiser(SerializerProtocol):
456
478
  def dumps(self, obj: Any) -> bytes:
457
479
  return json_dumpb(obj)
@@ -460,13 +482,13 @@ class PassthroughSerialiser(SerializerProtocol):
460
482
  return "json", json_dumpb(obj)
461
483
 
462
484
  def loads(self, data: bytes) -> Any:
463
- return orjson.loads(data)
485
+ return _safe_json_loads(data)
464
486
 
465
487
  def loads_typed(self, data: tuple[str, bytes]) -> Any:
466
488
  type, payload = data
467
489
  if type != "json":
468
490
  raise ValueError(f"Unsupported type {type}")
469
- return orjson.loads(payload)
491
+ return _safe_json_loads(payload)
470
492
 
471
493
 
472
494
  def _get_passthrough_checkpointer():
@@ -55,13 +55,15 @@ RUN_COUNTER = 0
55
55
  NODE_COUNTER = 0
56
56
  FROM_TIMESTAMP = datetime.now(UTC).isoformat()
57
57
 
58
- if (
59
- "api.smith.langchain.com" in LANGSMITH_AUTH_ENDPOINT
60
- and not LANGGRAPH_CLOUD_LICENSE_KEY
61
- ):
62
- METADATA_ENDPOINT = LANGSMITH_AUTH_ENDPOINT.rstrip("/") + "/v1/metadata/submit"
63
- else:
64
- METADATA_ENDPOINT = "https://api.smith.langchain.com/v1/metadata/submit"
58
+ # Beacon endpoint for license key submissions
59
+ BEACON_ENDPOINT = "https://api.smith.langchain.com/v1/metadata/submit"
60
+
61
+ # LangChain auth endpoint for API key submissions
62
+ LANGCHAIN_METADATA_ENDPOINT = None
63
+ if LANGSMITH_AUTH_ENDPOINT:
64
+ LANGCHAIN_METADATA_ENDPOINT = (
65
+ LANGSMITH_AUTH_ENDPOINT.rstrip("/") + "/v1/metadata/submit"
66
+ )
65
67
 
66
68
 
67
69
  def incr_runs(*, incr: int = 1) -> None:
@@ -82,8 +84,10 @@ async def metadata_loop() -> None:
82
84
  if not LANGGRAPH_CLOUD_LICENSE_KEY and not LANGSMITH_API_KEY:
83
85
  return
84
86
 
85
- if LANGGRAPH_CLOUD_LICENSE_KEY and not LANGGRAPH_CLOUD_LICENSE_KEY.startswith(
86
- "lcl_"
87
+ if (
88
+ LANGGRAPH_CLOUD_LICENSE_KEY
89
+ and not LANGGRAPH_CLOUD_LICENSE_KEY.startswith("lcl_")
90
+ and not LANGSMITH_API_KEY
87
91
  ):
88
92
  logger.info("Running in air-gapped mode, skipping metadata loop")
89
93
  return
@@ -102,9 +106,7 @@ async def metadata_loop() -> None:
102
106
  NODE_COUNTER = 0
103
107
  FROM_TIMESTAMP = to_timestamp
104
108
 
105
- payload = {
106
- "license_key": LANGGRAPH_CLOUD_LICENSE_KEY,
107
- "api_key": LANGSMITH_API_KEY,
109
+ base_payload = {
108
110
  "from_timestamp": from_timestamp,
109
111
  "to_timestamp": to_timestamp,
110
112
  "tags": {
@@ -130,17 +132,66 @@ async def metadata_loop() -> None:
130
132
  },
131
133
  "logs": [],
132
134
  }
133
- try:
134
- await http_request(
135
- "POST",
136
- METADATA_ENDPOINT,
137
- body=orjson.dumps(payload),
138
- headers={"Content-Type": "application/json"},
139
- )
140
- except Exception as e:
135
+
136
+ # Track successful submissions
137
+ submissions_attempted = []
138
+ submissions_failed = []
139
+
140
+ # 1. Send to beacon endpoint if license key starts with lcl_
141
+ if LANGGRAPH_CLOUD_LICENSE_KEY and LANGGRAPH_CLOUD_LICENSE_KEY.startswith(
142
+ "lcl_"
143
+ ):
144
+ beacon_payload = {
145
+ **base_payload,
146
+ "license_key": LANGGRAPH_CLOUD_LICENSE_KEY,
147
+ }
148
+ submissions_attempted.append("beacon")
149
+ try:
150
+ await http_request(
151
+ "POST",
152
+ BEACON_ENDPOINT,
153
+ body=orjson.dumps(beacon_payload),
154
+ headers={"Content-Type": "application/json"},
155
+ )
156
+ await logger.ainfo("Successfully submitted metadata to beacon endpoint")
157
+ except Exception as e:
158
+ submissions_failed.append("beacon")
159
+ await logger.awarning(
160
+ "Beacon metadata submission failed.", error=str(e)
161
+ )
162
+
163
+ # 2. Send to langchain auth endpoint if API key is set
164
+ if LANGSMITH_API_KEY and LANGCHAIN_METADATA_ENDPOINT:
165
+ langchain_payload = {
166
+ **base_payload,
167
+ "api_key": LANGSMITH_API_KEY,
168
+ }
169
+ submissions_attempted.append("langchain")
170
+ try:
171
+ await http_request(
172
+ "POST",
173
+ LANGCHAIN_METADATA_ENDPOINT,
174
+ body=orjson.dumps(langchain_payload),
175
+ headers={"Content-Type": "application/json"},
176
+ )
177
+ logger.info("Successfully submitted metadata to LangSmith instance")
178
+ except Exception as e:
179
+ submissions_failed.append("langchain")
180
+ await logger.awarning(
181
+ "LangChain metadata submission failed.", error=str(e)
182
+ )
183
+
184
+ if submissions_attempted and len(submissions_failed) == len(
185
+ submissions_attempted
186
+ ):
141
187
  # retry on next iteration
142
188
  incr_runs(incr=runs)
143
189
  incr_nodes("", incr=nodes)
144
190
  FROM_TIMESTAMP = from_timestamp
145
- await logger.ainfo("Metadata submission skipped.", error=str(e))
191
+ await logger.awarning(
192
+ "All metadata submissions failed, will retry",
193
+ attempted=submissions_attempted,
194
+ failed=submissions_failed,
195
+ )
196
+
146
197
  await asyncio.sleep(INTERVAL)
@@ -123,16 +123,18 @@ def _sanitise(o: Any) -> Any:
123
123
 
124
124
  def json_dumpb(obj) -> bytes:
125
125
  try:
126
- return orjson.dumps(obj, default=default, option=_option).replace(
127
- rb"\u0000", b""
128
- ) # null unicode char not allowed in json
126
+ dumped = orjson.dumps(obj, default=default, option=_option)
129
127
  except TypeError as e:
130
128
  if "surrogates not allowed" not in str(e):
131
129
  raise
132
- surrogate_sanitized = _sanitise(obj)
133
- return orjson.dumps(
134
- surrogate_sanitized, default=default, option=_option
135
- ).replace(rb"\u0000", b"")
130
+ dumped = orjson.dumps(_sanitise(obj), default=default, option=_option)
131
+ return (
132
+ # Unfortunately simply doing ``.replace(rb"\\u0000", b"")`` on
133
+ # the dumped bytes can leave an **orphaned back-slash** (e.g. ``\\q``)
134
+ # which makes the resulting JSON invalid. The fix is to delete the *double*
135
+ # back-slash form **first**, then (optionally) the single-escapes.
136
+ dumped.replace(rb"\\u0000", b"").replace(rb"\u0000", b"")
137
+ )
136
138
 
137
139
 
138
140
  def json_loads(content: bytes | Fragment | dict) -> Any:
@@ -154,6 +156,10 @@ class Serializer(JsonPlusSerializer):
154
156
  except TypeError:
155
157
  return "pickle", cloudpickle.dumps(obj)
156
158
 
159
+ def dumps(self, obj: Any) -> bytes:
160
+ # See comment above (in json_dumpb)
161
+ return super().dumps(obj).replace(rb"\\u0000", b"").replace(rb"\u0000", b"")
162
+
157
163
  def loads_typed(self, data: tuple[str, bytes]) -> Any:
158
164
  if data[0] == "pickle":
159
165
  try:
@@ -307,23 +307,9 @@ async def worker(
307
307
  conn, run["thread_id"], run_id, status, checkpoint, exception
308
308
  )
309
309
 
310
- # delete or set status of thread
311
- if not isinstance(exception, ALL_RETRIABLE_EXCEPTIONS):
312
- if temporary:
313
- await Threads.delete(conn, run["thread_id"])
314
- else:
315
- try:
316
- await Threads.set_status(
317
- conn, run["thread_id"], checkpoint, exception
318
- )
319
- except HTTPException as e:
320
- if e.status_code == 404:
321
- await logger.ainfo(
322
- "Ignoring set_status error for missing thread",
323
- exc=str(e),
324
- )
325
- else:
326
- raise
310
+ # delete thread if it's temporary and we don't want to retry
311
+ if temporary and not isinstance(exception, ALL_RETRIABLE_EXCEPTIONS):
312
+ await Threads.delete(conn, run["thread_id"])
327
313
 
328
314
  if isinstance(exception, ALL_RETRIABLE_EXCEPTIONS):
329
315
  await logger.awarning("RETRYING", exc_info=exception)
@@ -1,10 +1,6 @@
1
1
  version = 1
2
2
  revision = 2
3
3
  requires-python = ">=3.11"
4
- resolution-markers = [
5
- "python_full_version >= '3.12.4'",
6
- "python_full_version < '3.12.4'",
7
- ]
8
4
 
9
5
  [[package]]
10
6
  name = "aiosqlite"
@@ -55,11 +51,11 @@ wheels = [
55
51
 
56
52
  [[package]]
57
53
  name = "certifi"
58
- version = "2025.6.15"
54
+ version = "2025.7.9"
59
55
  source = { registry = "https://pypi.org/simple" }
60
- sdist = { url = "https://files.pythonhosted.org/packages/73/f7/f14b46d4bcd21092d7d3ccef689615220d8a08fb25e564b65d20738e672e/certifi-2025.6.15.tar.gz", hash = "sha256:d747aa5a8b9bbbb1bb8c22bb13e22bd1f18e9796defa16bab421f7f7a317323b", size = 158753, upload-time = "2025-06-15T02:45:51.329Z" }
56
+ sdist = { url = "https://files.pythonhosted.org/packages/de/8a/c729b6b60c66a38f590c4e774decc4b2ec7b0576be8f1aa984a53ffa812a/certifi-2025.7.9.tar.gz", hash = "sha256:c1d2ec05395148ee10cf672ffc28cd37ea0ab0d99f9cc74c43e588cbd111b079", size = 160386, upload-time = "2025-07-09T02:13:58.874Z" }
61
57
  wheels = [
62
- { url = "https://files.pythonhosted.org/packages/84/ae/320161bd181fc06471eed047ecce67b693fd7515b16d495d8932db763426/certifi-2025.6.15-py3-none-any.whl", hash = "sha256:2e0c7ce7cb5d8f8634ca55d2ba7e6ec2689a2fd6537d8dec1296a477a4910057", size = 157650, upload-time = "2025-06-15T02:45:49.977Z" },
58
+ { url = "https://files.pythonhosted.org/packages/66/f3/80a3f974c8b535d394ff960a11ac20368e06b736da395b551a49ce950cce/certifi-2025.7.9-py3-none-any.whl", hash = "sha256:d842783a14f8fdd646895ac26f719a061408834473cfc10203f6a575beb15d39", size = 159230, upload-time = "2025-07-09T02:13:57.007Z" },
63
59
  ]
64
60
 
65
61
  [[package]]
@@ -237,16 +233,16 @@ wheels = [
237
233
 
238
234
  [[package]]
239
235
  name = "fastapi"
240
- version = "0.115.13"
236
+ version = "0.116.0"
241
237
  source = { registry = "https://pypi.org/simple" }
242
238
  dependencies = [
243
239
  { name = "pydantic" },
244
240
  { name = "starlette" },
245
241
  { name = "typing-extensions" },
246
242
  ]
247
- sdist = { url = "https://files.pythonhosted.org/packages/20/64/ec0788201b5554e2a87c49af26b77a4d132f807a0fa9675257ac92c6aa0e/fastapi-0.115.13.tar.gz", hash = "sha256:55d1d25c2e1e0a0a50aceb1c8705cd932def273c102bff0b1c1da88b3c6eb307", size = 295680, upload-time = "2025-06-17T11:49:45.575Z" }
243
+ sdist = { url = "https://files.pythonhosted.org/packages/20/38/e1da78736143fd885c36213a3ccc493c384ae8fea6a0f0bc272ef42ebea8/fastapi-0.116.0.tar.gz", hash = "sha256:80dc0794627af0390353a6d1171618276616310d37d24faba6648398e57d687a", size = 296518, upload-time = "2025-07-07T15:09:27.82Z" }
248
244
  wheels = [
249
- { url = "https://files.pythonhosted.org/packages/59/4a/e17764385382062b0edbb35a26b7cf76d71e27e456546277a42ba6545c6e/fastapi-0.115.13-py3-none-any.whl", hash = "sha256:0a0cab59afa7bab22f5eb347f8c9864b681558c278395e94035a741fc10cd865", size = 95315, upload-time = "2025-06-17T11:49:44.106Z" },
245
+ { url = "https://files.pythonhosted.org/packages/2f/68/d80347fe2360445b5f58cf290e588a4729746e7501080947e6cdae114b1f/fastapi-0.116.0-py3-none-any.whl", hash = "sha256:fdcc9ed272eaef038952923bef2b735c02372402d1203ee1210af4eea7a78d2b", size = 95625, upload-time = "2025-07-07T15:09:26.348Z" },
250
246
  ]
251
247
 
252
248
  [[package]]
@@ -362,7 +358,7 @@ wheels = [
362
358
 
363
359
  [[package]]
364
360
  name = "langchain-core"
365
- version = "0.3.65"
361
+ version = "0.3.68"
366
362
  source = { registry = "https://pypi.org/simple" }
367
363
  dependencies = [
368
364
  { name = "jsonpatch" },
@@ -373,14 +369,14 @@ dependencies = [
373
369
  { name = "tenacity" },
374
370
  { name = "typing-extensions" },
375
371
  ]
376
- sdist = { url = "https://files.pythonhosted.org/packages/04/8a/d08c83195d1ef26c42728412ab92ab08211893906b283abce65775e21327/langchain_core-0.3.65.tar.gz", hash = "sha256:54b5e0c8d9bb405415c3211da508ef9cfe0acbe5b490d1b4a15664408ee82d9b", size = 558557, upload-time = "2025-06-10T20:08:28.94Z" }
372
+ sdist = { url = "https://files.pythonhosted.org/packages/23/20/f5b18a17bfbe3416177e702ab2fd230b7d168abb17be31fb48f43f0bb772/langchain_core-0.3.68.tar.gz", hash = "sha256:312e1932ac9aa2eaf111b70fdc171776fa571d1a86c1f873dcac88a094b19c6f", size = 563041, upload-time = "2025-07-03T17:02:28.704Z" }
377
373
  wheels = [
378
- { url = "https://files.pythonhosted.org/packages/54/f0/31db18b7b8213266aed926ce89b5bdd84ccde7ee2edf4cab14e3dd2bfcf1/langchain_core-0.3.65-py3-none-any.whl", hash = "sha256:80e8faf6e9f331f8ef728f3fe793549f1d3fb244fcf9e1bdcecab6a6f4669394", size = 438052, upload-time = "2025-06-10T20:08:27.393Z" },
374
+ { url = "https://files.pythonhosted.org/packages/f9/da/c89be0a272993bfcb762b2a356b9f55de507784c2755ad63caec25d183bf/langchain_core-0.3.68-py3-none-any.whl", hash = "sha256:5e5c1fbef419590537c91b8c2d86af896fbcbaf0d5ed7fdcdd77f7d8f3467ba0", size = 441405, upload-time = "2025-07-03T17:02:27.115Z" },
379
375
  ]
380
376
 
381
377
  [[package]]
382
378
  name = "langgraph"
383
- version = "0.5.0"
379
+ version = "0.5.2"
384
380
  source = { registry = "https://pypi.org/simple" }
385
381
  dependencies = [
386
382
  { name = "langchain-core" },
@@ -390,9 +386,9 @@ dependencies = [
390
386
  { name = "pydantic" },
391
387
  { name = "xxhash" },
392
388
  ]
393
- sdist = { url = "https://files.pythonhosted.org/packages/9b/5f/f08123cbfa0384b6a4011a9547fdbca52b53d51d69b21ff2a03332fde694/langgraph-0.5.0.tar.gz", hash = "sha256:7ad6d42f2a44e93e225cc65c59fac51c55ae549c9824adc22971d00e5ac26443", size = 434232, upload-time = "2025-06-26T22:55:18.068Z" }
389
+ sdist = { url = "https://files.pythonhosted.org/packages/c0/18/1e255fc8c36ff5056d797d83c9ca9fd926683ace294a9ba38c4b40599237/langgraph-0.5.2.tar.gz", hash = "sha256:393b767e9d6a129636a9df36edc492499336c71e4ee268e64b9d1299d30e636c", size = 442564, upload-time = "2025-07-09T19:15:20.219Z" }
394
390
  wheels = [
395
- { url = "https://files.pythonhosted.org/packages/d2/a4/0d5551ef675d382497765c74cdd893e2758d2dc2041e0f3671f110761a04/langgraph-0.5.0-py3-none-any.whl", hash = "sha256:74e33efb6527602b79bfcc4e3d0bfbb15b8d86fa25bb417fdd8d3306456cf8de", size = 143658, upload-time = "2025-06-26T22:55:16.599Z" },
391
+ { url = "https://files.pythonhosted.org/packages/3b/44/6e6c41a3cc00d533dc91cc5f086862b1fccf34aa0ec9605a2cbd116c6ba0/langgraph-0.5.2-py3-none-any.whl", hash = "sha256:db6b8053bf99887957fe45ec27918f8819c4bba269afde88b538e00e9301a581", size = 143735, upload-time = "2025-07-09T19:15:18.733Z" },
396
392
  ]
397
393
 
398
394
  [[package]]
@@ -512,28 +508,28 @@ wheels = [
512
508
 
513
509
  [[package]]
514
510
  name = "langgraph-cli"
515
- version = "0.3.3"
511
+ version = "0.3.4"
516
512
  source = { registry = "https://pypi.org/simple" }
517
513
  dependencies = [
518
514
  { name = "click" },
519
515
  { name = "langgraph-sdk" },
520
516
  ]
521
- sdist = { url = "https://files.pythonhosted.org/packages/f0/c2/53aaae208a3a08f727ef9d03edfd8f499403e017fc451c8ca5b52e95c930/langgraph_cli-0.3.3.tar.gz", hash = "sha256:120adc44064786bb11f1376a7b324b2125276a2e2c3a04bbfab7b8c1622ad4d7", size = 722842, upload-time = "2025-06-13T20:17:52.639Z" }
517
+ sdist = { url = "https://files.pythonhosted.org/packages/fe/ee/41f54032b2ab64c06e66e7f5e7a6c22d9159f2bff6bf08a38c9f11f84753/langgraph_cli-0.3.4.tar.gz", hash = "sha256:6300df4fc6f7106fd5fcdba2cbec9e8b1158daa6760d41333d1b3b5999280ad0", size = 728156, upload-time = "2025-07-08T19:52:24.687Z" }
522
518
  wheels = [
523
- { url = "https://files.pythonhosted.org/packages/50/aa/f52742987a30f9f2d80c00e1caccb310185dd511d07b56c3e611fe31adb5/langgraph_cli-0.3.3-py3-none-any.whl", hash = "sha256:a0c6b04d6a37191431ede17f7a358f1944c6e5ac9e1b7d2004b9f973fe940094", size = 35880, upload-time = "2025-06-13T20:17:51.437Z" },
519
+ { url = "https://files.pythonhosted.org/packages/e2/9c/310dae8c638477e2f0e5744726d4b283878c1e11639fa693bf24b23cf7ac/langgraph_cli-0.3.4-py3-none-any.whl", hash = "sha256:b3ac9fbc67cec5d0295c23a9e7a9014f34502639fb52b2d02c89b3bb2ba36c33", size = 36525, upload-time = "2025-07-08T19:52:23.351Z" },
524
520
  ]
525
521
 
526
522
  [[package]]
527
523
  name = "langgraph-prebuilt"
528
- version = "0.5.0"
524
+ version = "0.5.2"
529
525
  source = { registry = "https://pypi.org/simple" }
530
526
  dependencies = [
531
527
  { name = "langchain-core" },
532
528
  { name = "langgraph-checkpoint" },
533
529
  ]
534
- sdist = { url = "https://files.pythonhosted.org/packages/66/e6/5c9a6b283a088cd4897bded76d5585362674ebfbadce766a2c386c2bc32e/langgraph_prebuilt-0.5.0.tar.gz", hash = "sha256:c295f31328462034f457a1560c7d87e6433571be2a7aa3a16dd8e17199b79493", size = 117834, upload-time = "2025-06-26T22:50:21.449Z" }
530
+ sdist = { url = "https://files.pythonhosted.org/packages/bb/11/98134c47832fbde0caf0e06f1a104577da9215c358d7854093c1d835b272/langgraph_prebuilt-0.5.2.tar.gz", hash = "sha256:2c900a5be0d6a93ea2521e0d931697cad2b646f1fcda7aa5c39d8d7539772465", size = 117808, upload-time = "2025-06-30T19:52:48.307Z" }
535
531
  wheels = [
536
- { url = "https://files.pythonhosted.org/packages/ed/f9/fe6a8100b2cd1d0e8a5d920addc42a8f2a852296b1968b729f8a794ff178/langgraph_prebuilt-0.5.0-py3-none-any.whl", hash = "sha256:de28282048e03d2198d41ec114e57a2c4bff3421039c4828b6c72720dfe5bcb5", size = 23794, upload-time = "2025-06-26T22:50:20.386Z" },
532
+ { url = "https://files.pythonhosted.org/packages/c3/64/6bc45ab9e0e1112698ebff579fe21f5606ea65cd08266995a357e312a4d2/langgraph_prebuilt-0.5.2-py3-none-any.whl", hash = "sha256:1f4cd55deca49dffc3e5127eec12fcd244fc381321002f728afa88642d5ec59d", size = 23776, upload-time = "2025-06-30T19:52:47.494Z" },
537
533
  ]
538
534
 
539
535
  [[package]]
@@ -571,20 +567,20 @@ dev = [
571
567
 
572
568
  [[package]]
573
569
  name = "langgraph-sdk"
574
- version = "0.1.71"
570
+ version = "0.1.72"
575
571
  source = { registry = "https://pypi.org/simple" }
576
572
  dependencies = [
577
573
  { name = "httpx" },
578
574
  { name = "orjson" },
579
575
  ]
580
- sdist = { url = "https://files.pythonhosted.org/packages/f8/c1/aee46cb835aa0e94125bd0d84ddb54424d1b78ebf481dec348a7389d3c89/langgraph_sdk-0.1.71.tar.gz", hash = "sha256:14053a3e7a426026f013a035f67d791faa8a2476c81a4cb600316be574ac08e4", size = 71560, upload-time = "2025-06-26T18:32:08.625Z" }
576
+ sdist = { url = "https://files.pythonhosted.org/packages/c0/a6/cf13ace9bc7f0e8b13852ced0b37ece97f3140e232821c28bc852f8c1ea2/langgraph_sdk-0.1.72.tar.gz", hash = "sha256:396d8195881830700e2d54a0a9ee273e8b1173428e667502ef9c182a3cec7ab7", size = 71600, upload-time = "2025-06-27T01:12:03.788Z" }
581
577
  wheels = [
582
- { url = "https://files.pythonhosted.org/packages/f0/78/1739e8e05ba9452f251431bd17e74843d10a0a01b66ff0692e10d15ff34b/langgraph_sdk-0.1.71-py3-none-any.whl", hash = "sha256:df14c6781520fefeb5031699e60ca2168703b9ccd5b3b45b39e4616e0a401f8e", size = 50088, upload-time = "2025-06-26T18:32:07.387Z" },
578
+ { url = "https://files.pythonhosted.org/packages/4b/4b/d56b51da08d168c2315cd092faa47bc83388b116756dbd6995026ec9ba3f/langgraph_sdk-0.1.72-py3-none-any.whl", hash = "sha256:925d3fcc7a26361db04f9c4beb3ec05bc36361b2a836d181ff2ab145071ec3ce", size = 50129, upload-time = "2025-06-27T01:12:02.449Z" },
583
579
  ]
584
580
 
585
581
  [[package]]
586
582
  name = "langsmith"
587
- version = "0.3.45"
583
+ version = "0.4.4"
588
584
  source = { registry = "https://pypi.org/simple" }
589
585
  dependencies = [
590
586
  { name = "httpx" },
@@ -595,9 +591,9 @@ dependencies = [
595
591
  { name = "requests-toolbelt" },
596
592
  { name = "zstandard" },
597
593
  ]
598
- sdist = { url = "https://files.pythonhosted.org/packages/be/86/b941012013260f95af2e90a3d9415af4a76a003a28412033fc4b09f35731/langsmith-0.3.45.tar.gz", hash = "sha256:1df3c6820c73ed210b2c7bc5cdb7bfa19ddc9126cd03fdf0da54e2e171e6094d", size = 348201, upload-time = "2025-06-05T05:10:28.948Z" }
594
+ sdist = { url = "https://files.pythonhosted.org/packages/20/c8/8d2e0fc438d2d3d8d4300f7684ea30a754344ed00d7ba9cc2705241d2a5f/langsmith-0.4.4.tar.gz", hash = "sha256:70c53bbff24a7872e88e6fa0af98270f4986a6e364f9e85db1cc5636defa4d66", size = 352105, upload-time = "2025-06-27T19:20:36.207Z" }
599
595
  wheels = [
600
- { url = "https://files.pythonhosted.org/packages/6a/f4/c206c0888f8a506404cb4f16ad89593bdc2f70cf00de26a1a0a7a76ad7a3/langsmith-0.3.45-py3-none-any.whl", hash = "sha256:5b55f0518601fa65f3bb6b1a3100379a96aa7b3ed5e9380581615ba9c65ed8ed", size = 363002, upload-time = "2025-06-05T05:10:27.228Z" },
596
+ { url = "https://files.pythonhosted.org/packages/1d/33/a3337eb70d795495a299a1640d7a75f17fb917155a64309b96106e7b9452/langsmith-0.4.4-py3-none-any.whl", hash = "sha256:014c68329bd085bd6c770a6405c61bb6881f82eb554ce8c4d1984b0035fd1716", size = 367687, upload-time = "2025-06-27T19:20:33.839Z" },
601
597
  ]
602
598
 
603
599
  [[package]]
@@ -881,11 +877,11 @@ wheels = [
881
877
 
882
878
  [[package]]
883
879
  name = "pygments"
884
- version = "2.19.1"
880
+ version = "2.19.2"
885
881
  source = { registry = "https://pypi.org/simple" }
886
- sdist = { url = "https://files.pythonhosted.org/packages/7c/2d/c3338d48ea6cc0feb8446d8e6937e1408088a72a39937982cc6111d17f84/pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f", size = 4968581, upload-time = "2025-01-06T17:26:30.443Z" }
882
+ sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" }
887
883
  wheels = [
888
- { url = "https://files.pythonhosted.org/packages/8a/0b/9fcc47d19c48b59121088dd6da2488a49d5f72dacf8262e2790a1d2c7d15/pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c", size = 1225293, upload-time = "2025-01-06T17:26:25.553Z" },
884
+ { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" },
889
885
  ]
890
886
 
891
887
  [[package]]
@@ -1025,27 +1021,27 @@ wheels = [
1025
1021
 
1026
1022
  [[package]]
1027
1023
  name = "ruff"
1028
- version = "0.12.0"
1029
- source = { registry = "https://pypi.org/simple" }
1030
- sdist = { url = "https://files.pythonhosted.org/packages/24/90/5255432602c0b196a0da6720f6f76b93eb50baef46d3c9b0025e2f9acbf3/ruff-0.12.0.tar.gz", hash = "sha256:4d047db3662418d4a848a3fdbfaf17488b34b62f527ed6f10cb8afd78135bc5c", size = 4376101, upload-time = "2025-06-17T15:19:26.217Z" }
1031
- wheels = [
1032
- { url = "https://files.pythonhosted.org/packages/e6/fd/b46bb20e14b11ff49dbc74c61de352e0dc07fb650189513631f6fb5fc69f/ruff-0.12.0-py3-none-linux_armv6l.whl", hash = "sha256:5652a9ecdb308a1754d96a68827755f28d5dfb416b06f60fd9e13f26191a8848", size = 10311554, upload-time = "2025-06-17T15:18:45.792Z" },
1033
- { url = "https://files.pythonhosted.org/packages/e7/d3/021dde5a988fa3e25d2468d1dadeea0ae89dc4bc67d0140c6e68818a12a1/ruff-0.12.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:05ed0c914fabc602fc1f3b42c53aa219e5736cb030cdd85640c32dbc73da74a6", size = 11118435, upload-time = "2025-06-17T15:18:49.064Z" },
1034
- { url = "https://files.pythonhosted.org/packages/07/a2/01a5acf495265c667686ec418f19fd5c32bcc326d4c79ac28824aecd6a32/ruff-0.12.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:07a7aa9b69ac3fcfda3c507916d5d1bca10821fe3797d46bad10f2c6de1edda0", size = 10466010, upload-time = "2025-06-17T15:18:51.341Z" },
1035
- { url = "https://files.pythonhosted.org/packages/4c/57/7caf31dd947d72e7aa06c60ecb19c135cad871a0a8a251723088132ce801/ruff-0.12.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e7731c3eec50af71597243bace7ec6104616ca56dda2b99c89935fe926bdcd48", size = 10661366, upload-time = "2025-06-17T15:18:53.29Z" },
1036
- { url = "https://files.pythonhosted.org/packages/e9/ba/aa393b972a782b4bc9ea121e0e358a18981980856190d7d2b6187f63e03a/ruff-0.12.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:952d0630eae628250ab1c70a7fffb641b03e6b4a2d3f3ec6c1d19b4ab6c6c807", size = 10173492, upload-time = "2025-06-17T15:18:55.262Z" },
1037
- { url = "https://files.pythonhosted.org/packages/d7/50/9349ee777614bc3062fc6b038503a59b2034d09dd259daf8192f56c06720/ruff-0.12.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c021f04ea06966b02614d442e94071781c424ab8e02ec7af2f037b4c1e01cc82", size = 11761739, upload-time = "2025-06-17T15:18:58.906Z" },
1038
- { url = "https://files.pythonhosted.org/packages/04/8f/ad459de67c70ec112e2ba7206841c8f4eb340a03ee6a5cabc159fe558b8e/ruff-0.12.0-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:7d235618283718ee2fe14db07f954f9b2423700919dc688eacf3f8797a11315c", size = 12537098, upload-time = "2025-06-17T15:19:01.316Z" },
1039
- { url = "https://files.pythonhosted.org/packages/ed/50/15ad9c80ebd3c4819f5bd8883e57329f538704ed57bac680d95cb6627527/ruff-0.12.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0c0758038f81beec8cc52ca22de9685b8ae7f7cc18c013ec2050012862cc9165", size = 12154122, upload-time = "2025-06-17T15:19:03.727Z" },
1040
- { url = "https://files.pythonhosted.org/packages/76/e6/79b91e41bc8cc3e78ee95c87093c6cacfa275c786e53c9b11b9358026b3d/ruff-0.12.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:139b3d28027987b78fc8d6cfb61165447bdf3740e650b7c480744873688808c2", size = 11363374, upload-time = "2025-06-17T15:19:05.875Z" },
1041
- { url = "https://files.pythonhosted.org/packages/db/c3/82b292ff8a561850934549aa9dc39e2c4e783ab3c21debe55a495ddf7827/ruff-0.12.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:68853e8517b17bba004152aebd9dd77d5213e503a5f2789395b25f26acac0da4", size = 11587647, upload-time = "2025-06-17T15:19:08.246Z" },
1042
- { url = "https://files.pythonhosted.org/packages/2b/42/d5760d742669f285909de1bbf50289baccb647b53e99b8a3b4f7ce1b2001/ruff-0.12.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:3a9512af224b9ac4757f7010843771da6b2b0935a9e5e76bb407caa901a1a514", size = 10527284, upload-time = "2025-06-17T15:19:10.37Z" },
1043
- { url = "https://files.pythonhosted.org/packages/19/f6/fcee9935f25a8a8bba4adbae62495c39ef281256693962c2159e8b284c5f/ruff-0.12.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:b08df3d96db798e5beb488d4df03011874aff919a97dcc2dd8539bb2be5d6a88", size = 10158609, upload-time = "2025-06-17T15:19:12.286Z" },
1044
- { url = "https://files.pythonhosted.org/packages/37/fb/057febf0eea07b9384787bfe197e8b3384aa05faa0d6bd844b94ceb29945/ruff-0.12.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:6a315992297a7435a66259073681bb0d8647a826b7a6de45c6934b2ca3a9ed51", size = 11141462, upload-time = "2025-06-17T15:19:15.195Z" },
1045
- { url = "https://files.pythonhosted.org/packages/10/7c/1be8571011585914b9d23c95b15d07eec2d2303e94a03df58294bc9274d4/ruff-0.12.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:1e55e44e770e061f55a7dbc6e9aed47feea07731d809a3710feda2262d2d4d8a", size = 11641616, upload-time = "2025-06-17T15:19:17.6Z" },
1046
- { url = "https://files.pythonhosted.org/packages/6a/ef/b960ab4818f90ff59e571d03c3f992828d4683561095e80f9ef31f3d58b7/ruff-0.12.0-py3-none-win32.whl", hash = "sha256:7162a4c816f8d1555eb195c46ae0bd819834d2a3f18f98cc63819a7b46f474fb", size = 10525289, upload-time = "2025-06-17T15:19:19.688Z" },
1047
- { url = "https://files.pythonhosted.org/packages/34/93/8b16034d493ef958a500f17cda3496c63a537ce9d5a6479feec9558f1695/ruff-0.12.0-py3-none-win_amd64.whl", hash = "sha256:d00b7a157b8fb6d3827b49d3324da34a1e3f93492c1f97b08e222ad7e9b291e0", size = 11598311, upload-time = "2025-06-17T15:19:21.785Z" },
1048
- { url = "https://files.pythonhosted.org/packages/d0/33/4d3e79e4a84533d6cd526bfb42c020a23256ae5e4265d858bd1287831f7d/ruff-0.12.0-py3-none-win_arm64.whl", hash = "sha256:8cd24580405ad8c1cc64d61725bca091d6b6da7eb3d36f72cc605467069d7e8b", size = 10724946, upload-time = "2025-06-17T15:19:23.952Z" },
1024
+ version = "0.12.2"
1025
+ source = { registry = "https://pypi.org/simple" }
1026
+ sdist = { url = "https://files.pythonhosted.org/packages/6c/3d/d9a195676f25d00dbfcf3cf95fdd4c685c497fcfa7e862a44ac5e4e96480/ruff-0.12.2.tar.gz", hash = "sha256:d7b4f55cd6f325cb7621244f19c873c565a08aff5a4ba9c69aa7355f3f7afd3e", size = 4432239, upload-time = "2025-07-03T16:40:19.566Z" }
1027
+ wheels = [
1028
+ { url = "https://files.pythonhosted.org/packages/74/b6/2098d0126d2d3318fd5bec3ad40d06c25d377d95749f7a0c5af17129b3b1/ruff-0.12.2-py3-none-linux_armv6l.whl", hash = "sha256:093ea2b221df1d2b8e7ad92fc6ffdca40a2cb10d8564477a987b44fd4008a7be", size = 10369761, upload-time = "2025-07-03T16:39:38.847Z" },
1029
+ { url = "https://files.pythonhosted.org/packages/b1/4b/5da0142033dbe155dc598cfb99262d8ee2449d76920ea92c4eeb9547c208/ruff-0.12.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:09e4cf27cc10f96b1708100fa851e0daf21767e9709e1649175355280e0d950e", size = 11155659, upload-time = "2025-07-03T16:39:42.294Z" },
1030
+ { url = "https://files.pythonhosted.org/packages/3e/21/967b82550a503d7c5c5c127d11c935344b35e8c521f52915fc858fb3e473/ruff-0.12.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:8ae64755b22f4ff85e9c52d1f82644abd0b6b6b6deedceb74bd71f35c24044cc", size = 10537769, upload-time = "2025-07-03T16:39:44.75Z" },
1031
+ { url = "https://files.pythonhosted.org/packages/33/91/00cff7102e2ec71a4890fb7ba1803f2cdb122d82787c7d7cf8041fe8cbc1/ruff-0.12.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3eb3a6b2db4d6e2c77e682f0b988d4d61aff06860158fdb413118ca133d57922", size = 10717602, upload-time = "2025-07-03T16:39:47.652Z" },
1032
+ { url = "https://files.pythonhosted.org/packages/9b/eb/928814daec4e1ba9115858adcda44a637fb9010618721937491e4e2283b8/ruff-0.12.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:73448de992d05517170fc37169cbca857dfeaeaa8c2b9be494d7bcb0d36c8f4b", size = 10198772, upload-time = "2025-07-03T16:39:49.641Z" },
1033
+ { url = "https://files.pythonhosted.org/packages/50/fa/f15089bc20c40f4f72334f9145dde55ab2b680e51afb3b55422effbf2fb6/ruff-0.12.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3b8b94317cbc2ae4a2771af641739f933934b03555e51515e6e021c64441532d", size = 11845173, upload-time = "2025-07-03T16:39:52.069Z" },
1034
+ { url = "https://files.pythonhosted.org/packages/43/9f/1f6f98f39f2b9302acc161a4a2187b1e3a97634fe918a8e731e591841cf4/ruff-0.12.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:45fc42c3bf1d30d2008023a0a9a0cfb06bf9835b147f11fe0679f21ae86d34b1", size = 12553002, upload-time = "2025-07-03T16:39:54.551Z" },
1035
+ { url = "https://files.pythonhosted.org/packages/d8/70/08991ac46e38ddd231c8f4fd05ef189b1b94be8883e8c0c146a025c20a19/ruff-0.12.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce48f675c394c37e958bf229fb5c1e843e20945a6d962cf3ea20b7a107dcd9f4", size = 12171330, upload-time = "2025-07-03T16:39:57.55Z" },
1036
+ { url = "https://files.pythonhosted.org/packages/88/a9/5a55266fec474acfd0a1c73285f19dd22461d95a538f29bba02edd07a5d9/ruff-0.12.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:793d8859445ea47591272021a81391350205a4af65a9392401f418a95dfb75c9", size = 11774717, upload-time = "2025-07-03T16:39:59.78Z" },
1037
+ { url = "https://files.pythonhosted.org/packages/87/e5/0c270e458fc73c46c0d0f7cf970bb14786e5fdb88c87b5e423a4bd65232b/ruff-0.12.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6932323db80484dda89153da3d8e58164d01d6da86857c79f1961934354992da", size = 11646659, upload-time = "2025-07-03T16:40:01.934Z" },
1038
+ { url = "https://files.pythonhosted.org/packages/b7/b6/45ab96070c9752af37f0be364d849ed70e9ccede07675b0ec4e3ef76b63b/ruff-0.12.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:6aa7e623a3a11538108f61e859ebf016c4f14a7e6e4eba1980190cacb57714ce", size = 10604012, upload-time = "2025-07-03T16:40:04.363Z" },
1039
+ { url = "https://files.pythonhosted.org/packages/86/91/26a6e6a424eb147cc7627eebae095cfa0b4b337a7c1c413c447c9ebb72fd/ruff-0.12.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:2a4a20aeed74671b2def096bdf2eac610c7d8ffcbf4fb0e627c06947a1d7078d", size = 10176799, upload-time = "2025-07-03T16:40:06.514Z" },
1040
+ { url = "https://files.pythonhosted.org/packages/f5/0c/9f344583465a61c8918a7cda604226e77b2c548daf8ef7c2bfccf2b37200/ruff-0.12.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:71a4c550195612f486c9d1f2b045a600aeba851b298c667807ae933478fcef04", size = 11241507, upload-time = "2025-07-03T16:40:08.708Z" },
1041
+ { url = "https://files.pythonhosted.org/packages/1c/b7/99c34ded8fb5f86c0280278fa89a0066c3760edc326e935ce0b1550d315d/ruff-0.12.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:4987b8f4ceadf597c927beee65a5eaf994c6e2b631df963f86d8ad1bdea99342", size = 11717609, upload-time = "2025-07-03T16:40:10.836Z" },
1042
+ { url = "https://files.pythonhosted.org/packages/51/de/8589fa724590faa057e5a6d171e7f2f6cffe3287406ef40e49c682c07d89/ruff-0.12.2-py3-none-win32.whl", hash = "sha256:369ffb69b70cd55b6c3fc453b9492d98aed98062db9fec828cdfd069555f5f1a", size = 10523823, upload-time = "2025-07-03T16:40:13.203Z" },
1043
+ { url = "https://files.pythonhosted.org/packages/94/47/8abf129102ae4c90cba0c2199a1a9b0fa896f6f806238d6f8c14448cc748/ruff-0.12.2-py3-none-win_amd64.whl", hash = "sha256:dca8a3b6d6dc9810ed8f328d406516bf4d660c00caeaef36eb831cf4871b0639", size = 11629831, upload-time = "2025-07-03T16:40:15.478Z" },
1044
+ { url = "https://files.pythonhosted.org/packages/e2/1f/72d2946e3cc7456bb837e88000eb3437e55f80db339c840c04015a11115d/ruff-0.12.2-py3-none-win_arm64.whl", hash = "sha256:48d6c6bfb4761df68bc05ae630e24f506755e702d4fb08f08460be778c7ccb12", size = 10735334, upload-time = "2025-07-03T16:40:17.677Z" },
1049
1045
  ]
1050
1046
 
1051
1047
  [[package]]
@@ -1124,11 +1120,11 @@ wheels = [
1124
1120
 
1125
1121
  [[package]]
1126
1122
  name = "typing-extensions"
1127
- version = "4.14.0"
1123
+ version = "4.14.1"
1128
1124
  source = { registry = "https://pypi.org/simple" }
1129
- sdist = { url = "https://files.pythonhosted.org/packages/d1/bc/51647cd02527e87d05cb083ccc402f93e441606ff1f01739a62c8ad09ba5/typing_extensions-4.14.0.tar.gz", hash = "sha256:8676b788e32f02ab42d9e7c61324048ae4c6d844a399eebace3d4979d75ceef4", size = 107423, upload-time = "2025-06-02T14:52:11.399Z" }
1125
+ sdist = { url = "https://files.pythonhosted.org/packages/98/5a/da40306b885cc8c09109dc2e1abd358d5684b1425678151cdaed4731c822/typing_extensions-4.14.1.tar.gz", hash = "sha256:38b39f4aeeab64884ce9f74c94263ef78f3c22467c8724005483154c26648d36", size = 107673, upload-time = "2025-07-04T13:28:34.16Z" }
1130
1126
  wheels = [
1131
- { url = "https://files.pythonhosted.org/packages/69/e0/552843e0d356fbb5256d21449fa957fa4eff3bbc135a74a691ee70c7c5da/typing_extensions-4.14.0-py3-none-any.whl", hash = "sha256:a1514509136dd0b477638fc68d6a91497af5076466ad0fa6c338e44e359944af", size = 43839, upload-time = "2025-06-02T14:52:10.026Z" },
1127
+ { url = "https://files.pythonhosted.org/packages/b5/00/d631e67a838026495268c2f6884f3711a15a9a2a96cd244fdaea53b823fb/typing_extensions-4.14.1-py3-none-any.whl", hash = "sha256:d1e1e3b58374dc93031d6eda2420a48ea44a36c2b4766a4fdeb3710755731d76", size = 43906, upload-time = "2025-07-04T13:28:32.743Z" },
1132
1128
  ]
1133
1129
 
1134
1130
  [[package]]
@@ -1154,15 +1150,15 @@ wheels = [
1154
1150
 
1155
1151
  [[package]]
1156
1152
  name = "uvicorn"
1157
- version = "0.34.3"
1153
+ version = "0.35.0"
1158
1154
  source = { registry = "https://pypi.org/simple" }
1159
1155
  dependencies = [
1160
1156
  { name = "click" },
1161
1157
  { name = "h11" },
1162
1158
  ]
1163
- sdist = { url = "https://files.pythonhosted.org/packages/de/ad/713be230bcda622eaa35c28f0d328c3675c371238470abdea52417f17a8e/uvicorn-0.34.3.tar.gz", hash = "sha256:35919a9a979d7a59334b6b10e05d77c1d0d574c50e0fc98b8b1a0f165708b55a", size = 76631, upload-time = "2025-06-01T07:48:17.531Z" }
1159
+ sdist = { url = "https://files.pythonhosted.org/packages/5e/42/e0e305207bb88c6b8d3061399c6a961ffe5fbb7e2aa63c9234df7259e9cd/uvicorn-0.35.0.tar.gz", hash = "sha256:bc662f087f7cf2ce11a1d7fd70b90c9f98ef2e2831556dd078d131b96cc94a01", size = 78473, upload-time = "2025-06-28T16:15:46.058Z" }
1164
1160
  wheels = [
1165
- { url = "https://files.pythonhosted.org/packages/6d/0d/8adfeaa62945f90d19ddc461c55f4a50c258af7662d34b6a3d5d1f8646f6/uvicorn-0.34.3-py3-none-any.whl", hash = "sha256:16246631db62bdfbf069b0645177d6e8a77ba950cfedbfd093acef9444e4d885", size = 62431, upload-time = "2025-06-01T07:48:15.664Z" },
1161
+ { url = "https://files.pythonhosted.org/packages/d2/e2/dc81b1bd1dcfe91735810265e9d26bc8ec5da45b4c0f6237e286819194c3/uvicorn-0.35.0-py3-none-any.whl", hash = "sha256:197535216b25ff9b785e29a0b79199f55222193d47f820816e7da751e9bc8d4a", size = 66406, upload-time = "2025-06-28T16:15:44.816Z" },
1166
1162
  ]
1167
1163
 
1168
1164
  [[package]]
@@ -1 +0,0 @@
1
- __version__ = "0.2.78"
File without changes
File without changes
File without changes