langgraph-runtime-inmem 0.14.1__tar.gz → 0.16.0__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.
Files changed (17) hide show
  1. {langgraph_runtime_inmem-0.14.1 → langgraph_runtime_inmem-0.16.0}/.gitignore +1 -0
  2. {langgraph_runtime_inmem-0.14.1 → langgraph_runtime_inmem-0.16.0}/PKG-INFO +3 -3
  3. {langgraph_runtime_inmem-0.14.1 → langgraph_runtime_inmem-0.16.0}/langgraph_runtime_inmem/__init__.py +1 -1
  4. {langgraph_runtime_inmem-0.14.1 → langgraph_runtime_inmem-0.16.0}/langgraph_runtime_inmem/database.py +3 -1
  5. {langgraph_runtime_inmem-0.14.1 → langgraph_runtime_inmem-0.16.0}/langgraph_runtime_inmem/lifespan.py +39 -1
  6. {langgraph_runtime_inmem-0.14.1 → langgraph_runtime_inmem-0.16.0}/langgraph_runtime_inmem/metrics.py +1 -1
  7. {langgraph_runtime_inmem-0.14.1 → langgraph_runtime_inmem-0.16.0}/langgraph_runtime_inmem/ops.py +41 -43
  8. {langgraph_runtime_inmem-0.14.1 → langgraph_runtime_inmem-0.16.0}/pyproject.toml +3 -3
  9. langgraph_runtime_inmem-0.16.0/uv.lock +1166 -0
  10. langgraph_runtime_inmem-0.14.1/uv.lock +0 -1003
  11. {langgraph_runtime_inmem-0.14.1 → langgraph_runtime_inmem-0.16.0}/Makefile +0 -0
  12. {langgraph_runtime_inmem-0.14.1 → langgraph_runtime_inmem-0.16.0}/README.md +0 -0
  13. {langgraph_runtime_inmem-0.14.1 → langgraph_runtime_inmem-0.16.0}/langgraph_runtime_inmem/checkpoint.py +0 -0
  14. {langgraph_runtime_inmem-0.14.1 → langgraph_runtime_inmem-0.16.0}/langgraph_runtime_inmem/inmem_stream.py +0 -0
  15. {langgraph_runtime_inmem-0.14.1 → langgraph_runtime_inmem-0.16.0}/langgraph_runtime_inmem/queue.py +0 -0
  16. {langgraph_runtime_inmem-0.14.1 → langgraph_runtime_inmem-0.16.0}/langgraph_runtime_inmem/retry.py +0 -0
  17. {langgraph_runtime_inmem-0.14.1 → langgraph_runtime_inmem-0.16.0}/langgraph_runtime_inmem/store.py +0 -0
@@ -65,3 +65,4 @@ pnpm-debug.log*
65
65
  .editorconfig
66
66
  *.db
67
67
  executor_py/tests/bin/engine-test-server
68
+ .notes/
@@ -1,13 +1,13 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: langgraph-runtime-inmem
3
- Version: 0.14.1
3
+ Version: 0.16.0
4
4
  Summary: Inmem implementation for the LangGraph API server.
5
5
  Author-email: Will Fu-Hinthorn <will@langchain.dev>
6
6
  License: Elastic-2.0
7
7
  Requires-Python: >=3.11.0
8
8
  Requires-Dist: blockbuster<2.0.0,>=1.5.24
9
- Requires-Dist: langgraph-checkpoint>=2.0.25
10
- Requires-Dist: langgraph>=0.2
9
+ Requires-Dist: langgraph-checkpoint<4,>=3
10
+ Requires-Dist: langgraph<2,>=0.4.10
11
11
  Requires-Dist: sse-starlette>=2
12
12
  Requires-Dist: starlette>=0.37
13
13
  Requires-Dist: structlog>23
@@ -9,7 +9,7 @@ from langgraph_runtime_inmem import (
9
9
  store,
10
10
  )
11
11
 
