graphharbor-runtime 0.13.0.post33__tar.gz → 0.13.0.post35__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 (62) hide show
  1. {graphharbor_runtime-0.13.0.post33 → graphharbor_runtime-0.13.0.post35}/PKG-INFO +1 -1
  2. {graphharbor_runtime-0.13.0.post33 → graphharbor_runtime-0.13.0.post35}/pyproject.toml +1 -1
  3. {graphharbor_runtime-0.13.0.post33 → graphharbor_runtime-0.13.0.post35}/src/langgraph_runtime_pg/authorization.py +3 -1
  4. {graphharbor_runtime-0.13.0.post33 → graphharbor_runtime-0.13.0.post35}/tests/test_event_retention.py +79 -0
  5. {graphharbor_runtime-0.13.0.post33 → graphharbor_runtime-0.13.0.post35}/.gitignore +0 -0
  6. {graphharbor_runtime-0.13.0.post33 → graphharbor_runtime-0.13.0.post35}/LICENSE +0 -0
  7. {graphharbor_runtime-0.13.0.post33 → graphharbor_runtime-0.13.0.post35}/README.md +0 -0
  8. {graphharbor_runtime-0.13.0.post33 → graphharbor_runtime-0.13.0.post35}/src/langgraph_runtime_pg/__init__.py +0 -0
  9. {graphharbor_runtime-0.13.0.post33 → graphharbor_runtime-0.13.0.post35}/src/langgraph_runtime_pg/auth.py +0 -0
  10. {graphharbor_runtime-0.13.0.post33 → graphharbor_runtime-0.13.0.post35}/src/langgraph_runtime_pg/checkpoint.py +0 -0
  11. {graphharbor_runtime-0.13.0.post33 → graphharbor_runtime-0.13.0.post35}/src/langgraph_runtime_pg/checkpoint_mutations.py +0 -0
  12. {graphharbor_runtime-0.13.0.post33 → graphharbor_runtime-0.13.0.post35}/src/langgraph_runtime_pg/database.py +0 -0
  13. {graphharbor_runtime-0.13.0.post33 → graphharbor_runtime-0.13.0.post35}/src/langgraph_runtime_pg/graph_executor.py +0 -0
  14. {graphharbor_runtime-0.13.0.post33 → graphharbor_runtime-0.13.0.post35}/src/langgraph_runtime_pg/graph_registry.py +0 -0
  15. {graphharbor_runtime-0.13.0.post33 → graphharbor_runtime-0.13.0.post35}/src/langgraph_runtime_pg/lifespan.py +0 -0
  16. {graphharbor_runtime-0.13.0.post33 → graphharbor_runtime-0.13.0.post35}/src/langgraph_runtime_pg/metrics.py +0 -0
  17. {graphharbor_runtime-0.13.0.post33 → graphharbor_runtime-0.13.0.post35}/src/langgraph_runtime_pg/migrate.py +0 -0
  18. {graphharbor_runtime-0.13.0.post33 → graphharbor_runtime-0.13.0.post35}/src/langgraph_runtime_pg/migrations/__init__.py +0 -0
  19. {graphharbor_runtime-0.13.0.post33 → graphharbor_runtime-0.13.0.post35}/src/langgraph_runtime_pg/migrations/env.py +0 -0
  20. {graphharbor_runtime-0.13.0.post33 → graphharbor_runtime-0.13.0.post35}/src/langgraph_runtime_pg/migrations/script.py.mako +0 -0
  21. {graphharbor_runtime-0.13.0.post33 → graphharbor_runtime-0.13.0.post35}/src/langgraph_runtime_pg/migrations/versions/001_initial_schema.py +0 -0
  22. {graphharbor_runtime-0.13.0.post33 → graphharbor_runtime-0.13.0.post35}/src/langgraph_runtime_pg/migrations/versions/002_production_runtime.py +0 -0
  23. {graphharbor_runtime-0.13.0.post33 → graphharbor_runtime-0.13.0.post35}/src/langgraph_runtime_pg/migrations/versions/003_cron_scope.py +0 -0
  24. {graphharbor_runtime-0.13.0.post33 → graphharbor_runtime-0.13.0.post35}/src/langgraph_runtime_pg/migrations/versions/004_thread_event_seq.py +0 -0
  25. {graphharbor_runtime-0.13.0.post33 → graphharbor_runtime-0.13.0.post35}/src/langgraph_runtime_pg/migrations/versions/005_run_retry_schedule.py +0 -0
  26. {graphharbor_runtime-0.13.0.post33 → graphharbor_runtime-0.13.0.post35}/src/langgraph_runtime_pg/migrations/versions/006_terminal_events.py +0 -0
  27. {graphharbor_runtime-0.13.0.post33 → graphharbor_runtime-0.13.0.post35}/src/langgraph_runtime_pg/migrations/versions/007_checkpoint_baselines.py +0 -0
  28. {graphharbor_runtime-0.13.0.post33 → graphharbor_runtime-0.13.0.post35}/src/langgraph_runtime_pg/migrations/versions/008_remove_business_scope.py +0 -0
  29. {graphharbor_runtime-0.13.0.post33 → graphharbor_runtime-0.13.0.post35}/src/langgraph_runtime_pg/migrations/versions/009_event_retention_watermarks.py +0 -0
  30. {graphharbor_runtime-0.13.0.post33 → graphharbor_runtime-0.13.0.post35}/src/langgraph_runtime_pg/migrations/versions/__init__.py +0 -0
  31. {graphharbor_runtime-0.13.0.post33 → graphharbor_runtime-0.13.0.post35}/src/langgraph_runtime_pg/models.py +0 -0
  32. {graphharbor_runtime-0.13.0.post33 → graphharbor_runtime-0.13.0.post35}/src/langgraph_runtime_pg/observability.py +0 -0
  33. {graphharbor_runtime-0.13.0.post33 → graphharbor_runtime-0.13.0.post35}/src/langgraph_runtime_pg/ops.py +0 -0
  34. {graphharbor_runtime-0.13.0.post33 → graphharbor_runtime-0.13.0.post35}/src/langgraph_runtime_pg/production.py +0 -0
  35. {graphharbor_runtime-0.13.0.post33 → graphharbor_runtime-0.13.0.post35}/src/langgraph_runtime_pg/production_worker.py +0 -0
  36. {graphharbor_runtime-0.13.0.post33 → graphharbor_runtime-0.13.0.post35}/src/langgraph_runtime_pg/protocol.py +0 -0
  37. {graphharbor_runtime-0.13.0.post33 → graphharbor_runtime-0.13.0.post35}/src/langgraph_runtime_pg/queue.py +0 -0
  38. {graphharbor_runtime-0.13.0.post33 → graphharbor_runtime-0.13.0.post35}/src/langgraph_runtime_pg/redis_stream.py +0 -0
  39. {graphharbor_runtime-0.13.0.post33 → graphharbor_runtime-0.13.0.post35}/src/langgraph_runtime_pg/retry.py +0 -0
  40. {graphharbor_runtime-0.13.0.post33 → graphharbor_runtime-0.13.0.post35}/src/langgraph_runtime_pg/routes.py +0 -0
  41. {graphharbor_runtime-0.13.0.post33 → graphharbor_runtime-0.13.0.post35}/src/langgraph_runtime_pg/run_state.py +0 -0
  42. {graphharbor_runtime-0.13.0.post33 → graphharbor_runtime-0.13.0.post35}/src/langgraph_runtime_pg/run_store.py +0 -0
  43. {graphharbor_runtime-0.13.0.post33 → graphharbor_runtime-0.13.0.post35}/src/langgraph_runtime_pg/schema_setup.py +0 -0
  44. {graphharbor_runtime-0.13.0.post33 → graphharbor_runtime-0.13.0.post35}/src/langgraph_runtime_pg/store.py +0 -0
  45. {graphharbor_runtime-0.13.0.post33 → graphharbor_runtime-0.13.0.post35}/src/langgraph_runtime_pg/thread_config.py +0 -0
  46. {graphharbor_runtime-0.13.0.post33 → graphharbor_runtime-0.13.0.post35}/tests/conftest.py +0 -0
  47. {graphharbor_runtime-0.13.0.post33 → graphharbor_runtime-0.13.0.post35}/tests/test_business_boundary.py +0 -0
  48. {graphharbor_runtime-0.13.0.post33 → graphharbor_runtime-0.13.0.post35}/tests/test_checkpoint_mutation_safety.py +0 -0
  49. {graphharbor_runtime-0.13.0.post33 → graphharbor_runtime-0.13.0.post35}/tests/test_e2e.py +0 -0
  50. {graphharbor_runtime-0.13.0.post33 → graphharbor_runtime-0.13.0.post35}/tests/test_interface.py +0 -0
  51. {graphharbor_runtime-0.13.0.post33 → graphharbor_runtime-0.13.0.post35}/tests/test_observability.py +0 -0
  52. {graphharbor_runtime-0.13.0.post33 → graphharbor_runtime-0.13.0.post35}/tests/test_official_sdk_contract.py +0 -0
  53. {graphharbor_runtime-0.13.0.post33 → graphharbor_runtime-0.13.0.post35}/tests/test_ops_eager_iterators.py +0 -0
  54. {graphharbor_runtime-0.13.0.post33 → graphharbor_runtime-0.13.0.post35}/tests/test_persistence_contract.py +0 -0
  55. {graphharbor_runtime-0.13.0.post33 → graphharbor_runtime-0.13.0.post35}/tests/test_production_contract.py +0 -0
  56. {graphharbor_runtime-0.13.0.post33 → graphharbor_runtime-0.13.0.post35}/tests/test_public_runtime.py +0 -0
  57. {graphharbor_runtime-0.13.0.post33 → graphharbor_runtime-0.13.0.post35}/tests/test_queue.py +0 -0
  58. {graphharbor_runtime-0.13.0.post33 → graphharbor_runtime-0.13.0.post35}/tests/test_rest_contract.py +0 -0
  59. {graphharbor_runtime-0.13.0.post33 → graphharbor_runtime-0.13.0.post35}/tests/test_run_submission_concurrency.py +0 -0
  60. {graphharbor_runtime-0.13.0.post33 → graphharbor_runtime-0.13.0.post35}/tests/test_runtime_service_p0_e2e.py +0 -0
  61. {graphharbor_runtime-0.13.0.post33 → graphharbor_runtime-0.13.0.post35}/tests/test_schema_setup.py +0 -0
  62. {graphharbor_runtime-0.13.0.post33 → graphharbor_runtime-0.13.0.post35}/tests/test_vue_protocol_events.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: graphharbor-runtime
