langgraph-api 0.0.8__py3-none-any.whl → 0.0.10__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.

@@ -3,9 +3,10 @@ from random import random
3
3
 
4
4
  import structlog
5
5
  from langchain_core.runnables.config import run_in_executor
6
+ from starlette.authentication import SimpleUser
6
7
 
7
8
  from langgraph_api.models.run import create_valid_run
8
- from langgraph_api.utils import next_cron_date
9
+ from langgraph_api.utils import next_cron_date, with_user
9
10
  from langgraph_storage.database import connect
10
11
  from langgraph_storage.ops import Crons
11
12
  from langgraph_storage.retry import retry_db
@@ -22,35 +23,39 @@ async def cron_scheduler():
22
23
  try:
23
24
  async with connect() as conn:
24
25
  async for cron in Crons.next(conn):
25
- logger.debug(f"Scheduling cron run {cron}")
26
- try:
27
- run_payload = cron["payload"]
28
- run = await create_valid_run(
29
- conn,
30
- thread_id=str(cron.get("thread_id"))
31
- if cron.get("thread_id")
32
- else None,
33
- payload=run_payload,
34
- user_id=cron.get("user_id"),
35
- headers={},
36
- )
37
- if not run:
38
- logger.error(
39
- "Run not created for cron_id={} payload".format(
40
- cron["cron_id"],
26
+ async with with_user(SimpleUser(str(cron["user_id"])), None):
27
+ logger.debug(f"Scheduling cron run {cron}")
28
+ try:
29
+ run_payload = cron["payload"]
30
+ run = await create_valid_run(
31
+ conn,
32
+ thread_id=(
33
+ str(cron.get("thread_id"))
34
+ if cron.get("thread_id")
35
+ else None
36
+ ),
37
+ payload=run_payload,
38
+ headers={},
39
+ )
40
+ if not run:
41
+ logger.error(
42
+ "Run not created for cron_id={} payload".format(
43
+ cron["cron_id"],
44
+ )
41
45
  )
46
+ except Exception as e:
47
+ logger.error(
48
+ "Error scheduling cron run cron_id={}".format(
49
+ cron["cron_id"]
50
+ ),
51
+ exc_info=e,
42
52
  )
43
- except Exception as e:
44
- logger.error(
45
- "Error scheduling cron run cron_id={}".format(
46
- cron["cron_id"]
47
- ),
48
- exc_info=e,
53
+ next_run_date = await run_in_executor(
54
+ None, next_cron_date, cron["schedule"], cron["now"]
55
+ )
56
+ await Crons.set_next_run_date(
57
+ conn, cron["cron_id"], next_run_date
49
58
  )
50
- next_run_date = await run_in_executor(
51
- None, next_cron_date, cron["schedule"], cron["now"]
52
- )
53
- await Crons.set_next_run_date(conn, cron["cron_id"], next_run_date)
54
59
 
55
60
  await asyncio.sleep(SLEEP_TIME)
56
61
  except asyncio.CancelledError:
langgraph_api/graph.py CHANGED
@@ -366,17 +366,6 @@ def _graph_from_spec(spec: GraphSpec) -> GraphValue:
366
366
  return graph
367
367
 
368
368
 
369
- def _load(path: str, variable: str) -> None:
370
- modname = "".join(choice("abcdefghijklmnopqrstuvwxyz") for _ in range(24))
371
- modspec = importlib.util.spec_from_file_location(modname, path)
372
- if modspec is None:
373
- raise ValueError(f"Could not find python file for embeddings: {path}")
374
- module = importlib.util.module_from_spec(modspec)
375
- sys.modules[modname] = module
376
- modspec.loader.exec_module(module)
377
- return module.__dict__[variable]
378
-
379
-
380
369
  @functools.lru_cache
381
370
  def _get_init_embeddings() -> Callable[[str, ...], "Embeddings"] | None:
382
371
  try:
@@ -1,10 +1,11 @@
1
1
  import asyncio
2
2
  import uuid
3
3
  from collections.abc import Mapping, Sequence
4
- from typing import Any, TypedDict
4
+ from typing import Any, NamedTuple, TypedDict
5
5
  from uuid import UUID
6
6
 
7
7
  from langgraph.checkpoint.base.id import uuid6
8
+ from starlette.authentication import BaseUser
8
9
  from starlette.exceptions import HTTPException
9
10
 
10
11
  from langgraph_api.graph import get_assistant_id
@@ -19,7 +20,7 @@ from langgraph_api.schema import (
19
20
  RunCommand,
20
21
  StreamMode,
21
22
  )
22
- from langgraph_api.utils import AsyncConnectionProto
23
+ from langgraph_api.utils import AsyncConnectionProto, get_auth_ctx
23
24
  from langgraph_storage.ops import Runs, logger
24
25
 
25
26
 
@@ -82,24 +83,32 @@ class RunCreateDict(TypedDict):
82
83
 
83
84
 
84
85
  def ensure_ids(
85
- assistant_id: str,
86
- thread_id: str | None,
86
+ assistant_id: str | UUID,
87
+ thread_id: str | UUID | None,
87
88
  payload: RunCreateDict,
88
89
  ) -> tuple[uuid.UUID, uuid.UUID | None, uuid.UUID | None]:
89
90
  try:
90
- results = [UUID(assistant_id)]
91
+ results = [
92
+ assistant_id if isinstance(assistant_id, UUID) else UUID(assistant_id)
93
+ ]
91
94
  except ValueError:
92
95
  raise HTTPException(status_code=422, detail="Invalid assistant ID") from None
93
96
  if thread_id:
94
97
  try:
95
- results.append(UUID(thread_id))
98
+ results.append(
99
+ thread_id if isinstance(thread_id, UUID) else UUID(thread_id)
100
+ )
96
101
  except ValueError:
97
102
  raise HTTPException(status_code=422, detail="Invalid thread ID") from None
98
103
  else:
99
104
  results.append(None)
100
- if payload.get("checkpoint_id"):
105
+ if checkpoint_id := payload.get("checkpoint_id"):
101
106
  try:
102
- results.append(UUID(payload.get("checkpoint_id")))
107
+ results.append(
108
+ checkpoint_id
109
+ if isinstance(checkpoint_id, UUID)
110
+ else UUID(checkpoint_id)
111
+ )
103
112
  except ValueError:
104
113
  raise HTTPException(
105
114
  status_code=422, detail="Invalid checkpoint ID"
@@ -125,27 +134,40 @@ def assign_defaults(
125
134
  return stream_mode, multitask_strategy, prevent_insert_if_inflight
126
135
 
127
136
 
137
+ def get_user_id(user: BaseUser | None) -> str | None:
138
+ if user is None:
139
+ return None
140
+ try:
141
+ return user.identity
142
+ except NotImplementedError:
143
+ try:
144
+ return user.display_name
145
+ except NotImplementedError:
146
+ pass
147
+
148
+
128
149
  async def create_valid_run(
129
150
  conn: AsyncConnectionProto,
130
151
  thread_id: str | None,
131
152
  payload: RunCreateDict,
132
- user_id: str | None,
133
153
  headers: Mapping[str, str],
134
154
  barrier: asyncio.Barrier | None = None,
135
155
  run_id: UUID | None = None,
136
156
  ) -> Run:
137
- # get assistant_id
138
- assistant_id = get_assistant_id(payload["assistant_id"])
139
-
140
- # ensure UUID validity defaults
141
- assistant_id, thread_id, checkpoint_id = ensure_ids(
142
- assistant_id, thread_id, payload
157
+ (
158
+ assistant_id,
159
+ thread_id,
160
+ checkpoint_id,
161
+ run_id,
162
+ ) = _get_ids(
163
+ thread_id,
164
+ payload,
165
+ run_id=run_id,
143
166
  )
167
+ temporary = thread_id is None and payload.get("on_completion", "delete") == "delete"
144
168
  stream_mode, multitask_strategy, prevent_insert_if_inflight = assign_defaults(
145
169
  payload
146
170
  )
147
- temporary = thread_id is None and payload.get("on_completion", "delete") == "delete"
148
- run_id = run_id or uuid6()
149
171
 
150
172
  # assign custom headers and checkpoint to config
151
173
  config = payload.get("config") or {}
@@ -163,8 +185,14 @@ async def create_valid_run(
163
185
  ):
164
186
  continue
165
187
  config["configurable"][key] = value
166
-
167
- # try to insert
188
+ ctx = get_auth_ctx()
189
+ if ctx:
190
+ user = ctx.user
191
+ user_id = get_user_id(user)
192
+ config["configurable"]["langgraph_auth_user"] = user
193
+ config["configurable"]["langgraph_auth_user_id"] = user_id
194
+ else:
195
+ user_id = None
168
196
  run_coro = Runs.put(
169
197
  conn,
170
198
  assistant_id,
@@ -227,3 +255,33 @@ async def create_valid_run(
227
255
  )
228
256
  else:
229
257
  raise NotImplementedError
258
+
259
+
260
+ class _Ids(NamedTuple):
261
+ assistant_id: uuid.UUID
262
+ thread_id: uuid.UUID | None
263
+ checkpoint_id: uuid.UUID | None
264
+ run_id: uuid.UUID
265
+
266
+
267
+ def _get_ids(
268
+ thread_id: str | None,
269
+ payload: RunCreateDict,
270
+ run_id: UUID | None = None,
271
+ ) -> _Ids:
272
+ # get assistant_id
273
+ assistant_id = get_assistant_id(payload["assistant_id"])
274
+
275
+ # ensure UUID validity defaults
276
+ assistant_id, thread_id, checkpoint_id = ensure_ids(
277
+ assistant_id, thread_id, payload
278
+ )
279
+
280
+ run_id = run_id or uuid6()
281
+
282
+ return _Ids(
283
+ assistant_id,
284
+ thread_id,
285
+ checkpoint_id,
286
+ run_id,
287
+ )
langgraph_api/route.py CHANGED
@@ -14,6 +14,7 @@ from starlette.routing import Route, compile_path, get_name
14
14
  from starlette.types import ASGIApp, Receive, Scope, Send
15
15
 
16
16
  from langgraph_api.serde import json_dumpb
17
+ from langgraph_api.utils import set_auth_ctx
17
18
 
18
19
 
19
20
  def api_request_response(
@@ -115,4 +116,5 @@ class ApiRoute(Route):
115
116
  async def handle(self, scope: Scope, receive: Receive, send: Send) -> None:
116
117
  # https://asgi.readthedocs.io/en/latest/specs/www.html#http-connection-scope
117
118
  scope["route"] = self.path
119
+ set_auth_ctx(scope.get("user"), scope.get("auth"))
118
120
  return await super().handle(scope, receive, send)
langgraph_api/utils.py CHANGED
@@ -1,13 +1,45 @@
1
+ import contextvars
1
2
  import uuid
2
3
  from collections.abc import AsyncIterator
3
4
  from contextlib import asynccontextmanager
4
5
  from datetime import datetime
5
6
  from typing import Any, Protocol, TypeAlias, TypeVar
6
7
 
8
+ from langgraph_sdk import Auth
9
+ from starlette.authentication import AuthCredentials, BaseUser
7
10
  from starlette.exceptions import HTTPException
8
11
 
9
12
  T = TypeVar("T")
10
13
  Row: TypeAlias = dict[str, Any]
14
+ AuthContext = contextvars.ContextVar[Auth.types.BaseAuthContext | None](
15
+ "AuthContext", default=None
16
+ )
17
+
18
+
19
+ @asynccontextmanager
20
+ async def with_user(user: BaseUser | None = None, auth: AuthCredentials | None = None):
21
+ current = get_auth_ctx()
22
+ set_auth_ctx(user, auth)
23
+ yield
24
+ if current is None:
25
+ return
26
+ set_auth_ctx(current.user, AuthCredentials(scopes=current.scopes))
27
+
28
+
29
+ def set_auth_ctx(user: BaseUser | None, auth: AuthCredentials | None) -> None:
30
+ if not user or not auth:
31
+ AuthContext.set(None)
32
+ else:
33
+ AuthContext.set(
34
+ Auth.types.BaseAuthContext(
35
+ scopes=auth.scopes,
36
+ user=user,
37
+ )
38
+ )
39
+
40
+
41
+ def get_auth_ctx() -> Auth.types.BaseAuthContext | None:
42
+ return AuthContext.get()
11
43
 
12
44
 
13
45
  class AsyncCursorProto(Protocol):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: langgraph-api
3
- Version: 0.0.8
3
+ Version: 0.0.10
4
4
  Summary:
5
5
  License: Elastic-2.0
6
6
  Author: Nuno Campos
@@ -16,7 +16,7 @@ Requires-Dist: jsonschema-rs (>=0.25.0,<0.26.0)
16
16
  Requires-Dist: langchain-core (>=0.2.38,<0.4.0)
17
17
  Requires-Dist: langgraph (>=0.2.56,<0.3.0)
18
18
  Requires-Dist: langgraph-checkpoint (>=2.0.7,<3.0)
19
- Requires-Dist: langsmith (>=0.1.63,<0.2.0)
19
+ Requires-Dist: langsmith (>=0.1.63,<0.3.0)
20
20
  Requires-Dist: orjson (>=3.10.1)
21
21
  Requires-Dist: pyjwt (>=2.9.0,<3.0.0)
22
22
  Requires-Dist: sse-starlette (>=2.1.0,<3.0.0)
@@ -3,22 +3,23 @@ langgraph_api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
3
  langgraph_api/api/__init__.py,sha256=tlMXuqnyJt99aSlUXwR-dS3w5X6sDDczJu4hbm2LP30,2057
4
4
  langgraph_api/api/assistants.py,sha256=3v4v7kLmlb2meSAnNt73toijnaSikbJgH9Jhb6IL04g,11270
5
5
  langgraph_api/api/meta.py,sha256=hueasWpTDQ6xYLo9Bzt2jhNH8XQRzreH8FTeFfnRoxQ,2700
6
- langgraph_api/api/openapi.py,sha256=zvKMMebBP28hLPMNt1kTtFK17aV9Tz-ok2okEM45XjA,906
7
- langgraph_api/api/runs.py,sha256=X3fPWVjj4IUGWMIkjVkddVSqBaNh2f9HqtXNVLol8-Q,15961
6
+ langgraph_api/api/openapi.py,sha256=3eRo3V6Pni8DjqHI_MNLn8F5CEOOGVBMXPEffft8Ubo,2781
7
+ langgraph_api/api/runs.py,sha256=wAzPXi_kcYB9BcLBL4FXgkBohWwCPIpe4XERnsnWnsA,16042
8
8
  langgraph_api/api/store.py,sha256=y7VIejpsE7rpPF-tiMGBqqBwWPZ1wb3o48th6NUvb5I,3849
9
9
  langgraph_api/api/threads.py,sha256=taU61XPcCEhBPCYPZcMDsgVDwwWUWJs8p-PrXFXWY48,8661
10
10
  langgraph_api/asyncio.py,sha256=XiFEllu-Kg4zAO084npHPYOPnLQRire3V75XrVQYMxE,6023
11
11
  langgraph_api/auth/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
+ langgraph_api/auth/custom.py,sha256=nE8dgtSuX4W8UoByEmr5aH0yK6H_rk0VOoETET9BqJQ,17528
12
13
  langgraph_api/auth/langsmith/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
14
  langgraph_api/auth/langsmith/backend.py,sha256=uHeb5-h13NIjrX_LDAvfWYr3zpbJvlvbdUffch48hbM,2571
14
15
  langgraph_api/auth/langsmith/client.py,sha256=eKchvAom7hdkUXauD8vHNceBDDUijrFgdTV8bKd7x4Q,3998
15
- langgraph_api/auth/middleware.py,sha256=_gJTOskEaln4RUT2rVYdQGPJVAyAiq-orsL_eQ3CynE,1369
16
+ langgraph_api/auth/middleware.py,sha256=eHj4D2p2ASpMKzoTgMkEUXMhuubQdg9Jnq0gQS9qbvE,1637
16
17
  langgraph_api/auth/noop.py,sha256=vDJmzG2vArJxVzdHePvrJWahEa0dvGnhc2LEMMeiFz0,391
17
- langgraph_api/cli.py,sha256=qQIJHw8doFE-sLebnqnUZjukeYp_WW4GDQ443l3z0ag,10281
18
- langgraph_api/config.py,sha256=cG6eO4P_SZ2pKedb2b4n4vnBHRQr0aiECvGvOA8ZlJA,2259
19
- langgraph_api/cron_scheduler.py,sha256=DAzY2DsADzEpPVbG2BOSLTIufI93yeRswd71Aby_lV0,2257
18
+ langgraph_api/cli.py,sha256=7vQQiD3F50r-8KkbuFjwIz8LLbdKUTd4xZGUJPiO3yQ,11688
19
+ langgraph_api/config.py,sha256=m3cf13AS9HVF84deJ3-9mmNTJ5GSVqcukES-FNbDbvo,2794
20
+ langgraph_api/cron_scheduler.py,sha256=CybK-9Jwopi_scObTHRyB7gyb0KjC4gqaT2GLe-WOFg,2587
20
21
  langgraph_api/errors.py,sha256=Bu_i5drgNTyJcLiyrwVE_6-XrSU50BHf9TDpttki9wQ,1690
21
- langgraph_api/graph.py,sha256=gpLblFbqtT2VQU2I69KvRX8mqOJRJDxQW5Sh1fW8FXY,16268
22
+ langgraph_api/graph.py,sha256=LXTo2lGnku3KPBiC9ssh4NcSvieT4Ks2vJuhPaqBCpY,15807
22
23
  langgraph_api/http.py,sha256=XrbyxpjtfSvnaWWh5ZLGpgZmY83WoDCrP_1GPguNiXI,4712
23
24
  langgraph_api/http_logger.py,sha256=Sxo_q-65tElauRvkzVLt9lJojgNdgtcHGBYD0IRyX7M,3146
24
25
  langgraph_api/js/.gitignore,sha256=qAah3Fq0HWAlfRj5ktZyC6QRQIsAolGLRGcRukA1XJI,33
@@ -54,33 +55,33 @@ langgraph_api/lifespan.py,sha256=Uj7NV-NqxxD1fgx_umM9pVqclcy-VlqrIxDljyj2he0,182
54
55
  langgraph_api/logging.py,sha256=tiDNrEFwqaIdL5ywZv908OXlzzfXsPCws9GXeoFtBV8,3367
55
56
  langgraph_api/metadata.py,sha256=mih2G7ScQxiqyUlbksVXkqR3Oo-pM1b6lXtzOsgR1sw,3044
56
57
  langgraph_api/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
57
- langgraph_api/models/run.py,sha256=nNhlG-l2P-DThFWYmCaar0UuPctadB9sH3CCWJfLqVc,8178
58
+ langgraph_api/models/run.py,sha256=qGTcgQjnzmXrxer5VPaGHoGwifV1nyE4ZCXtBiZH1bc,9546
58
59
  langgraph_api/patch.py,sha256=94ddcTSZJe22JcpjxiSNjFZdYVnmeoWjk4IX4iBSoyk,1249
59
60
  langgraph_api/queue.py,sha256=7tsbDgv4GlUYieJsrvIJDMQUEok4Eu-n_PIQ93rwKjk,9810
60
- langgraph_api/route.py,sha256=Dzje_dSigJramglqkt4ERT9-cb2xFli7dx25ZV6B6mI,4147
61
+ langgraph_api/route.py,sha256=HnAcWeStCrWNe37YUXIqEsjsrCPCIPGijHG7pM9ga28,4251
61
62
  langgraph_api/schema.py,sha256=EiCWRR2GmGrBrOYcuK9SeVQS5b98SdaJlKaqOL7t-WQ,5263
62
63
  langgraph_api/serde.py,sha256=VoJ7Z1IuqrQGXFzEP1qijAITtWCrmjtVqlCRuScjXJI,3533
63
64
  langgraph_api/server.py,sha256=afHDnL6b_fAIu_q4icnK60a74lHTTZOMIe1egdhRXIk,1522
64
65
  langgraph_api/sse.py,sha256=2wNodCOP2eg7a9mpSu0S3FQ0CHk2BBV_vv0UtIgJIcc,4034
65
66
  langgraph_api/state.py,sha256=8jx4IoTCOjTJuwzuXJKKFwo1VseHjNnw_CCq4x1SW14,2284
66
67
  langgraph_api/stream.py,sha256=u0gjbCrmYvVw8Ux6DgsYTojLCHSwM4Pi-0LhSLGY4HM,11546
67
- langgraph_api/utils.py,sha256=FI50tOFMVidV4-1TefouL1N-OJX41qD_fSEoWigTtf0,1575
68
+ langgraph_api/utils.py,sha256=17AZrdVzGHUQ7Y8b82Mknxe1LD5JMo4tZsb6TCcXEW0,2474
68
69
  langgraph_api/validation.py,sha256=McizHlz-Ez8Jhdbc79mbPSde7GIuf2Jlbjx2yv_l6dA,4475
69
70
  langgraph_license/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
70
71
  langgraph_license/middleware.py,sha256=_ODIYzQkymr6W9_Fp9wtf1kAQspnpsmr53xuzyF2GA0,612
71
72
  langgraph_license/validation.py,sha256=Uu_G8UGO_WTlLsBEY0gTVWjRR4czYGfw5YAD3HLZoj0,203
72
73
  langgraph_storage/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
73
- langgraph_storage/checkpoint.py,sha256=55J7W0s5Z7svqqan9wphubSgCphWUItq98j-iWVwbH8,2774
74
- langgraph_storage/database.py,sha256=jYAC1tDijO7jTPABxAKB2GutTKLf3EkbMI9VAIM3dT8,5186
75
- langgraph_storage/ops.py,sha256=Uev3bLTrEP53vX9POHIEo0eYEVH0pyZiLmliE04VCIM,52708
74
+ langgraph_storage/checkpoint.py,sha256=V4t2GwYEJdPCHbhq_4Udhlv0TWKDzlMu_rlNPdTDc50,3589
75
+ langgraph_storage/database.py,sha256=Nr5zE9Fur3-tESkqe7xNXMf2QlBuw3H0CUie7jVa6Q4,6003
76
+ langgraph_storage/ops.py,sha256=V0gnBlpzW3AE9kBNcT_uONYYEHEdgTR6a4mA9Owz6S4,67669
76
77
  langgraph_storage/queue.py,sha256=6cTZ0ubHu3S1T43yxHMVOwsQsDaJupByiU0sTUFFls8,3261
77
78
  langgraph_storage/retry.py,sha256=uvYFuXJ-T6S1QY1ZwkZHyZQbsvS-Ab68LSbzbUUSI2E,696
78
79
  langgraph_storage/store.py,sha256=D-p3cWc_umamkKp-6Cz3cAriSACpvM5nxUIvND6PuxE,2710
79
80
  langgraph_storage/ttl_dict.py,sha256=FlpEY8EANeXWKo_G5nmIotPquABZGyIJyk6HD9u6vqY,1533
80
81
  logging.json,sha256=3RNjSADZmDq38eHePMm1CbP6qZ71AmpBtLwCmKU9Zgo,379
81
82
  openapi.json,sha256=UxAGHZYM4PgNd48TSZt7f2lVuyPUkDadxBBhRy5jcmk,124512
82
- langgraph_api-0.0.8.dist-info/LICENSE,sha256=ZPwVR73Biwm3sK6vR54djCrhaRiM4cAD2zvOQZV8Xis,3859
83
- langgraph_api-0.0.8.dist-info/METADATA,sha256=A_zUXmA2dKkblSdlXqHFDeKePEWLja9Yenl57LleJKs,3993
84
- langgraph_api-0.0.8.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
85
- langgraph_api-0.0.8.dist-info/entry_points.txt,sha256=3EYLgj89DfzqJHHYGxPH4A_fEtClvlRbWRUHaXO7hj4,77
86
- langgraph_api-0.0.8.dist-info/RECORD,,
83
+ langgraph_api-0.0.10.dist-info/LICENSE,sha256=ZPwVR73Biwm3sK6vR54djCrhaRiM4cAD2zvOQZV8Xis,3859
84
+ langgraph_api-0.0.10.dist-info/METADATA,sha256=VgLW5dnP15w_SuuNkgpgYVjDrBGxxuKGvvEaDOkxMhk,3994
85
+ langgraph_api-0.0.10.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
86
+ langgraph_api-0.0.10.dist-info/entry_points.txt,sha256=3EYLgj89DfzqJHHYGxPH4A_fEtClvlRbWRUHaXO7hj4,77
87
+ langgraph_api-0.0.10.dist-info/RECORD,,
@@ -1,3 +1,4 @@
1
+ import logging
1
2
  import os
2
3
  import uuid
3
4
 
@@ -12,6 +13,8 @@ from langgraph.checkpoint.memory import MemorySaver, PersistentDict
12
13
 
13
14
  from langgraph_api.serde import Serializer
14
15
 
16
+ logger = logging.getLogger(__name__)
17
+
15
18
  _EXCLUDED_KEYS = {"checkpoint_ns", "checkpoint_id", "run_id", "thread_id"}
16
19
 
17
20
 
@@ -36,6 +39,19 @@ class InMemorySaver(MemorySaver):
36
39
  d.load()
37
40
  except FileNotFoundError:
38
41
  pass
42
+ except ModuleNotFoundError:
43
+ logger.error(
44
+ "Unable to load cached data - your code has changed in a way that's incompatible with the cache."
45
+ "\nThis usually happens when you've:"
46
+ "\n - Renamed or moved classes"
47
+ "\n - Changed class structures"
48
+ "\n - Pulled updates that modified class definitions in a way that's incompatible with the cache"
49
+ "\n\nRemoving invalid cache data stored at path: .langgraph_api"
50
+ )
51
+ os.remove(self.filename)
52
+ except Exception as e:
53
+ logger.error("Failed to load cached data: %s", str(e))
54
+ os.remove(self.filename)
39
55
  return d
40
56
 
41
57
  super().__init__(
@@ -164,7 +164,23 @@ async def start_pool() -> None:
164
164
  if not os.path.exists(".langgraph_api"):
165
165
  os.mkdir(".langgraph_api")
166
166
  if os.path.exists(OPS_FILENAME):
167
- GLOBAL_STORE.load()
167
+ try:
168
+ GLOBAL_STORE.load()
169
+ except ModuleNotFoundError:
170
+ logger.error(
171
+ "Unable to load cached data - your code has changed in a way that's incompatible with the cache."
172
+ "\nThis usually happens when you've:"
173
+ "\n - Renamed or moved classes"
174
+ "\n - Changed class structures"
175
+ "\n - Pulled updates that modified class definitions in a way that's incompatible with the cache"
176
+ "\n\nRemoving invalid cache data stored at path: .langgraph_api"
177
+ )
178
+ os.remove(OPS_FILENAME)
179
+ os.remove(RETRY_COUNTER_FILENAME)
180
+ except Exception as e:
181
+ logger.error("Failed to load cached data: %s", str(e))
182
+ os.remove(OPS_FILENAME)
183
+ os.remove(RETRY_COUNTER_FILENAME)
168
184
  for k in ["runs", "threads", "assistants", "assistant_versions"]:
169
185
  if not GLOBAL_STORE.get(k):
170
186
  GLOBAL_STORE[k] = []