12
- __version__ = "0.14.1"
12
+ __version__ = "0.16.0"
13
13
  __all__ = [
14
14
  "ops",
15
15
  "database",
@@ -142,7 +142,9 @@ class InMemConnectionProto:
142
142
 
143
143
 
144
144
  @asynccontextmanager
145
- async def connect(*, __test__: bool = False) -> AsyncIterator["AsyncConnectionProto"]:
145
+ async def connect(
146
+ *, supports_core_api: bool = False, __test__: bool = False
147
+ ) -> AsyncIterator["AsyncConnectionProto"]:
146
148
  yield InMemConnectionProto()
147
149
 
148
150
 
@@ -14,6 +14,13 @@ from langgraph_runtime_inmem.database import start_pool, stop_pool
14
14
  logger = structlog.stdlib.get_logger(__name__)
15
15
 
16
16
 
17
+ _LAST_LIFESPAN_ERROR: BaseException | None = None
18
+
19
+
20
+ def get_last_error() -> BaseException | None:
21
+ return _LAST_LIFESPAN_ERROR
22
+
23
+
17
24
  @asynccontextmanager
18
25
  async def lifespan(
19
26
  app: Starlette | None = None,
@@ -42,9 +49,27 @@ async def lifespan(
42
49
  except RuntimeError:
43
50
  await logger.aerror("Failed to set loop")
44
51
 
52
+ global _LAST_LIFESPAN_ERROR
53
+ _LAST_LIFESPAN_ERROR = None
54
+
45
55
  await start_http_client()
46
56
  await start_pool()
47
57
  await start_ui_bundler()
58
+
59
+ async def _log_graph_load_failure(err: graph.GraphLoadError) -> None:
60
+ cause = err.__cause__ or err.cause
61
+ log_fields = err.log_fields()
62
+ log_fields["action"] = "fix_user_graph"
63
+ await logger.aerror(
64
+ f"Graph '{err.spec.id}' failed to load: {err.cause_message}",
65
+ **log_fields,
66
+ )
67
+ await logger.adebug(
68
+ "Full graph load failure traceback (internal)",
69
+ **{k: v for k, v in log_fields.items() if k != "user_traceback"},
70
+ exc_info=cause,
71
+ )
72
+
48
73
  try:
49
74
  async with SimpleTaskGroup(
50
75
  cancel=True,
@@ -77,11 +102,21 @@ async def lifespan(
77
102
  var_child_runnable_config.set(langgraph_config)
78
103
 
79
104
  # Keep after the setter above so users can access the store from within the factory function
80
- await graph.collect_graphs_from_env(True)
105
+ try:
106
+ await graph.collect_graphs_from_env(True)
107
+ except graph.GraphLoadError as exc:
108
+ _LAST_LIFESPAN_ERROR = exc
109
+ await _log_graph_load_failure(exc)
110
+ raise
81
111
  if config.N_JOBS_PER_WORKER > 0:
82
112
  tg.create_task(queue_with_signal())
83
113
 
84
114
  yield
115
+ except graph.GraphLoadError as exc:
116
+ _LAST_LIFESPAN_ERROR = exc
117
+ raise
118
+ except asyncio.CancelledError:
119
+ pass
85
120
  finally:
86
121
  await api_store.exit_store()
87
122
  await stop_ui_bundler()
@@ -98,3 +133,6 @@ async def queue_with_signal():
98
133
  except Exception as exc:
99
134
  logger.exception("Queue failed. Signaling shutdown", exc_info=exc)
100
135
  signal.raise_signal(signal.SIGINT)
136
+
137
+
138
+ lifespan.get_last_error = get_last_error # type: ignore[attr-defined]
@@ -1,7 +1,7 @@
1
1
  from langgraph_runtime_inmem.queue import get_num_workers
2
2
 
3
3
 
4
- def get_metrics() -> dict[str, int]:
4
+ def get_metrics() -> dict[str, dict[str, int]]:
5
5
  from langgraph_api import config
6
6
 
7
7
  workers_max = config.N_JOBS_PER_WORKER
@@ -556,6 +556,8 @@ class Assistants(Authenticated):
556
556
  "metadata": version_data["metadata"],
557
557
  "version": version_data["version"],
558
558
  "updated_at": datetime.now(UTC),
559
+ "name": version_data["name"],
560
+ "description": version_data["description"],
559
561
  }
560
562
  )
561
563
 
@@ -1230,13 +1232,23 @@ class Threads(Authenticated):
1230
1232
  """Create a copy of an existing thread."""
1231
1233
  thread_id = _ensure_uuid(thread_id)
1232
1234
  new_thread_id = uuid4()
1233
- filters = await Threads.handle_event(
1235
+ read_filters = await Threads.handle_event(
1234
1236
  ctx,
1235
1237
  "read",
1236
1238
  Auth.types.ThreadsRead(
1239
+ thread_id=thread_id,
1240
+ ),
1241
+ )
1242
+ # Assert that the user has permissions to create a new thread.
1243
+ # (We don't actually need the filters.)
1244
+ await Threads.handle_event(
1245
+ ctx,
1246
+ "create",
1247
+ Auth.types.ThreadsCreate(
1237
1248
  thread_id=new_thread_id,
1238
1249
  ),
1239
1250
  )
1251
+
1240
1252
  async with conn.pipeline():
1241
1253
  # Find the original thread in our store
1242
1254
  original_thread = next(
@@ -1245,8 +1257,8 @@ class Threads(Authenticated):
1245
1257
 
1246
1258
  if not original_thread:
1247
1259
  return _empty_generator()
1248
- if filters and not _check_filter_match(
1249
- original_thread["metadata"], filters
1260
+ if read_filters and not _check_filter_match(
1261
+ original_thread["metadata"], read_filters
1250
1262
  ):
1251
1263
  return _empty_generator()
1252
1264
 
@@ -1399,6 +1411,7 @@ class Threads(Authenticated):
1399
1411
  """Add state to a thread."""
1400
1412
  from langgraph_api.graph import get_graph
1401
1413
  from langgraph_api.schema import ThreadUpdateResponse
1414
+ from langgraph_api.state import state_snapshot_to_thread_state
1402
1415
  from langgraph_api.store import get_store
1403
1416
  from langgraph_api.utils import fetchone
1404
1417
 
@@ -1487,7 +1500,7 @@ class Threads(Authenticated):
1487
1500
  from langgraph_api.serde import json_dumpb
1488
1501
 
1489
1502
  event_data = {
1490
- "state": state,
1503
+ "state": state_snapshot_to_thread_state(state),
1491
1504
  "thread_id": str(thread_id),
1492
1505
  }
1493
1506
  await Threads.Stream.publish(
@@ -2107,7 +2120,6 @@ class Runs(Authenticated):
2107
2120
  ctx: Auth.types.BaseAuthContext | None = None,
2108
2121
  ) -> AsyncIterator[Run]:
2109
2122
  """Create a run."""
2110
- from langgraph_api.config import FF_RICH_THREADS
2111
2123
  from langgraph_api.schema import Run, Thread
2112
2124
 
2113
2125
  assistant_id = _ensure_uuid(assistant_id)
@@ -2154,49 +2166,35 @@ class Runs(Authenticated):
2154
2166
  # Create new thread
2155
2167
  if thread_id is None:
2156
2168
  thread_id = uuid4()
2157
- if FF_RICH_THREADS:
2158
- thread = Thread(
2159
- thread_id=thread_id,
2160
- status="busy",
2161
- metadata={
2162
- "graph_id": assistant["graph_id"],
2163
- "assistant_id": str(assistant_id),
2164
- **(config.get("metadata") or {}),
2165
- **metadata,
2166
- },
2167
- config=Runs._merge_jsonb(
2168
- assistant["config"],
2169
- config,
2170
- {
2171
- "configurable": Runs._merge_jsonb(
2172
- Runs._get_configurable(assistant["config"]),
2173
- )
2174
- },
2175
- ),
2176
- created_at=datetime.now(UTC),
2177
- updated_at=datetime.now(UTC),
2178
- values=b"",
2179
- )
2180
- else:
2181
- thread = Thread(
2182
- thread_id=thread_id,
2183
- status="idle",
2184
- metadata={
2185
- "graph_id": assistant["graph_id"],
2186
- "assistant_id": str(assistant_id),
2187
- **(config.get("metadata") or {}),
2188
- **metadata,
2169
+
2170
+ thread = Thread(
2171
+ thread_id=thread_id,
2172
+ status="busy",
2173
+ metadata={
2174
+ "graph_id": assistant["graph_id"],
2175
+ "assistant_id": str(assistant_id),
2176
+ **(config.get("metadata") or {}),
2177
+ **metadata,
2178
+ },
2179
+ config=Runs._merge_jsonb(
2180
+ assistant["config"],
2181
+ config,
2182
+ {
2183
+ "configurable": Runs._merge_jsonb(
2184
+ Runs._get_configurable(assistant["config"]),
2185
+ )
2189
2186
  },
2190
- config={},
2191
- created_at=datetime.now(UTC),
2192
- updated_at=datetime.now(UTC),
2193
- values=b"",
2194
- )
2187
+ ),
2188
+ created_at=datetime.now(UTC),
2189
+ updated_at=datetime.now(UTC),
2190
+ values=b"",
2191
+ )
2192
+
2195
2193
  await logger.ainfo("Creating thread", thread_id=thread_id)
2196
2194
  conn.store["threads"].append(thread)
2197
2195
  elif existing_thread:
2198
2196
  # Update existing thread
2199
- if FF_RICH_THREADS and existing_thread["status"] != "busy":
2197
+ if existing_thread["status"] != "busy":
2200
2198
  existing_thread["status"] = "busy"
2201
2199
  existing_thread["metadata"] = Runs._merge_jsonb(
2202
2200
  existing_thread["metadata"],
@@ -14,11 +14,11 @@ readme = "README.md"
14
14
  requires-python = ">=3.11.0"
15
15
  dependencies = [
16
16
  "blockbuster>=1.5.24,<2.0.0",
17
- "langgraph>=0.2",
17
+ "langgraph>=0.4.10,<2",
18
18
  "structlog>23",
19
19
  "sse-starlette>=2",
20
20
  "starlette>=0.37",
21
- "langgraph-checkpoint>=2.0.25",
21
+ "langgraph-checkpoint>=3,<4",
22
22
  ]
23
23
 
24
24
  [dependency-groups]
@@ -62,4 +62,4 @@ exclude = [
62
62
  [tool.ruff]
63
63
  lint.select = ["E", "F", "UP", "B", "I"]
64
64
  lint.ignore = ["E501", "B008"]
65
- target-version = "py311"
65
+ target-version = "py311"