3
- Version: 0.13.0.post33
3
+ Version: 0.13.0.post35
4
4
  Summary: GraphHarbor PostgreSQL and Redis runtime for the LangGraph Agent Server
5
5
  Project-URL: Homepage, https://github.com/ljxpython/graphharbor
6
6
  Project-URL: Repository, https://github.com/ljxpython/graphharbor
@@ -1,7 +1,7 @@
1
1
  [project]
2
2
  name = "graphharbor-runtime"
3
3
  # GraphHarbor has its own release line; langgraph-api is compatibility-spike-only.
4
- version = "0.13.0.post33"
4
+ version = "0.13.0.post35"
5
5
  description = "GraphHarbor PostgreSQL and Redis runtime for the LangGraph Agent Server"
6
6
  readme = "README.md"
7
7
  license = "MIT"
@@ -2,6 +2,7 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ import inspect
5
6
  from collections.abc import Mapping
6
7
  from typing import Any, cast
7
8
 
@@ -59,7 +60,8 @@ async def authorize(
59
60
  action=cast(Any, action),
60
61
  )
61
62
  try:
62
- result = await handler(ctx=ctx, value=value)
63
+ raw_result = handler(ctx=ctx, value=value)
64
+ result: Any = await raw_result if inspect.isawaitable(raw_result) else raw_result
63
65
  except Auth.exceptions.HTTPException as exc:
