langgraph-api 0.0.36__py3-none-any.whl → 0.0.38__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/api/mcp.py CHANGED
@@ -351,7 +351,9 @@ async def handle_tools_list(
351
351
 
352
352
  # Get assistants from the API
353
353
  # For now set a large limit to get all assistants
354
- assistants = await client.assistants.search(offset=cursor, limit=DEFAULT_PAGE_SIZE)
354
+ assistants = await client.assistants.search(
355
+ offset=cursor, limit=DEFAULT_PAGE_SIZE, headers=request.headers
356
+ )
355
357
 
356
358
  if len(assistants) == DEFAULT_PAGE_SIZE:
357
359
  next_cursor = cursor + DEFAULT_PAGE_SIZE
@@ -370,7 +372,7 @@ async def handle_tools_list(
370
372
  else:
371
373
  seen_names.add(name)
372
374
 
373
- schemas = await client.assistants.get_schemas(id_)
375
+ schemas = await client.assistants.get_schemas(id_, headers=request.headers)
374
376
  tools.append(
375
377
  {
376
378
  "name": name,
@@ -408,7 +410,9 @@ async def handle_tools_call(
408
410
  }
409
411
 
410
412
  arguments = params.get("arguments", {})
411
- assistants = await client.assistants.search(limit=MAX_ASSISTANTS)
413
+ assistants = await client.assistants.search(
414
+ limit=MAX_ASSISTANTS, headers=request.headers
415
+ )
412
416
  matching_assistant = [
413
417
  assistant for assistant in assistants if assistant["name"] == tool_name
414
418
  ]
@@ -437,7 +441,11 @@ async def handle_tools_call(
437
441
  tool_name = matching_assistant[0]["assistant_id"]
438
442
 
439
443
  value = await client.runs.wait(
440
- thread_id=None, assistant_id=tool_name, input=arguments, raise_error=False
444
+ thread_id=None,
445
+ assistant_id=tool_name,
446
+ input=arguments,
447
+ raise_error=False,
448
+ headers=request.headers,
441
449
  )
442
450
 
443
451
  if "__error__" in value:
@@ -379,13 +379,16 @@ def _depends() -> Any:
379
379
  return None
380
380
 
381
381
 
382
+ _EXCLUDED = ("values", "keys", "items", "dict")
383
+
384
+
382
385
  class DotDict:
383
386
  def __init__(self, dictionary: dict[str, Any]):
384
387
  self._dict = dictionary
385
388
  for key, value in dictionary.items():
386
389
  if isinstance(value, dict):
387
390
  setattr(self, key, DotDict(value))
388
- else:
391
+ elif key not in _EXCLUDED:
389
392
  setattr(self, key, value)
390
393
 
391
394
  def __getattr__(self, name):
@@ -393,6 +396,9 @@ class DotDict:
393
396
  raise AttributeError(f"'DotDict' object has no attribute '{name}'")
394
397
  return self._dict[name]
395
398
 
399
+ def __contains__(self, key: str) -> bool:
400
+ return key in self._dict
401
+
396
402
  def __getitem__(self, key):
397
403
  return self._dict[key]
398
404
 
@@ -409,6 +415,21 @@ class DotDict:
409
415
  def dict(self):
410
416
  return self._dict
411
417
 
418
+ def items(self):
419
+ return self._dict.items()
420
+
421
+ def values(self):
422
+ return self._dict.values()
423
+
424
+ def keys(self):
425
+ return self._dict.keys()
426
+
427
+ def __iter__(self):
428
+ return iter(self._dict)
429
+
430
+ def __len__(self):
431
+ return len(self._dict)
432
+
412
433
 
413
434
  class ProxyUser(BaseUser):
414
435
  """A proxy that wraps a user object to ensure it has all BaseUser properties.
@@ -462,6 +483,9 @@ class ProxyUser(BaseUser):
462
483
  **d,
463
484
  }
464
485
 
486
+ def __contains__(self, key: str) -> bool:
487
+ return key in self._user
488
+
465
489
  def __getitem__(self, key):
466
490
  return self._user[key]
467
491
 
langgraph_api/cli.py CHANGED
@@ -130,6 +130,7 @@ def run_server(
130
130
  auth: AuthConfig | None = None,
131
131
  http: typing.Optional["HttpConfig"] = None,
132
132
  studio_url: str | None = None,
133
+ disable_persistence: bool = False,
133
134
  **kwargs: typing.Any,
134
135
  ):
135
136
  """Run the LangGraph API server."""
@@ -189,6 +190,7 @@ def run_server(
189
190
  LANGGRAPH_AUTH=json.dumps(auth) if auth else None,
190
191
  LANGGRAPH_HTTP=json.dumps(http) if http else None,
191
192
  LANGGRAPH_API_URL=local_url,
193
+ LANGGRAPH_DISABLE_FILE_PERSISTENCE=str(disable_persistence).lower(),
192
194
  # See https://developer.chrome.com/blog/private-network-access-update-2024-03
193
195
  ALLOW_PRIVATE_NETWORK="true",
194
196
  )
@@ -1,9 +1,11 @@
1
1
  import asyncio
2
+ import urllib.parse
2
3
  import uuid
3
4
  from collections.abc import Mapping, Sequence
4
5
  from typing import Any, NamedTuple, TypedDict
5
6
  from uuid import UUID
6
7
 
8
+ import orjson
7
9
  from langgraph.checkpoint.base.id import uuid6
8
10
  from starlette.authentication import BaseUser
9
11
  from starlette.exceptions import HTTPException
@@ -152,6 +154,11 @@ def get_user_id(user: BaseUser | None) -> str | None:
152
154
  pass
153
155
 
154
156
 
157
+ LANGSMITH_METADATA = "langsmith-metadata"
158
+ LANGSMITH_TAGS = "langsmith-tags"
159
+ LANGSMITH_PROJECT = "langsmith-project"
160
+
161
+
155
162
  async def create_valid_run(
156
163
  conn: AsyncConnectionProto,
157
164
  thread_id: str | None,
@@ -174,14 +181,13 @@ async def create_valid_run(
174
181
  stream_mode, multitask_strategy, prevent_insert_if_inflight = assign_defaults(
175
182
  payload
176
183
  )
177
-
178
184
  # assign custom headers and checkpoint to config
179
185
  config = payload.get("config") or {}
180
- config.setdefault("configurable", {})
186
+ configurable = config.setdefault("configurable", {})
181
187
  if checkpoint_id:
182
- config["configurable"]["checkpoint_id"] = str(checkpoint_id)
188
+ configurable["checkpoint_id"] = str(checkpoint_id)
183
189
  if checkpoint := payload.get("checkpoint"):
184
- config["configurable"].update(checkpoint)
190
+ configurable.update(checkpoint)
185
191
  for key, value in headers.items():
186
192
  if key.startswith("x-"):
187
193
  if key in (
@@ -190,14 +196,27 @@ async def create_valid_run(
190
196
  "x-service-key",
191
197
  ):
192
198
  continue
193
- config["configurable"][key] = value
199
+ configurable[key] = value
200
+ elif key == "langsmith-trace":
201
+ configurable[key] = value
202
+ if baggage := headers.get("baggage"):
203
+ for item in baggage.split(","):
204
+ key, value = item.split("=")
205
+ if key == LANGSMITH_METADATA and key not in configurable:
206
+ configurable[key] = orjson.loads(urllib.parse.unquote(value))
207
+ elif key == LANGSMITH_TAGS:
208
+ configurable[key] = urllib.parse.unquote(value).split(",")
209
+ elif key == LANGSMITH_PROJECT:
210
+ configurable[key] = urllib.parse.unquote(value)
211
+ elif key == "user-agent":
212
+ configurable[key] = value
194
213
  ctx = get_auth_ctx()
195
214
  if ctx:
196
215
  user = ctx.user
197
216
  user_id = get_user_id(user)
198
- config["configurable"]["langgraph_auth_user"] = user
199
- config["configurable"]["langgraph_auth_user_id"] = user_id
200
- config["configurable"]["langgraph_auth_permissions"] = ctx.permissions
217
+ configurable["langgraph_auth_user"] = user
218
+ configurable["langgraph_auth_user_id"] = user_id
219
+ configurable["langgraph_auth_permissions"] = ctx.permissions
201
220
  else:
202
221
  user_id = None
203
222
  run_coro = Runs.put(
langgraph_api/serde.py CHANGED
@@ -1,5 +1,4 @@
1
1
  import asyncio
2
- import pickle
3
2
  import uuid
4
3
  from base64 import b64encode
5
4
  from collections import deque
@@ -18,9 +17,13 @@ from re import Pattern
18
17
  from typing import Any, NamedTuple
19
18
  from zoneinfo import ZoneInfo
20
19
 
20
+ import cloudpickle
21
21
  import orjson
22
+ import structlog
22
23
  from langgraph.checkpoint.serde.jsonplus import JsonPlusSerializer
23
24
 
25
+ logger = structlog.stdlib.get_logger(__name__)
26
+
24
27
 
25
28
  class Fragment(NamedTuple):
26
29
  buf: bytes
@@ -122,9 +125,15 @@ class Serializer(JsonPlusSerializer):
122
125
  try:
123
126
  return super().dumps_typed(obj)
124
127
  except TypeError:
125
- return "pickle", pickle.dumps(obj)
128
+ return "pickle", cloudpickle.dumps(obj)
126
129
 
127
130
  def loads_typed(self, data: tuple[str, bytes]) -> Any:
128
131
  if data[0] == "pickle":
129
- return pickle.loads(data[1])
132
+ try:
133
+ return cloudpickle.loads(data[1])
134
+ except Exception as e:
135
+ logger.warning(
136
+ "Failed to unpickle object, replacing w None", exc_info=e
137
+ )
138
+ return None
130
139
  return super().loads_typed(data)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: langgraph-api
3
- Version: 0.0.36
3
+ Version: 0.0.38
4
4
  Summary:
5
5
  License: Elastic-2.0
6
6
  Author: Nuno Campos
@@ -10,13 +10,14 @@ Classifier: License :: Other/Proprietary License
10
10
  Classifier: Programming Language :: Python :: 3
11
11
  Classifier: Programming Language :: Python :: 3.11
12
12
  Classifier: Programming Language :: Python :: 3.12
13
+ Requires-Dist: cloudpickle (>=3.0.0,<4.0.0)
13
14
  Requires-Dist: cryptography (>=43.0.3,<44.0.0)
14
15
  Requires-Dist: httpx (>=0.25.0)
15
16
  Requires-Dist: jsonschema-rs (>=0.20.0,<0.30)
16
17
  Requires-Dist: langchain-core (>=0.2.38,<0.4.0)
17
18
  Requires-Dist: langgraph (>=0.2.56,<0.4.0)
18
19
  Requires-Dist: langgraph-checkpoint (>=2.0.23,<3.0)
19
- Requires-Dist: langgraph-sdk (>=0.1.58,<0.2.0)
20
+ Requires-Dist: langgraph-sdk (>=0.1.59,<0.2.0)
20
21
  Requires-Dist: langsmith (>=0.1.63,<0.4.0)
21
22
  Requires-Dist: orjson (>=3.9.7)
22
23
  Requires-Dist: pyjwt (>=2.9.0,<3.0.0)
@@ -2,7 +2,7 @@ LICENSE,sha256=ZPwVR73Biwm3sK6vR54djCrhaRiM4cAD2zvOQZV8Xis,3859
2
2
  langgraph_api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
3
  langgraph_api/api/__init__.py,sha256=iDxgg0arqzZ0-roCSuWo3f8zXmKcl4fuU3Jw0-lqtZs,5447
4
4
  langgraph_api/api/assistants.py,sha256=nU6tnbgdr_6Utlq0A9nw2a6xxpUM_DNuCFI42_Kcs_o,14233
5
- langgraph_api/api/mcp.py,sha256=dpKT9DgIoLERTmYZ4sSOPyHbfGbm7hCyb2MrMS_ol18,13593
5
+ langgraph_api/api/mcp.py,sha256=KbR19dtFCpJEiKYj3IfepAuJij8YZVELuVp7JY_yu_o,13754
6
6
  langgraph_api/api/meta.py,sha256=ifJ_Ki0Qf2DYbmY6OKlqKhLGxbt55gm0lEqH1A0cJbw,2790
7
7
  langgraph_api/api/openapi.py,sha256=f9gfmWN2AMKNUpLCpSgZuw_aeOF9jCXPdOtFT5PaTWM,10960
8
8
  langgraph_api/api/runs.py,sha256=uijgtrw_FSf1lZHsx8pM-CIj_ur2O88Y7ys-CJZ4SNQ,17988
@@ -11,14 +11,14 @@ langgraph_api/api/threads.py,sha256=kvv8pmRoUIvPFEuAhJpMC6qMv7Xo5itrzb5EzJFyWMg,
11
11
  langgraph_api/api/ui.py,sha256=LiOZVewKOPbKEykCm30hCEaOA7vuS_Ti5hB32EEy4vw,2082
12
12
  langgraph_api/asyncio.py,sha256=hVuAxWTHoUyNqTzcIEKTkAvh7HFrdGK6WIDowDxORAE,8397
13
13
  langgraph_api/auth/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
- langgraph_api/auth/custom.py,sha256=Gays0bYwZmRQA8-LSmQSNp1zS2_NG_xybG2cW79YweU,20890
14
+ langgraph_api/auth/custom.py,sha256=sPYkF-xTdGIn_WMSE2tfR_azg_azzXPRWwUfCye6GQ8,21401
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
18
  langgraph_api/auth/middleware.py,sha256=jU8aDSIZHdzCGdifejRF7ndHkSjBtqIHcBwFIuUdHEA,1875
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
- langgraph_api/cli.py,sha256=cSRKZzaxSbfDQO3-2YWAo2NgeE5IK0mabYWt4m5q-dU,12047
21
+ langgraph_api/cli.py,sha256=76wJLx1ekaRmXR5DxGy1_RuA0lQq2jBUzHAAVUKV2Ho,12163
22
22
  langgraph_api/command.py,sha256=3O9v3i0OPa96ARyJ_oJbLXkfO8rPgDhLCswgO9koTFA,768
23
23
  langgraph_api/config.py,sha256=MwTyJUr2wwZdUbL0gIvol_cCGi24Py9DvBnSozE1ai8,9342
24
24
  langgraph_api/cron_scheduler.py,sha256=9yzbbGxzNgJdIg4ZT7yu2oTwT_wRuPxD1c2sbbd52xs,2630
@@ -67,12 +67,12 @@ langgraph_api/middleware/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZ
67
67
  langgraph_api/middleware/http_logger.py,sha256=yuFPNFIWwn-4AE1CogBfWlo8KytzywLi_Bd4ccsyVQE,3150
68
68
  langgraph_api/middleware/private_network.py,sha256=eYgdyU8AzU2XJu362i1L8aSFoQRiV7_aLBPw7_EgeqI,2111
69
69
  langgraph_api/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
70
- langgraph_api/models/run.py,sha256=J1DpfMwv7W3zX9WtCANyObaCuZWBVfz2JWgdIBa9VzU,10180
70
+ langgraph_api/models/run.py,sha256=85-pyyvosFAot8WrWl2QjCje-2c4lhattYvoAEMqztA,11000
71
71
  langgraph_api/patch.py,sha256=82xjuFqY7tgrUm-k1XWHI6k8S6QovSD0zhe--8_xW4o,1296
72
72
  langgraph_api/queue_entrypoint.py,sha256=4xICUxXarNV8DhnaqAMhVi3xCmyVKCL3J5NzHxPA9Xc,1835
73
73
  langgraph_api/route.py,sha256=fM4qYCGbmH0a3_cV8uKocb1sLklehxO6HhdRXqLK6OM,4421
74
74
  langgraph_api/schema.py,sha256=hNbg6ep2wiGBBtBJVNBgMYA8uC33AfaqhRXXVUY_At8,5361
75
- langgraph_api/serde.py,sha256=HfaIBNfdKg2W_mIpw_er5irG3GlIiiVqkqfbDgnYHpg,3671
75
+ langgraph_api/serde.py,sha256=D5t_HeABMYKRAsoXWaWG0IsdaYM8yOXIez2wJUTIgT8,3963
76
76
  langgraph_api/server.py,sha256=bnXOOYztQmqR-QVpEFoRWB5Fzd33PuEIlwBK2R7W8NE,4849
77
77
  langgraph_api/sse.py,sha256=2wNodCOP2eg7a9mpSu0S3FQ0CHk2BBV_vv0UtIgJIcc,4034
78
78
  langgraph_api/state.py,sha256=8jx4IoTCOjTJuwzuXJKKFwo1VseHjNnw_CCq4x1SW14,2284
@@ -86,18 +86,18 @@ langgraph_license/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU
86
86
  langgraph_license/middleware.py,sha256=_ODIYzQkymr6W9_Fp9wtf1kAQspnpsmr53xuzyF2GA0,612
87
87
  langgraph_license/validation.py,sha256=Uu_G8UGO_WTlLsBEY0gTVWjRR4czYGfw5YAD3HLZoj0,203
88
88
  langgraph_storage/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
89
- langgraph_storage/checkpoint.py,sha256=X6YHrCwIPVE0iEQy16GhleyyHUdXdCkC-c-Q1SW-rAM,3884
89
+ langgraph_storage/checkpoint.py,sha256=Qq0y6vdh27qdF3h5nOLT5CcX9Rj2bcFqkVOMeCaGoK4,4036
90
90
  langgraph_storage/database.py,sha256=I0AgFeJ-NSTT34vxKxQBUf1z2syFP0S8QpKCqTixrzY,5652
91
91
  langgraph_storage/inmem_stream.py,sha256=8bxkILIuFpr7P7RQ37SQAxrpRKvmbHdRB_nbfFiomlk,3263
92
92
  langgraph_storage/ops.py,sha256=S0QHbnquEXvAM63NQMgqtyTjm-8nlq0pOvgJgFD38Z8,75312
93
93
  langgraph_storage/queue.py,sha256=UDgsUTtUMfBSRDrQ8Onis-FJO4n7KTsX6sdpbY8Hs0A,5055
94
94
  langgraph_storage/retry.py,sha256=XmldOP4e_H5s264CagJRVnQMDFcEJR_dldVR1Hm5XvM,763
95
- langgraph_storage/store.py,sha256=8QSM0gm6ZV40antWw0YHfnU71swZRsozjd85R-MSrjg,3100
95
+ langgraph_storage/store.py,sha256=JB9jZ87GE19MVN9wgl3-esgR2eIkeipws9q6qsPWkgc,3399
96
96
  langgraph_storage/ttl_dict.py,sha256=FlpEY8EANeXWKo_G5nmIotPquABZGyIJyk6HD9u6vqY,1533
97
97
  logging.json,sha256=3RNjSADZmDq38eHePMm1CbP6qZ71AmpBtLwCmKU9Zgo,379
98
98
  openapi.json,sha256=-25y3NRQ88e_944UXo76Goa34HJhC7pj6I9tjYUwvuE,131492
99
- langgraph_api-0.0.36.dist-info/LICENSE,sha256=ZPwVR73Biwm3sK6vR54djCrhaRiM4cAD2zvOQZV8Xis,3859
100
- langgraph_api-0.0.36.dist-info/METADATA,sha256=T5nIH34PPCdvY8DcFJWKDgrrNutgpgwXGmv52fX_2AI,4027
101
- langgraph_api-0.0.36.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
102
- langgraph_api-0.0.36.dist-info/entry_points.txt,sha256=3EYLgj89DfzqJHHYGxPH4A_fEtClvlRbWRUHaXO7hj4,77
103
- langgraph_api-0.0.36.dist-info/RECORD,,
99
+ langgraph_api-0.0.38.dist-info/LICENSE,sha256=ZPwVR73Biwm3sK6vR54djCrhaRiM4cAD2zvOQZV8Xis,3859
100
+ langgraph_api-0.0.38.dist-info/METADATA,sha256=kv_c0bBL9Vp6pM0NQOUv1uyruDkf5K67X74nSICrekw,4071
101
+ langgraph_api-0.0.38.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
102
+ langgraph_api-0.0.38.dist-info/entry_points.txt,sha256=3EYLgj89DfzqJHHYGxPH4A_fEtClvlRbWRUHaXO7hj4,77
103
+ langgraph_api-0.0.38.dist-info/RECORD,,
@@ -16,6 +16,9 @@ from langgraph_api.serde import Serializer
16
16
  logger = logging.getLogger(__name__)
17
17
 
18
18
  _EXCLUDED_KEYS = {"checkpoint_ns", "checkpoint_id", "run_id", "thread_id"}
19
+ DISABLE_FILE_PERSISTENCE = (
20
+ os.getenv("LANGGRAPH_DISABLE_FILE_PERSISTENCE", "false").lower() == "true"
21
+ )
19
22
 
20
23
 
21
24
  class InMemorySaver(MemorySaver):
@@ -56,7 +59,7 @@ class InMemorySaver(MemorySaver):
56
59
 
57
60
  super().__init__(
58
61
  serde=serde if serde is not None else Serializer(),
59
- factory=factory,
62
+ factory=factory if not DISABLE_FILE_PERSISTENCE else None,
60
63
  )
61
64
 
62
65
  def put(
@@ -13,13 +13,22 @@ from langgraph.store.memory import InMemoryStore
13
13
  from langgraph_api.graph import resolve_embeddings
14
14
 
15
15
  _STORE_CONFIG = None
16
+ DISABLE_FILE_PERSISTENCE = (
17
+ os.getenv("LANGGRAPH_DISABLE_FILE_PERSISTENCE", "false").lower() == "true"
18
+ )
16
19
 
17
20
 
18
21
  class DiskBackedInMemStore(InMemoryStore):
19
22
  def __init__(self, *args: Any, **kwargs: Any) -> None:
20
23
  super().__init__(*args, **kwargs)
21
- self._data = PersistentDict(dict, filename=_STORE_FILE)
22
- self._vectors = PersistentDict(lambda: defaultdict(dict), filename=_VECTOR_FILE)
24
+ if not DISABLE_FILE_PERSISTENCE:
25
+ self._data = PersistentDict(dict, filename=_STORE_FILE)
26
+ self._vectors = PersistentDict(
27
+ lambda: defaultdict(dict), filename=_VECTOR_FILE
28
+ )
29
+ else:
30
+ self._data = InMemoryStore._data
31
+ self._vectors = InMemoryStore._vectors
23
32
  self._load_data(self._data, which="data")
24
33
  self._load_data(self._vectors, which="vectors")
25
34