langgraph-api 0.1.2__py3-none-any.whl → 0.1.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of langgraph-api might be problematic. Click here for more details.

langgraph_api/__init__.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.1.1"
1
+ __version__ = "0.1.3"
@@ -36,7 +36,7 @@ def on_error(conn: HTTPConnection, exc: AuthenticationError):
36
36
 
37
37
  class ConditionalAuthenticationMiddleware(AuthenticationMiddleware):
38
38
  async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
39
- if scope["root_path"] == "/noauth":
39
+ if (root_path := scope.get("root_path")) and root_path.startswith("/noauth"):
40
40
  # disable auth for requests originating from SDK ASGI transport
41
41
  # root_path cannot be set from a request, so safe to use as auth bypass
42
42
  await self.app(scope, receive, send)
@@ -46,7 +46,6 @@ class ConditionalAuthenticationMiddleware(AuthenticationMiddleware):
46
46
  # disable auth for UI asset requests
47
47
  await self.app(scope, receive, send)
48
48
  return
49
-
50
49
  return await super().__call__(scope, receive, send)
51
50
 
52
51
 
langgraph_api/config.py CHANGED
@@ -37,6 +37,8 @@ class HttpConfig(TypedDict, total=False):
37
37
  """Disable /ui routes"""
38
38
  disable_mcp: bool
39
39
  """Disable /mcp routes"""
40
+ mount_prefix: str
41
+ """Prefix for mounted routes. E.g., "/my-deployment/api"."""
40
42
 
41
43
 
42
44
  class ThreadTTLConfig(TypedDict, total=False):