64
66
  raise HTTPException(exc.status_code, exc.detail, headers=exc.headers) from exc
65
67
  except AssertionError as exc:
@@ -167,6 +167,85 @@ async def test_redis_heartbeat_outage_does_not_cancel_postgres_owned_run(monkeyp
167
167
  await asyncio.gather(task, return_exceptions=True)
168
168
 
169
169
 
170
+ @pytest.mark.asyncio
171
+ async def test_pruning_waits_for_inflight_thread_replay() -> None:
172
+ assert "graphharbor_event_retention_verify" in os.environ.get("DATABASE_URI", "")
173
+
174
+ from langgraph_runtime_pg.database import connect, start_pool, stop_pool
175
+ from langgraph_runtime_pg.models import AssistantRow, RunRow, RuntimeEventRow, ThreadRow
176
+ from langgraph_runtime_pg.run_store import RunRepository
177
+
178
+ assistant_id, thread_id, run_id = uuid4(), uuid4(), uuid4()
179
+ old = datetime.now(UTC) - timedelta(days=2)
180
+ await start_pool()
181
+ try:
182
+ async with connect() as conn:
183
+ conn.session.add(AssistantRow(assistant_id=assistant_id, graph_id="retention-test"))
184
+ conn.session.add(ThreadRow(thread_id=thread_id, metadata_={}, config={}, interrupts={}))
185
+ conn.session.add(
186
+ RunRow(
187
+ run_id=run_id,
188
+ thread_id=thread_id,
189
+ assistant_id=assistant_id,
190
+ status="success",
191
+ updated_at=old,
192
+ kwargs={},
193
+ metadata_={},
194
+ )
195
+ )
196
+ await conn.session.flush()
197
+ conn.session.add(
198
+ RuntimeEventRow(
199
+ run_id=run_id,
200
+ thread_id=thread_id,
201
+ sequence=1,
202
+ topic="messages",
203
+ payload={"event": "messages"},
204
+ created_at=old,
205
+ )
206
+ )
207
+
208
+ async with connect() as reader:
209
+ thread = await reader.session.scalar(
210
+ select(ThreadRow)
211
+ .where(ThreadRow.thread_id == thread_id)
212
+ .with_for_update(read=True)
213
+ )
214
+ assert thread is not None and thread.event_pruned_through == 0
215
+
216
+ async def prune() -> int:
217
+ async with connect() as writer:
218
+ return await RunRepository().prune_expired_events(
219
+ writer.session, retention_seconds=86400, now=datetime.now(UTC)
220
+ )
221
+
222
+ task = asyncio.create_task(prune())
223
+ try:
224
+ with pytest.raises(TimeoutError):
225
+ await asyncio.wait_for(asyncio.shield(task), timeout=0.2)
226
+ rows = (
227
+ await reader.session.scalars(
228
+ select(RuntimeEventRow).where(RuntimeEventRow.thread_id == thread_id)
229
+ )
230
+ ).all()
231
+ assert [row.sequence for row in rows] == [1]
232
+ finally:
233
+ if task.done():
234
+ await task
235
+ assert await asyncio.wait_for(task, timeout=5) == 1
236
+ async with connect() as conn:
237
+ thread = await conn.session.get(ThreadRow, thread_id)
238
+ rows = (
239
+ await conn.session.scalars(
240
+ select(RuntimeEventRow).where(RuntimeEventRow.thread_id == thread_id)
241
+ )
242
+ ).all()
243
+ assert thread is not None and thread.event_pruned_through == 1
244
+ assert rows == []
245
+ finally:
246
+ await stop_pool()
247
+
248
+
170
249
  @pytest.mark.asyncio
171
250
  @pytest.mark.parametrize("terminal_status", ["success", "error"])
172
251
  async def test_expired_event_pruning_is_bounded_and_preserves_active_state(