langgraph-runtime-inmem 0.34.0.dev4__tar.gz → 0.34.0.dev5__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.
- {langgraph_runtime_inmem-0.34.0.dev4 → langgraph_runtime_inmem-0.34.0.dev5}/PKG-INFO +1 -1
- {langgraph_runtime_inmem-0.34.0.dev4 → langgraph_runtime_inmem-0.34.0.dev5}/langgraph_runtime_inmem/__init__.py +1 -1
- {langgraph_runtime_inmem-0.34.0.dev4 → langgraph_runtime_inmem-0.34.0.dev5}/langgraph_runtime_inmem/checkpoint.py +10 -4
- {langgraph_runtime_inmem-0.34.0.dev4 → langgraph_runtime_inmem-0.34.0.dev5}/langgraph_runtime_inmem/ops.py +111 -6
- {langgraph_runtime_inmem-0.34.0.dev4 → langgraph_runtime_inmem-0.34.0.dev5}/pyproject.toml +4 -0
- {langgraph_runtime_inmem-0.34.0.dev4 → langgraph_runtime_inmem-0.34.0.dev5}/.gitignore +0 -0
- {langgraph_runtime_inmem-0.34.0.dev4 → langgraph_runtime_inmem-0.34.0.dev5}/Makefile +0 -0
- {langgraph_runtime_inmem-0.34.0.dev4 → langgraph_runtime_inmem-0.34.0.dev5}/README.md +0 -0
- {langgraph_runtime_inmem-0.34.0.dev4 → langgraph_runtime_inmem-0.34.0.dev5}/langgraph_runtime_inmem/_persistence.py +0 -0
- {langgraph_runtime_inmem-0.34.0.dev4 → langgraph_runtime_inmem-0.34.0.dev5}/langgraph_runtime_inmem/database.py +0 -0
- {langgraph_runtime_inmem-0.34.0.dev4 → langgraph_runtime_inmem-0.34.0.dev5}/langgraph_runtime_inmem/inmem_stream.py +0 -0
- {langgraph_runtime_inmem-0.34.0.dev4 → langgraph_runtime_inmem-0.34.0.dev5}/langgraph_runtime_inmem/lifespan.py +0 -0
- {langgraph_runtime_inmem-0.34.0.dev4 → langgraph_runtime_inmem-0.34.0.dev5}/langgraph_runtime_inmem/metrics.py +0 -0
- {langgraph_runtime_inmem-0.34.0.dev4 → langgraph_runtime_inmem-0.34.0.dev5}/langgraph_runtime_inmem/queue.py +0 -0
- {langgraph_runtime_inmem-0.34.0.dev4 → langgraph_runtime_inmem-0.34.0.dev5}/langgraph_runtime_inmem/retry.py +0 -0
- {langgraph_runtime_inmem-0.34.0.dev4 → langgraph_runtime_inmem-0.34.0.dev5}/langgraph_runtime_inmem/routes.py +0 -0
- {langgraph_runtime_inmem-0.34.0.dev4 → langgraph_runtime_inmem-0.34.0.dev5}/langgraph_runtime_inmem/store.py +0 -0
- {langgraph_runtime_inmem-0.34.0.dev4 → langgraph_runtime_inmem-0.34.0.dev5}/uv.lock +0 -0
|
@@ -157,7 +157,9 @@ class InMemorySaver(InMemorySaverBase):
|
|
|
157
157
|
if os.path.exists(file_path):
|
|
158
158
|
os.remove(file_path)
|
|
159
159
|
|
|
160
|
-
async def _decrypt_json(
|
|
160
|
+
async def _decrypt_json(
|
|
161
|
+
self, data: dict[str, Any], model: str, field: str
|
|
162
|
+
) -> dict[str, Any]:
|
|
161
163
|
"""Decrypt a dict if custom encryption is configured."""
|
|
162
164
|
from langgraph_api import config as api_config # noqa: PLC0415
|
|
163
165
|
|
|
@@ -168,7 +170,7 @@ class InMemorySaver(InMemorySaverBase):
|
|
|
168
170
|
decrypt_json_if_needed,
|
|
169
171
|
)
|
|
170
172
|
|
|
171
|
-
result = await decrypt_json_if_needed(data, get_encryption(),
|
|
173
|
+
result = await decrypt_json_if_needed(data, get_encryption(), model, field)
|
|
172
174
|
if result is None:
|
|
173
175
|
raise ValueError("decrypt_json_if_needed returned None for non-None input")
|
|
174
176
|
return result
|
|
@@ -180,7 +182,9 @@ class InMemorySaver(InMemorySaverBase):
|
|
|
180
182
|
return None
|
|
181
183
|
|
|
182
184
|
# Decrypt metadata if encryption is enabled
|
|
183
|
-
decrypted_metadata = await self._decrypt_json(
|
|
185
|
+
decrypted_metadata = await self._decrypt_json(
|
|
186
|
+
tuple_.metadata, "run", "metadata"
|
|
187
|
+
)
|
|
184
188
|
|
|
185
189
|
from langgraph.checkpoint.base import ( # noqa: PLC0415
|
|
186
190
|
CheckpointTuple as CPTuple,
|
|
@@ -209,7 +213,9 @@ class InMemorySaver(InMemorySaverBase):
|
|
|
209
213
|
|
|
210
214
|
for tuple_ in self.list(config, filter=filter, before=before, limit=limit):
|
|
211
215
|
# Decrypt metadata if encryption is enabled
|
|
212
|
-
decrypted_metadata = await self._decrypt_json(
|
|
216
|
+
decrypted_metadata = await self._decrypt_json(
|
|
217
|
+
tuple_.metadata, "run", "metadata"
|
|
218
|
+
)
|
|
213
219
|
|
|
214
220
|
yield CPTuple(
|
|
215
221
|
config=tuple_.config,
|
|
@@ -75,6 +75,26 @@ LANGGRAPH_PY_MINOR = tuple(map(int, __version__.split(".")[:2]))
|
|
|
75
75
|
USE_NEW_INTERRUPTS = LANGGRAPH_PY_MINOR >= (0, 6)
|
|
76
76
|
|
|
77
77
|
|
|
78
|
+
async def _decrypt_response(data: dict, model: str, fields: list[str]) -> dict:
|
|
79
|
+
from langgraph_api.encryption.middleware import decrypt_response # noqa: PLC0415
|
|
80
|
+
|
|
81
|
+
return await decrypt_response(data, model, fields) # type: ignore[arg-type]
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
async def _encrypt_request(data: dict, model: str, fields: list[str]) -> dict:
|
|
85
|
+
from langgraph_api.encryption.middleware import encrypt_request # noqa: PLC0415
|
|
86
|
+
|
|
87
|
+
return await encrypt_request(data, model, fields) # type: ignore[arg-type]
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _using_custom_encryption() -> bool:
|
|
91
|
+
from langgraph_api.encryption.shared import ( # noqa: PLC0415
|
|
92
|
+
using_custom_encryption,
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
return using_custom_encryption()
|
|
96
|
+
|
|
97
|
+
|
|
78
98
|
def _run_stream_mode_matches(event_mode: str, stream_mode: list[str] | None) -> bool:
|
|
79
99
|
"""Return True if a published run-stream event matches the join filter."""
|
|
80
100
|
if not stream_mode:
|
|
@@ -279,6 +299,18 @@ class Assistants(Authenticated):
|
|
|
279
299
|
|
|
280
300
|
assistant_id = _ensure_uuid(assistant_id)
|
|
281
301
|
metadata = metadata if metadata is not None else {}
|
|
302
|
+
custom_encryption = _using_custom_encryption()
|
|
303
|
+
if custom_encryption:
|
|
304
|
+
decrypted = await _decrypt_response(
|
|
305
|
+
{"config": config, "context": context, "metadata": metadata},
|
|
306
|
+
"assistant",
|
|
307
|
+
["config", "context", "metadata"],
|
|
308
|
+
)
|
|
309
|
+
config, context, metadata = (
|
|
310
|
+
decrypted["config"],
|
|
311
|
+
decrypted["context"],
|
|
312
|
+
decrypted["metadata"],
|
|
313
|
+
)
|
|
282
314
|
filters = await Assistants.handle_event(
|
|
283
315
|
ctx,
|
|
284
316
|
"create",
|
|
@@ -311,9 +343,14 @@ class Assistants(Authenticated):
|
|
|
311
343
|
None,
|
|
312
344
|
)
|
|
313
345
|
if existing_assistant:
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
346
|
+
existing_metadata = existing_assistant["metadata"]
|
|
347
|
+
if custom_encryption:
|
|
348
|
+
existing_metadata = (
|
|
349
|
+
await _decrypt_response(
|
|
350
|
+
{"metadata": existing_metadata}, "assistant", ["metadata"]
|
|
351
|
+
)
|
|
352
|
+
)["metadata"]
|
|
353
|
+
if filters and not _check_filter_match(existing_metadata, filters):
|
|
317
354
|
raise HTTPException(
|
|
318
355
|
status_code=409, detail=f"Assistant {assistant_id} already exists"
|
|
319
356
|
)
|
|
@@ -352,6 +389,13 @@ class Assistants(Authenticated):
|
|
|
352
389
|
"name": name,
|
|
353
390
|
"description": description,
|
|
354
391
|
}
|
|
392
|
+
if custom_encryption and not system:
|
|
393
|
+
new_assistant = await _encrypt_request(
|
|
394
|
+
new_assistant, "assistant", ["config", "context", "metadata"]
|
|
395
|
+
)
|
|
396
|
+
new_version = await _encrypt_request(
|
|
397
|
+
new_version, "assistant", ["config", "context", "metadata"]
|
|
398
|
+
)
|
|
355
399
|
conn.store["assistants"].append(new_assistant)
|
|
356
400
|
conn.store["assistant_versions"].append(new_version)
|
|
357
401
|
|
|
@@ -394,6 +438,18 @@ class Assistants(Authenticated):
|
|
|
394
438
|
assistant_id = _ensure_uuid(assistant_id)
|
|
395
439
|
metadata = metadata if metadata is not None else {}
|
|
396
440
|
config = config if config is not None else {}
|
|
441
|
+
custom_encryption = _using_custom_encryption()
|
|
442
|
+
if custom_encryption:
|
|
443
|
+
decrypted = await _decrypt_response(
|
|
444
|
+
{"config": config, "context": context, "metadata": metadata},
|
|
445
|
+
"assistant",
|
|
446
|
+
["config", "context", "metadata"],
|
|
447
|
+
)
|
|
448
|
+
config, context, metadata = (
|
|
449
|
+
decrypted["config"],
|
|
450
|
+
decrypted["context"],
|
|
451
|
+
decrypted["metadata"],
|
|
452
|
+
)
|
|
397
453
|
filters = await Assistants.handle_event(
|
|
398
454
|
ctx,
|
|
399
455
|
"update",
|
|
@@ -430,7 +486,12 @@ class Assistants(Authenticated):
|
|
|
430
486
|
raise HTTPException(
|
|
431
487
|
status_code=404, detail=f"Assistant {assistant_id} not found"
|
|
432
488
|
)
|
|
433
|
-
|
|
489
|
+
stored_assistant = assistant
|
|
490
|
+
if custom_encryption:
|
|
491
|
+
assistant = await _decrypt_response(
|
|
492
|
+
assistant, "assistant", ["config", "context", "metadata"]
|
|
493
|
+
)
|
|
494
|
+
if filters and not _check_filter_match(assistant["metadata"], filters):
|
|
434
495
|
raise HTTPException(
|
|
435
496
|
status_code=404, detail=f"Assistant {assistant_id} not found"
|
|
436
497
|
)
|
|
@@ -464,10 +525,16 @@ class Assistants(Authenticated):
|
|
|
464
525
|
description if description is not None else assistant.get("description")
|
|
465
526
|
),
|
|
466
527
|
}
|
|
528
|
+
if custom_encryption:
|
|
529
|
+
new_version_entry = await _encrypt_request(
|
|
530
|
+
new_version_entry,
|
|
531
|
+
"assistant",
|
|
532
|
+
["config", "context", "metadata"],
|
|
533
|
+
)
|
|
467
534
|
conn.store["assistant_versions"].append(new_version_entry)
|
|
468
535
|
|
|
469
536
|
# Update assistants table
|
|
470
|
-
|
|
537
|
+
stored_assistant.update(
|
|
471
538
|
{
|
|
472
539
|
"graph_id": new_version_entry["graph_id"],
|
|
473
540
|
"config": new_version_entry["config"],
|
|
@@ -485,7 +552,7 @@ class Assistants(Authenticated):
|
|
|
485
552
|
)
|
|
486
553
|
|
|
487
554
|
async def _yield_updated():
|
|
488
|
-
yield
|
|
555
|
+
yield stored_assistant
|
|
489
556
|
|
|
490
557
|
return _yield_updated()
|
|
491
558
|
|
|
@@ -2478,6 +2545,18 @@ class Runs(Authenticated):
|
|
|
2478
2545
|
if not assistant:
|
|
2479
2546
|
return _empty_generator()
|
|
2480
2547
|
|
|
2548
|
+
custom_encryption = _using_custom_encryption()
|
|
2549
|
+
if custom_encryption:
|
|
2550
|
+
assistant = await _decrypt_response(
|
|
2551
|
+
assistant, "assistant", ["metadata", "config", "context"]
|
|
2552
|
+
)
|
|
2553
|
+
kwargs = await _decrypt_response(
|
|
2554
|
+
kwargs, "run", ["input", "command", "config", "context"]
|
|
2555
|
+
)
|
|
2556
|
+
metadata = (
|
|
2557
|
+
await _decrypt_response({"metadata": metadata}, "run", ["metadata"])
|
|
2558
|
+
)["metadata"]
|
|
2559
|
+
|
|
2481
2560
|
thread_id = _ensure_uuid(thread_id) if thread_id else None
|
|
2482
2561
|
run_id = _ensure_uuid(run_id) if run_id else None
|
|
2483
2562
|
metadata = metadata if metadata is not None else {}
|
|
@@ -2511,6 +2590,12 @@ class Runs(Authenticated):
|
|
|
2511
2590
|
existing_thread = next(
|
|
2512
2591
|
(t for t in conn.store["threads"] if t["thread_id"] == thread_id), None
|
|
2513
2592
|
)
|
|
2593
|
+
if custom_encryption and existing_thread:
|
|
2594
|
+
existing_thread = await _decrypt_response(
|
|
2595
|
+
existing_thread,
|
|
2596
|
+
"thread",
|
|
2597
|
+
["metadata", "config"],
|
|
2598
|
+
)
|
|
2514
2599
|
# Automatically enforce assistant ownership for non-system assistants
|
|
2515
2600
|
# by calling the user's assistant read auth handler.
|
|
2516
2601
|
if assistant.get("metadata", {}).get("created_by") != "system":
|
|
@@ -2662,6 +2747,26 @@ class Runs(Authenticated):
|
|
|
2662
2747
|
updated_at=datetime.now(UTC),
|
|
2663
2748
|
langsmith_session_name=langsmith_session_name,
|
|
2664
2749
|
)
|
|
2750
|
+
if custom_encryption:
|
|
2751
|
+
# Unlike Go core, in-memory workers read nested run kwargs and thread
|
|
2752
|
+
# config directly from the store, so those containers must stay readable.
|
|
2753
|
+
encrypted_thread = await _encrypt_request(
|
|
2754
|
+
thread if not existing_thread else existing_thread,
|
|
2755
|
+
"thread",
|
|
2756
|
+
["metadata"],
|
|
2757
|
+
)
|
|
2758
|
+
stored_thread = next(
|
|
2759
|
+
t
|
|
2760
|
+
for t in conn.store["threads"]
|
|
2761
|
+
if t["thread_id"] == encrypted_thread["thread_id"]
|
|
2762
|
+
)
|
|
2763
|
+
stored_thread.update(encrypted_thread)
|
|
2764
|
+
new_run["kwargs"] = await _encrypt_request(
|
|
2765
|
+
new_run["kwargs"],
|
|
2766
|
+
"run",
|
|
2767
|
+
["input", "config", "context", "command"],
|
|
2768
|
+
)
|
|
2769
|
+
new_run = Run(**await _encrypt_request(new_run, "run", ["metadata"]))
|
|
2665
2770
|
conn.store["runs"].append(new_run)
|
|
2666
2771
|
|
|
2667
2772
|
async def _yield_new():
|
|
@@ -36,7 +36,11 @@ dev = [
|
|
|
36
36
|
[tool.hatch.version]
|
|
37
37
|
path = "langgraph_runtime_inmem/__init__.py"
|
|
38
38
|
|
|
39
|
+
[tool.hatch.build.targets.wheel]
|
|
40
|
+
core-metadata-version = "2.4"
|
|
41
|
+
|
|
39
42
|
[tool.hatch.build.targets.sdist]
|
|
43
|
+
core-metadata-version = "2.4"
|
|
40
44
|
exclude = [
|
|
41
45
|
"tests/",
|
|
42
46
|
"docs/",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|