@@ -169,11 +171,15 @@ HTTP_CONFIG: HttpConfig | None = env("LANGGRAPH_HTTP", cast=_parse_json, default
169
171
  STORE_CONFIG: StoreConfig | None = env(
170
172
  "LANGGRAPH_STORE", cast=_parse_json, default=None
171
173
  )
174
+
175
+ MOUNT_PREFIX: str | None = env("MOUNT_PREFIX", cast=str, default=None) or (
176
+ HTTP_CONFIG.get("mount_prefix") if HTTP_CONFIG else None
177
+ )
178
+
172
179
  CORS_ALLOW_ORIGINS = env("CORS_ALLOW_ORIGINS", cast=CommaSeparatedStrings, default="*")
173
- if HTTP_CONFIG and HTTP_CONFIG.get("cors"):
174
- CORS_CONFIG = HTTP_CONFIG["cors"]
175
- else:
176
- CORS_CONFIG: CorsConfig | None = env("CORS_CONFIG", cast=_parse_json, default=None)
180
+ CORS_CONFIG: CorsConfig | None = env("CORS_CONFIG", cast=_parse_json, default=None) or (
181
+ HTTP_CONFIG.get("cors") if HTTP_CONFIG else None
182
+ )
177
183
  """
178
184
  {
179
185
  "type": "object",
@@ -821,7 +821,7 @@ describe("runs", () => {
821
821
  },
822
822
  );
823
823
 
824
- it.concurrent("human in the loop - modification", async () => {
824
+ it.concurrent("human in the loop - modification", {retry: 3}, async () => {
825
825
  // (2) interrupt, modify the message and then continue running
826
826
  const assistant = await client.assistants.create({ graphId: "agent" });
827
827
  const thread = await client.threads.create();
@@ -1204,7 +1204,7 @@ describe("subgraphs", () => {
1204
1204
  });
1205
1205
 
1206
1206
  // (1) interrupt and then continue running, no modification
1207
- it.concurrent("human in the loop - no modification", async () => {
1207
+ it.concurrent("human in the loop - no modification", {retry: 3}, async () => {
1208
1208
  const assistant = await client.assistants.create({ graphId: "weather" });
1209
1209
  const thread = await client.threads.create();
1210
1210
 
@@ -1491,7 +1491,7 @@ describe("subgraphs", () => {
1491
1491
  });
1492
1492
 
1493
1493
  // (2) interrupt, modify the message and then continue running
1494
- it.concurrent("human in the loop - modification", async () => {
1494
+ it.concurrent("human in the loop - modification", {retry: 3}, async () => {
1495
1495
  const assistant = await client.assistants.create({ graphId: "weather" });
1496
1496
  const thread = await client.threads.create();
1497
1497
  const input = {
langgraph_api/metadata.py CHANGED
@@ -70,6 +70,12 @@ async def metadata_loop() -> None:
70
70
  if not LANGGRAPH_CLOUD_LICENSE_KEY and not LANGSMITH_API_KEY:
71
71
  return
72
72
 
73
+ if LANGGRAPH_CLOUD_LICENSE_KEY and not LANGGRAPH_CLOUD_LICENSE_KEY.startswith(
74
+ "lcl_"
75
+ ):
76
+ logger.info("Running in air-gapped mode, skipping metadata loop")
77
+ return
78
+
73
79
  logger.info("Starting metadata loop")
74
80
 
75
81
  global RUN_COUNTER, NODE_COUNTER, FROM_TIMESTAMP
langgraph_api/serde.py CHANGED
@@ -104,7 +104,7 @@ _option = orjson.OPT_SERIALIZE_NUMPY | orjson.OPT_NON_STR_KEYS
104
104
 
105
105
  def json_dumpb(obj) -> bytes:
106
106
  return orjson.dumps(obj, default=default, option=_option).replace(
107
- b"\u0000", b""
107
+ rb"\u0000", b""
108
108
  ) # null unicode char not allowed in json
109
109
 
110
110
 
langgraph_api/server.py CHANGED
@@ -5,6 +5,7 @@ import sys
5
5
  # WARNING: Keep the import above before other code runs as it
6
6
  # patches an error in the Starlette library.
7
7
  import logging
8
+ import typing
8
9
 
9
10
  import jsonschema_rs
10
11
  import structlog
@@ -13,10 +14,13 @@ from langgraph.errors import EmptyInputError, InvalidUpdateError
13
14
  from starlette.applications import Starlette
14
15
  from starlette.middleware import Middleware
15
16
  from starlette.middleware.cors import CORSMiddleware
17
+
16
18
  from langgraph_api.api.openapi import set_custom_spec
19
+ from starlette.types import Receive, Scope, Send
17
20
 
18
21
  import langgraph_api.config as config
19
22
  from langgraph_api.api import routes, meta_routes, user_router
23
+ from starlette.routing import Mount
20
24
  from langgraph_api.errors import (
21
25
  overloaded_error_handler,
22
26
  validation_error_handler,
@@ -149,3 +153,37 @@ else:
149
153
  middleware=middleware,
150
154
  exception_handlers=exception_handlers,
151
155
  )
156
+
157
+ if config.MOUNT_PREFIX:
158
+ prefix = config.MOUNT_PREFIX
159
+ if not prefix.startswith("/") or prefix.endswith("/"):
160
+ raise ValueError(
161
+ f"Invalid mount_prefix '{prefix}': Must start with '/' and must not end with '/'. "
162
+ f"Valid examples: '/my-api', '/v1', '/api/v1'.\nInvalid examples: 'api/', '/api/'"
163
+ )
164
+ logger.info(f"Mounting routes at prefix: {prefix}")
165
+ plen = len(prefix)
166
+ rplen = len(prefix.encode("utf-8"))
167
+
168
+ class ASGIBypassMiddleware:
169
+ def __init__(self, app: typing.Any, **kwargs):
170
+ self.app = app
171
+
172
+ async def __call__(
173
+ self, scope: Scope, receive: Receive, send: Send
174
+ ) -> typing.Any:
175
+ if (root_path := scope.get("root_path")) and root_path == "/noauth":
176
+ # The SDK initialized with None is trying to connect via
177
+ # ASGITransport. Ensure that it has the correct subpath prefixes
178
+ # so the regular router can handle it.
179
+ scope["path"] = f'/noauth{prefix}{scope["path"]}'
180
+ scope["raw_path"] = scope["path"].encode("utf-8")
181
+
182
+ return await self.app(scope, receive, send)
183
+
184
+ app = Starlette(
185
+ routes=[Mount(prefix, app=app)],
186
+ lifespan=app.router.lifespan_context,
187
+ middleware=[Middleware(ASGIBypassMiddleware)] + app.user_middleware,
188
+ exception_handlers=app.exception_handlers,
189
+ )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: langgraph-api
3
- Version: 0.1.2
3
+ Version: 0.1.3
4
4
  Summary:
5
5
  License: Elastic-2.0
6
6
  Author: Nuno Campos
@@ -1,5 +1,5 @@
1
1
  LICENSE,sha256=ZPwVR73Biwm3sK6vR54djCrhaRiM4cAD2zvOQZV8Xis,3859
2
- langgraph_api/__init__.py,sha256=rnObPjuBcEStqSO0S6gsdS_ot8ITOQjVj_-P1LUUYpg,22
2
+ langgraph_api/__init__.py,sha256=XEqb2aiIn8fzGE68Mph4ck1FtQqsR_am0wRWvrYPffQ,22
3
3
  langgraph_api/api/__init__.py,sha256=ohkuKTIYaWMAnr2pck2XAMrg4srA418VM76GQWRf5tU,5493
4
4
  langgraph_api/api/assistants.py,sha256=i-nxkScUB2g8bTVGtQIp1psABXlaY1aVx9pkB_UiRH8,14353
5
5
  langgraph_api/api/mcp.py,sha256=KbR19dtFCpJEiKYj3IfepAuJij8YZVELuVp7JY_yu_o,13754
@@ -15,12 +15,12 @@ langgraph_api/auth/custom.py,sha256=QR49PnIfPPQZ6ygWmnMkSuAzgOpBBUbbz5kJ2yVwGmQ,
15
15
  langgraph_api/auth/langsmith/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
16
  langgraph_api/auth/langsmith/backend.py,sha256=InScaL-HYCnxYEauhxU198gRZV9pJn9SzzBoR9Edn7g,2654
17
17
  langgraph_api/auth/langsmith/client.py,sha256=eKchvAom7hdkUXauD8vHNceBDDUijrFgdTV8bKd7x4Q,3998
18
- langgraph_api/auth/middleware.py,sha256=jU8aDSIZHdzCGdifejRF7ndHkSjBtqIHcBwFIuUdHEA,1875
18
+ langgraph_api/auth/middleware.py,sha256=jDA4t41DUoAArEY_PNoXesIUBJ0nGhh85QzRdn5EPD0,1916
19
19
  langgraph_api/auth/noop.py,sha256=Bk6Nf3p8D_iMVy_OyfPlyiJp_aEwzL-sHrbxoXpCbac,586
20
20
  langgraph_api/auth/studio_user.py,sha256=FzFQRROKDlA9JjtBuwyZvk6Mbwno5M9RVYjDO6FU3F8,186
21
21
  langgraph_api/cli.py,sha256=eVX8zGeQAoVMotib2s-QhIQWQX1JInpVMItkTTgG_dU,12833
22
22
  langgraph_api/command.py,sha256=3O9v3i0OPa96ARyJ_oJbLXkfO8rPgDhLCswgO9koTFA,768
23
- langgraph_api/config.py,sha256=ZxVQitVzc9Rp9_j3myjW_NcSJ463kUAMmbxmmOS-tXY,10357
23
+ langgraph_api/config.py,sha256=uvw51OP8VApy54DbqZIShNoaCJeM7M83mR-8hDgMLY8,10553
24
24
  langgraph_api/cron_scheduler.py,sha256=i87j4pJrcsmsqMKeKUs69gaAjrGaSM3pM3jnXdN5JDQ,2630
25
25
  langgraph_api/errors.py,sha256=Bu_i5drgNTyJcLiyrwVE_6-XrSU50BHf9TDpttki9wQ,1690
26
26
  langgraph_api/graph.py,sha256=GZtVXyP1l-nKlcRmB9s_m0U9UnifPUzIBBjDXmT11oo,20959
@@ -46,7 +46,7 @@ langgraph_api/js/src/utils/importMap.mts,sha256=pX4TGOyUpuuWF82kXcxcv3-8mgusRezO
46
46
  langgraph_api/js/src/utils/pythonSchemas.mts,sha256=98IW7Z_VP7L_CHNRMb3_MsiV3BgLE2JsWQY_PQcRR3o,685
47
47
  langgraph_api/js/src/utils/serde.mts,sha256=OuyyO9btvwWd55rU_H4x91dFEJiaPxL-lL9O6Zgo908,742
48
48
  langgraph_api/js/sse.py,sha256=lsfp4nyJyA1COmlKG9e2gJnTttf_HGCB5wyH8OZBER8,4105
49
- langgraph_api/js/tests/api.test.mts,sha256=DLuwYcLp8EwSEi27WP-Uc92og23OZldJxG0Wc5rXsNY,62951
49
+ langgraph_api/js/tests/api.test.mts,sha256=CJ4q0HmutNRLMFhEkOZ4w3UJxgEG9FYLDVFCB8WirGQ,62987
50
50
  langgraph_api/js/tests/auth.test.mts,sha256=A8JXfEep6S4jzduoSZeRQkqq9WsFbVE8Bvi3Hj_zx2w,21600
51
51
  langgraph_api/js/tests/compose-postgres.auth.yml,sha256=iPfJbCeYZdV6GiRLiDn_f7qgpG4TyyGaQ4lV-ZXr6Qk,1768
52
52
  langgraph_api/js/tests/compose-postgres.yml,sha256=yz99nUy4Gm8vXuE5cnp2ekPzv7x5XWdwFPB9muAqeGE,1861
@@ -70,7 +70,7 @@ langgraph_api/js/tests/utils.mts,sha256=q1V9gvT63v95onlfK9W4iv3n9ZJO3h-0RD9TdDYu
70
70
  langgraph_api/js/ui.py,sha256=XNT8iBcyT8XmbIqSQUWd-j_00HsaWB2vRTVabwFBkik,2439
71
71
  langgraph_api/js/yarn.lock,sha256=A4b9TS9cljYvyc-7RF9QY0zcP_NFJMe2-RVeTSlX2DY,83691
72
72
  langgraph_api/logging.py,sha256=JJIzbNIgLCN6ClQ3tA-Mm5ffuBGvpRDSZsEvnIlsuu4,3693
73
- langgraph_api/metadata.py,sha256=iM_3qaxFk1ItB4E5VaVb4n_mbqXzqZFMknVcHXnV0mI,3937
73
+ langgraph_api/metadata.py,sha256=RUDDgxlrmf7zDFT_kI587JZboD0IdmlBwwkLA0Fxm00,4132
74
74
  langgraph_api/middleware/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
75
75
  langgraph_api/middleware/http_logger.py,sha256=aj4mdisRobFePkD3Iy6-w_Mujwx4TQRaEhPvSd6HgLk,3284
76
76
  langgraph_api/middleware/private_network.py,sha256=eYgdyU8AzU2XJu362i1L8aSFoQRiV7_aLBPw7_EgeqI,2111
@@ -80,8 +80,8 @@ langgraph_api/patch.py,sha256=Dgs0PXHytekX4SUL6KsjjN0hHcOtGLvv1GRGbh6PswU,1408
80
80
  langgraph_api/queue_entrypoint.py,sha256=gjtajZfnDXhsi7JElfmkY-p0ENBiKBDJ4Ugiw-exapw,1839
81
81
  langgraph_api/route.py,sha256=fM4qYCGbmH0a3_cV8uKocb1sLklehxO6HhdRXqLK6OM,4421
82
82
  langgraph_api/schema.py,sha256=Frh_YOC3S1cDAMPUVanNi78ooSXK2WFpu9YkIVz5h14,5433
83
- langgraph_api/serde.py,sha256=D5t_HeABMYKRAsoXWaWG0IsdaYM8yOXIez2wJUTIgT8,3963
84
- langgraph_api/server.py,sha256=Chjk-TddIVKqYqulRJb7AGYW6GZS3aU3T8ApNS7azGA,4853
83
+ langgraph_api/serde.py,sha256=TVsx2QQtepf8Wsgsabcku1NV4Vbugu4Oujmdnq4qMS0,3964
84
+ langgraph_api/server.py,sha256=d1OeDY7zvw16mX_NWAi9PbFbpFygFlM0clJXRoooPig,6368
85
85
  langgraph_api/sse.py,sha256=2wNodCOP2eg7a9mpSu0S3FQ0CHk2BBV_vv0UtIgJIcc,4034
86
86
  langgraph_api/state.py,sha256=8jx4IoTCOjTJuwzuXJKKFwo1VseHjNnw_CCq4x1SW14,2284
87
87
  langgraph_api/stream.py,sha256=SN96CLJ5QnSQ2YWRrkQb4K8XkdoSbloyFRj8N1vKcs8,11369
@@ -96,8 +96,8 @@ langgraph_license/validation.py,sha256=Uu_G8UGO_WTlLsBEY0gTVWjRR4czYGfw5YAD3HLZo
96
96
  langgraph_runtime/__init__.py,sha256=3stEi5ECApK7l0Y7q4vN-fQQz8cgJ9lyqAyhqDBqags,1075
97
97
  logging.json,sha256=3RNjSADZmDq38eHePMm1CbP6qZ71AmpBtLwCmKU9Zgo,379
98
98
  openapi.json,sha256=YW4ND-N3adriEoNwxw7UD9endO2xUZoodCtwVIfa2dU,132261
99
- langgraph_api-0.1.2.dist-info/LICENSE,sha256=ZPwVR73Biwm3sK6vR54djCrhaRiM4cAD2zvOQZV8Xis,3859
100
- langgraph_api-0.1.2.dist-info/METADATA,sha256=AXwaqScw3Ar7ugD1VPcHsz1QZ-Aa7HuCGY2fsWe5Aho,4119
101
- langgraph_api-0.1.2.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
102
- langgraph_api-0.1.2.dist-info/entry_points.txt,sha256=3EYLgj89DfzqJHHYGxPH4A_fEtClvlRbWRUHaXO7hj4,77
103
- langgraph_api-0.1.2.dist-info/RECORD,,
99
+ langgraph_api-0.1.3.dist-info/LICENSE,sha256=ZPwVR73Biwm3sK6vR54djCrhaRiM4cAD2zvOQZV8Xis,3859
100
+ langgraph_api-0.1.3.dist-info/METADATA,sha256=AM0f6xyGMvefzzmkHsD37dNq8Qyq4BAb6wBwswBjLMg,4119
101
+ langgraph_api-0.1.3.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
102
+ langgraph_api-0.1.3.dist-info/entry_points.txt,sha256=3EYLgj89DfzqJHHYGxPH4A_fEtClvlRbWRUHaXO7hj4,77
103
+ langgraph_api-0.1.3.dist-info/RECORD,,