langgraph-api 0.14.0.dev4__py3-none-any.whl → 0.14.0.dev6__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.
- langgraph_api/__init__.py +1 -1
- langgraph_api/_checkpointer/_adapter.py +13 -0
- langgraph_api/api/__init__.py +5 -0
- langgraph_api/api/a2a.py +7 -0
- langgraph_api/beacon_data_plane_jwt_auth.py +71 -0
- langgraph_api/config/__init__.py +24 -0
- langgraph_api/grpc/servicers/encryption.py +67 -14
- langgraph_api/queue_entrypoint.py +2 -3
- langgraph_api/self_hosted_logs.py +52 -23
- langgraph_api/server.py +4 -2
- {langgraph_api-0.14.0.dev4.dist-info → langgraph_api-0.14.0.dev6.dist-info}/METADATA +1 -1
- {langgraph_api-0.14.0.dev4.dist-info → langgraph_api-0.14.0.dev6.dist-info}/RECORD +22 -21
- langgraph_grpc_common/checkpointer.py +21 -0
- langgraph_grpc_common/proto/checkpointer_pb2.py +46 -38
- langgraph_grpc_common/proto/checkpointer_pb2.pyi +56 -2
- langgraph_grpc_common/proto/encryption_pb2.py +11 -7
- langgraph_grpc_common/proto/encryption_pb2.pyi +61 -1
- langgraph_grpc_common/proto/encryption_pb2_grpc.py +88 -0
- langgraph_grpc_common/proto/encryption_pb2_grpc.pyi +24 -0
- {langgraph_api-0.14.0.dev4.dist-info → langgraph_api-0.14.0.dev6.dist-info}/WHEEL +0 -0
- {langgraph_api-0.14.0.dev4.dist-info → langgraph_api-0.14.0.dev6.dist-info}/entry_points.txt +0 -0
- {langgraph_api-0.14.0.dev4.dist-info → langgraph_api-0.14.0.dev6.dist-info}/licenses/LICENSE +0 -0
langgraph_api/__init__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.14.0.
|
|
1
|
+
__version__ = "0.14.0.dev6"
|
|
@@ -262,6 +262,17 @@ class _CustomCheckpointerAdapter(BaseCheckpointSaver):
|
|
|
262
262
|
return f"{next_v:032}.{next_h:.16f}"
|
|
263
263
|
|
|
264
264
|
|
|
265
|
+
def _encryption_context_provider() -> Callable[[], dict[str, Any]] | None:
|
|
266
|
+
"""Supply request-scoped encryption context to the gRPC checkpointer."""
|
|
267
|
+
if not config.LANGGRAPH_ENCRYPTION:
|
|
268
|
+
return None
|
|
269
|
+
from langgraph_api.encryption.context import ( # noqa: PLC0415
|
|
270
|
+
get_encryption_context,
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
return get_encryption_context
|
|
274
|
+
|
|
275
|
+
|
|
265
276
|
async def get_checkpointer(
|
|
266
277
|
*,
|
|
267
278
|
conn: Any | None = None,
|
|
@@ -331,6 +342,7 @@ async def get_checkpointer(
|
|
|
331
342
|
GrpcCheckpointer(
|
|
332
343
|
get_stub=_get_shared_checkpointer_stub,
|
|
333
344
|
serializer=_ApiSerializer(),
|
|
345
|
+
encryption_context_provider=_encryption_context_provider(),
|
|
334
346
|
),
|
|
335
347
|
)
|
|
336
348
|
|
|
@@ -345,6 +357,7 @@ async def get_checkpointer(
|
|
|
345
357
|
GrpcCheckpointer(
|
|
346
358
|
get_stub=_get_shared_checkpointer_stub,
|
|
347
359
|
serializer=_ApiSerializer(),
|
|
360
|
+
encryption_context_provider=_encryption_context_provider(),
|
|
348
361
|
),
|
|
349
362
|
)
|
|
350
363
|
|
langgraph_api/api/__init__.py
CHANGED
|
@@ -236,6 +236,11 @@ if HTTP_CONFIG:
|
|
|
236
236
|
user_router.router.lifespan_context,
|
|
237
237
|
)
|
|
238
238
|
|
|
239
|
+
# Keep the reference to user router's lifespan in case it gets combined with other lifespans.
|
|
240
|
+
custom_app_lifespan = (
|
|
241
|
+
None if user_router is None else user_router.router.lifespan_context
|
|
242
|
+
)
|
|
243
|
+
|
|
239
244
|
|
|
240
245
|
if "__inmem" in MIGRATIONS_PATH:
|
|
241
246
|
from langgraph_runtime_inmem.routes import get_internal_routes
|
langgraph_api/api/a2a.py
CHANGED
|
@@ -27,6 +27,7 @@ from starlette.responses import JSONResponse, Response
|
|
|
27
27
|
from typing_extensions import TypedDict
|
|
28
28
|
|
|
29
29
|
from langgraph_api import __version__
|
|
30
|
+
from langgraph_api.config import A2A_ALLOWED_TOOL_CALL_RESULTS
|
|
30
31
|
from langgraph_api.metadata import USER_API_URL
|
|
31
32
|
from langgraph_api.route import ApiRequest, ApiRoute
|
|
32
33
|
from langgraph_api.schema import RunCommand
|
|
@@ -893,6 +894,12 @@ def _tool_result_data(message: dict[str, Any]) -> dict[str, Any] | None:
|
|
|
893
894
|
if not isinstance(tool_call_id, str) or not tool_call_id:
|
|
894
895
|
return None
|
|
895
896
|
|
|
897
|
+
if (
|
|
898
|
+
A2A_ALLOWED_TOOL_CALL_RESULTS is not None
|
|
899
|
+
and message.get("name") not in A2A_ALLOWED_TOOL_CALL_RESULTS
|
|
900
|
+
):
|
|
901
|
+
return None
|
|
902
|
+
|
|
896
903
|
result: dict[str, Any] = {"toolCallId": tool_call_id}
|
|
897
904
|
content = message.get("content")
|
|
898
905
|
if content not in (None, ""):
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"""Data-plane JWT authentication for Beacon OTLP exports."""
|
|
2
|
+
|
|
3
|
+
import time
|
|
4
|
+
from datetime import UTC, datetime, timedelta
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
import jwt
|
|
8
|
+
from requests.sessions import Session
|
|
9
|
+
|
|
10
|
+
DATA_PLANE_AUTH_HEADER = "X-Data-Plane-Auth"
|
|
11
|
+
BEACON_OTEL_COLLECTOR_PURPOSE = "beacon-otel-collector"
|
|
12
|
+
|
|
13
|
+
_BEACON_OTEL_JWT_TTL_SECONDS = 60 * 60
|
|
14
|
+
_BEACON_OTEL_JWT_REFRESH_BEFORE_SECONDS = 60
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def sign_data_plane_jwt(data_plane_id: str, organization_id: str, secret: str) -> str:
|
|
18
|
+
now = datetime.now(UTC)
|
|
19
|
+
return jwt.encode(
|
|
20
|
+
{
|
|
21
|
+
"data_plane_id": data_plane_id,
|
|
22
|
+
"organization_id": organization_id,
|
|
23
|
+
"purpose": BEACON_OTEL_COLLECTOR_PURPOSE,
|
|
24
|
+
"iat": now,
|
|
25
|
+
"exp": now + timedelta(seconds=_BEACON_OTEL_JWT_TTL_SECONDS),
|
|
26
|
+
},
|
|
27
|
+
secret,
|
|
28
|
+
algorithm="HS256",
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class _BeaconOtelJWTCache:
|
|
33
|
+
def __init__(self, data_plane_id: str, organization_id: str, secret: str) -> None:
|
|
34
|
+
self._data_plane_id = data_plane_id
|
|
35
|
+
self._organization_id = organization_id
|
|
36
|
+
self._secret = secret
|
|
37
|
+
self._cached_token: tuple[str, float] | None = None
|
|
38
|
+
|
|
39
|
+
def get(self) -> str:
|
|
40
|
+
cached_token = self._cached_token
|
|
41
|
+
if cached_token is not None:
|
|
42
|
+
token, refresh_at = cached_token
|
|
43
|
+
if time.monotonic() < refresh_at:
|
|
44
|
+
return token
|
|
45
|
+
|
|
46
|
+
token = sign_data_plane_jwt(
|
|
47
|
+
self._data_plane_id, self._organization_id, self._secret
|
|
48
|
+
)
|
|
49
|
+
refresh_at = time.monotonic() + (
|
|
50
|
+
_BEACON_OTEL_JWT_TTL_SECONDS - _BEACON_OTEL_JWT_REFRESH_BEFORE_SECONDS
|
|
51
|
+
)
|
|
52
|
+
self._cached_token = (token, refresh_at)
|
|
53
|
+
return token
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class _DataPlaneAuthSession(Session):
|
|
57
|
+
def __init__(self, cache: _BeaconOtelJWTCache) -> None:
|
|
58
|
+
super().__init__()
|
|
59
|
+
self._cache = cache
|
|
60
|
+
|
|
61
|
+
def request(self, method: str, url: str, **kwargs: Any):
|
|
62
|
+
headers = dict(kwargs.pop("headers", None) or {})
|
|
63
|
+
headers[DATA_PLANE_AUTH_HEADER] = self._cache.get()
|
|
64
|
+
return super().request(method, url, headers=headers, **kwargs)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def beacon_otel_session(
|
|
68
|
+
data_plane_id: str, organization_id: str, secret: str
|
|
69
|
+
) -> Session:
|
|
70
|
+
cache = _BeaconOtelJWTCache(data_plane_id, organization_id, secret)
|
|
71
|
+
return _DataPlaneAuthSession(cache)
|
langgraph_api/config/__init__.py
CHANGED
|
@@ -251,6 +251,17 @@ JSON_THREAD_POOL_MINIMUM_SIZE_BYTES = 100 * 1024 # 100 KB
|
|
|
251
251
|
HTTP_CONFIG = env("LANGGRAPH_HTTP", cast=_parse.parse_schema(HttpConfig), default=None)
|
|
252
252
|
MCP_ENABLED = HTTP_CONFIG is None or not HTTP_CONFIG.get("disable_mcp")
|
|
253
253
|
A2A_ENABLED = HTTP_CONFIG is None or not HTTP_CONFIG.get("disable_a2a")
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def _parse_allowed_tool_call_results(value: str) -> frozenset[str]:
|
|
257
|
+
return frozenset(name.strip() for name in value.split(",") if name.strip())
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
A2A_ALLOWED_TOOL_CALL_RESULTS = env(
|
|
261
|
+
"A2A_ALLOWED_TOOL_CALL_RESULTS",
|
|
262
|
+
cast=_parse_allowed_tool_call_results,
|
|
263
|
+
default=None,
|
|
264
|
+
)
|
|
254
265
|
WEBHOOKS_ENABLED = HTTP_CONFIG and HTTP_CONFIG.get("disable_webhooks")
|
|
255
266
|
# Not in public docs: populated by langgraph.json config, not set as env var directly
|
|
256
267
|
STORE_CONFIG = env(
|
|
@@ -603,6 +614,14 @@ DATADOG_METRICS_ENABLED = bool(LSD_DD_API_KEY)
|
|
|
603
614
|
EXPOSE_INTERNAL_METRICS_PROMETHEUS = env(
|
|
604
615
|
"EXPOSE_INTERNAL_METRICS_PROMETHEUS", cast=bool, default=False
|
|
605
616
|
)
|
|
617
|
+
|
|
618
|
+
# These are only set for internal agents (polly/insights/engine/etc..) deployed in byoc so we can fetch logs (see api/langgraph_api/self_hosted_logs.py)
|
|
619
|
+
DATA_PLANE_ID: str | None = env("DATA_PLANE_ID", cast=str, default=None)
|
|
620
|
+
DATA_PLANE_ORG_ID: str | None = env("DATA_PLANE_ORG_ID", cast=str, default=None)
|
|
621
|
+
DATA_PLANE_JWT_SECRET: str | None = env("DATA_PLANE_JWT_SECRET", cast=str, default=None)
|
|
622
|
+
BYOC_HOST = env("BYOC_HOST", cast=str, default="byoc")
|
|
623
|
+
BYOC_ENV = env("BYOC_ENV", cast=str, default="byoc")
|
|
624
|
+
|
|
606
625
|
LANGGRAPH_LOGS_ENDPOINT = env("LANGGRAPH_LOGS_ENDPOINT", cast=str, default=None)
|
|
607
626
|
LANGGRAPH_LOGS_ENABLED = env("LANGGRAPH_LOGS_ENABLED", cast=bool, default=False)
|
|
608
627
|
|
|
@@ -649,6 +668,8 @@ __all__ = [
|
|
|
649
668
|
"BG_JOB_MAX_RETRIES",
|
|
650
669
|
"BG_JOB_SHUTDOWN_GRACE_PERIOD_SECS",
|
|
651
670
|
"BG_JOB_TIMEOUT_SECS",
|
|
671
|
+
"BYOC_ENV",
|
|
672
|
+
"BYOC_HOST",
|
|
652
673
|
"CHECKPOINTER_CONFIG",
|
|
653
674
|
"CHECKPOINTER_POSTGRES_POOL_MIN_SIZE",
|
|
654
675
|
"CHECKPOINTER_POSTGRES_POOL_TIMEOUT_SECONDS",
|
|
@@ -659,6 +680,9 @@ __all__ = [
|
|
|
659
680
|
"CRON_SCHEDULER_SLEEP_TIME",
|
|
660
681
|
"DATABASE_URI",
|
|
661
682
|
"DATADOG_METRICS_ENABLED",
|
|
683
|
+
"DATA_PLANE_ID",
|
|
684
|
+
"DATA_PLANE_JWT_SECRET",
|
|
685
|
+
"DATA_PLANE_ORG_ID",
|
|
662
686
|
"DB_MIGRATION_BY_CORE_API",
|
|
663
687
|
"EXPOSE_INTERNAL_METRICS_PROMETHEUS",
|
|
664
688
|
"FF_CRONS_ENABLED",
|
|
@@ -19,6 +19,7 @@ from langgraph_grpc_common.proto.encryption_pb2 import (
|
|
|
19
19
|
from langgraph_grpc_common.proto.encryption_pb2_grpc import EncryptionServicer
|
|
20
20
|
from langgraph_sdk import EncryptionContext
|
|
21
21
|
|
|
22
|
+
from langgraph_api.encryption.custom import get_custom_encryption_instance
|
|
22
23
|
from langgraph_api.encryption.middleware import _extract_skip_fields
|
|
23
24
|
from langgraph_api.encryption.shared import BLOB_ENCRYPTION_CONTEXT_KEY, get_encryption
|
|
24
25
|
from langgraph_api.schema import NESTED_ENCRYPTED_SUBFIELDS
|
|
@@ -250,10 +251,11 @@ class EncryptionServicerImpl(EncryptionServicer):
|
|
|
250
251
|
|
|
251
252
|
encryptor = encryption_instance.get_json_encryptor(model_type)
|
|
252
253
|
if encryptor is None:
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
254
|
+
metadata = _parse_metadata(dict(request.context.metadata))
|
|
255
|
+
if metadata:
|
|
256
|
+
data[BLOB_ENCRYPTION_CONTEXT_KEY] = metadata
|
|
257
|
+
return EncryptResponse(data=orjson.dumps(data))
|
|
258
|
+
return EncryptResponse(data=request.data)
|
|
257
259
|
|
|
258
260
|
# Use recursive encryption that handles NESTED_ENCRYPTED_SUBFIELDS
|
|
259
261
|
encrypted = await self._encrypt_field_recursive(
|
|
@@ -279,12 +281,7 @@ class EncryptionServicerImpl(EncryptionServicer):
|
|
|
279
281
|
request,
|
|
280
282
|
context: grpc_aio.ServicerContext,
|
|
281
283
|
) -> DecryptResponse:
|
|
282
|
-
"""Decrypt JSON data using the configured encryption.
|
|
283
|
-
|
|
284
|
-
Uses the JsonEncryptionWrapper which routes to the appropriate decryptor
|
|
285
|
-
based on the encryption context marker (handles AES migration).
|
|
286
|
-
Handles nested subfields defined in NESTED_ENCRYPTED_SUBFIELDS recursively.
|
|
287
|
-
"""
|
|
284
|
+
"""Decrypt JSON data using the configured encryption."""
|
|
288
285
|
try:
|
|
289
286
|
encryption_instance = get_encryption()
|
|
290
287
|
if encryption_instance is None:
|
|
@@ -300,12 +297,9 @@ class EncryptionServicerImpl(EncryptionServicer):
|
|
|
300
297
|
# removes it, so we save and restore it after decryption.
|
|
301
298
|
blob_enc_ctx = data.get(BLOB_ENCRYPTION_CONTEXT_KEY)
|
|
302
299
|
|
|
303
|
-
# Model and field are optional, used for decryptor selection/routing
|
|
304
300
|
model_type: ModelType | None = request.model or None
|
|
305
301
|
field = request.field or None
|
|
306
302
|
|
|
307
|
-
# Get the decryptor from the wrapper (handles routing based on
|
|
308
|
-
# __encryption_context__ marker in the data)
|
|
309
303
|
decryptor = encryption_instance.get_json_decryptor(model_type)
|
|
310
304
|
if decryptor is None:
|
|
311
305
|
model_err = f"No decryptor configured for model type: {model_type}"
|
|
@@ -313,7 +307,6 @@ class EncryptionServicerImpl(EncryptionServicer):
|
|
|
313
307
|
context.set_details(model_err)
|
|
314
308
|
raise RuntimeError(model_err)
|
|
315
309
|
|
|
316
|
-
# Use recursive decryption that handles NESTED_ENCRYPTED_SUBFIELDS
|
|
317
310
|
decrypted = await self._decrypt_field_recursive(
|
|
318
311
|
data, model_type, field or "", decryptor
|
|
319
312
|
)
|
|
@@ -334,3 +327,63 @@ class EncryptionServicerImpl(EncryptionServicer):
|
|
|
334
327
|
context.set_code(grpc.StatusCode.INTERNAL)
|
|
335
328
|
context.set_details(f"Decryption failed: {e}")
|
|
336
329
|
raise
|
|
330
|
+
|
|
331
|
+
async def EncryptBlob(
|
|
332
|
+
self,
|
|
333
|
+
request,
|
|
334
|
+
context: grpc_aio.ServicerContext,
|
|
335
|
+
) -> EncryptResponse:
|
|
336
|
+
"""Encrypt opaque bytes with the configured blob handler."""
|
|
337
|
+
try:
|
|
338
|
+
encryption_instance = get_custom_encryption_instance()
|
|
339
|
+
if (
|
|
340
|
+
encryption_instance is None
|
|
341
|
+
or encryption_instance._blob_encryptor is None
|
|
342
|
+
):
|
|
343
|
+
context.set_code(grpc.StatusCode.FAILED_PRECONDITION)
|
|
344
|
+
context.set_details("No blob encryption configured")
|
|
345
|
+
raise RuntimeError("No blob encryption configured")
|
|
346
|
+
|
|
347
|
+
enc_ctx = _build_encryption_context(
|
|
348
|
+
request.context.model or None,
|
|
349
|
+
request.context.field or None,
|
|
350
|
+
dict(request.context.metadata),
|
|
351
|
+
)
|
|
352
|
+
encrypted = await encryption_instance._blob_encryptor(enc_ctx, request.data)
|
|
353
|
+
return EncryptResponse(data=encrypted)
|
|
354
|
+
except Exception as e:
|
|
355
|
+
await logger.aerror("EncryptBlob failed", error=str(e), exc_info=True)
|
|
356
|
+
if context.code() is None:
|
|
357
|
+
context.set_code(grpc.StatusCode.INTERNAL)
|
|
358
|
+
context.set_details(f"Blob encryption failed: {e}")
|
|
359
|
+
raise
|
|
360
|
+
|
|
361
|
+
async def DecryptBlob(
|
|
362
|
+
self,
|
|
363
|
+
request,
|
|
364
|
+
context: grpc_aio.ServicerContext,
|
|
365
|
+
) -> DecryptResponse:
|
|
366
|
+
"""Decrypt opaque bytes with the configured blob handler."""
|
|
367
|
+
try:
|
|
368
|
+
encryption_instance = get_custom_encryption_instance()
|
|
369
|
+
if (
|
|
370
|
+
encryption_instance is None
|
|
371
|
+
or encryption_instance._blob_decryptor is None
|
|
372
|
+
):
|
|
373
|
+
context.set_code(grpc.StatusCode.FAILED_PRECONDITION)
|
|
374
|
+
context.set_details("No blob decryption configured")
|
|
375
|
+
raise RuntimeError("No blob decryption configured")
|
|
376
|
+
|
|
377
|
+
enc_ctx = _build_encryption_context(
|
|
378
|
+
request.context.model or None,
|
|
379
|
+
request.context.field or None,
|
|
380
|
+
dict(request.context.metadata),
|
|
381
|
+
)
|
|
382
|
+
decrypted = await encryption_instance._blob_decryptor(enc_ctx, request.data)
|
|
383
|
+
return DecryptResponse(data=decrypted)
|
|
384
|
+
except Exception as e:
|
|
385
|
+
await logger.aerror("DecryptBlob failed", error=str(e), exc_info=True)
|
|
386
|
+
if context.code() is None:
|
|
387
|
+
context.set_code(grpc.StatusCode.INTERNAL)
|
|
388
|
+
context.set_details(f"Blob decryption failed: {e}")
|
|
389
|
+
raise
|
|
@@ -180,12 +180,11 @@ async def entrypoint(
|
|
|
180
180
|
):
|
|
181
181
|
from langgraph_api import logging as lg_logging # noqa: PLC0415
|
|
182
182
|
from langgraph_api import timing # noqa: PLC0415
|
|
183
|
-
from langgraph_api.api import
|
|
183
|
+
from langgraph_api.api import custom_app_lifespan # noqa: PLC0415
|
|
184
184
|
from langgraph_api.server import app # noqa: PLC0415
|
|
185
185
|
|
|
186
186
|
lg_logging.set_logging_context({"entrypoint": entrypoint_name})
|
|
187
187
|
tasks: set[asyncio.Task] = set()
|
|
188
|
-
user_lifespan = None if user_router is None else user_router.router.lifespan_context
|
|
189
188
|
wrapped_lifespan = timing.combine_lifespans(
|
|
190
189
|
functools.partial(
|
|
191
190
|
lifespan.lifespan,
|
|
@@ -193,7 +192,7 @@ async def entrypoint(
|
|
|
193
192
|
taskset=tasks,
|
|
194
193
|
cancel_event=cancel_event,
|
|
195
194
|
),
|
|
196
|
-
|
|
195
|
+
custom_app_lifespan,
|
|
197
196
|
)
|
|
198
197
|
|
|
199
198
|
async with wrapped_lifespan(app):
|
|
@@ -10,6 +10,7 @@ from opentelemetry.sdk.resources import SERVICE_NAME, Resource
|
|
|
10
10
|
from opentelemetry.util.types import Attributes
|
|
11
11
|
|
|
12
12
|
from langgraph_api import config
|
|
13
|
+
from langgraph_api.beacon_data_plane_jwt_auth import beacon_otel_session
|
|
13
14
|
from langgraph_api.logging import OTLPFormatter
|
|
14
15
|
|
|
15
16
|
logger = structlog.stdlib.get_logger(__name__)
|
|
@@ -45,10 +46,27 @@ def initialize_self_hosted_logs() -> None:
|
|
|
45
46
|
raise RuntimeError(
|
|
46
47
|
"LANGGRAPH_LOGS_ENABLED is true but no LANGGRAPH_LOGS_ENDPOINT is configured"
|
|
47
48
|
)
|
|
49
|
+
log_endpoint = config.LANGGRAPH_LOGS_ENDPOINT
|
|
50
|
+
|
|
51
|
+
data_plane_settings = (
|
|
52
|
+
("DATA_PLANE_ID", config.DATA_PLANE_ID),
|
|
53
|
+
("DATA_PLANE_ORG_ID", config.DATA_PLANE_ORG_ID),
|
|
54
|
+
("DATA_PLANE_JWT_SECRET", config.DATA_PLANE_JWT_SECRET),
|
|
55
|
+
)
|
|
56
|
+
has_data_plane_settings = any(value for _, value in data_plane_settings)
|
|
57
|
+
missing_data_plane_settings = [
|
|
58
|
+
name for name, value in data_plane_settings if not value
|
|
59
|
+
]
|
|
60
|
+
if has_data_plane_settings and missing_data_plane_settings:
|
|
61
|
+
raise RuntimeError(
|
|
62
|
+
"LANGGRAPH_LOGS_ENABLED is true but required data plane settings are missing: "
|
|
63
|
+
+ ", ".join(missing_data_plane_settings)
|
|
64
|
+
)
|
|
48
65
|
|
|
49
|
-
#
|
|
50
|
-
|
|
51
|
-
|
|
66
|
+
# "data-plane" here refers to a BYOC data plane
|
|
67
|
+
is_data_plane = has_data_plane_settings
|
|
68
|
+
# For now, the non-data-plane path is only enabled for fully self-hosted customers.
|
|
69
|
+
if not is_data_plane and not config.LANGGRAPH_CLOUD_LICENSE_KEY:
|
|
52
70
|
logger.warning(
|
|
53
71
|
"Self-hosted logs require a license key, and do not work with hybrid deployments yet."
|
|
54
72
|
)
|
|
@@ -58,6 +76,13 @@ def initialize_self_hosted_logs() -> None:
|
|
|
58
76
|
resource_attributes = {
|
|
59
77
|
SERVICE_NAME: config.SELF_HOSTED_OBSERVABILITY_SERVICE_NAME,
|
|
60
78
|
}
|
|
79
|
+
if is_data_plane:
|
|
80
|
+
resource_attributes["data_plane_id"] = cast("str", config.DATA_PLANE_ID)
|
|
81
|
+
resource_attributes["organization_id"] = cast(
|
|
82
|
+
"str", config.DATA_PLANE_ORG_ID
|
|
83
|
+
)
|
|
84
|
+
resource_attributes["host"] = config.BYOC_HOST
|
|
85
|
+
resource_attributes["env"] = config.BYOC_ENV
|
|
61
86
|
|
|
62
87
|
if config.LANGGRAPH_CLOUD_LICENSE_KEY:
|
|
63
88
|
try:
|
|
@@ -70,43 +95,47 @@ def initialize_self_hosted_logs() -> None:
|
|
|
70
95
|
_customer_attributes["customer_id"] = CUSTOMER_ID
|
|
71
96
|
if CUSTOMER_NAME:
|
|
72
97
|
_customer_attributes["customer_name"] = CUSTOMER_NAME
|
|
73
|
-
|
|
74
|
-
# resolves to pod name in k8s, or container id in docker
|
|
75
|
-
instance_id = os.environ.get("HOSTNAME")
|
|
76
|
-
if instance_id:
|
|
77
|
-
_customer_attributes["instance_id"] = instance_id
|
|
78
98
|
except ImportError:
|
|
79
99
|
pass
|
|
80
100
|
except Exception as e:
|
|
81
101
|
logger.warning("Failed to get customer info from license", exc_info=e)
|
|
82
102
|
|
|
103
|
+
# Resolves to pod name in k8s, or container ID in Docker.
|
|
104
|
+
instance_id = os.environ.get("HOSTNAME")
|
|
105
|
+
if instance_id:
|
|
106
|
+
_customer_attributes["instance_id"] = instance_id
|
|
107
|
+
|
|
83
108
|
if config.IS_QUEUE_ENTRYPOINT:
|
|
84
109
|
_customer_attributes["entrypoint"] = "queue"
|
|
85
|
-
elif config.IS_EXECUTOR_ENTRYPOINT:
|
|
86
|
-
_customer_attributes["entrypoint"] = "executor"
|
|
87
110
|
else:
|
|
88
111
|
_customer_attributes["entrypoint"] = "api"
|
|
89
112
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
"X-Langchain-License-Key": config.LANGGRAPH_CLOUD_LICENSE_KEY,
|
|
99
|
-
},
|
|
100
|
-
)
|
|
113
|
+
if is_data_plane:
|
|
114
|
+
exporter = OTLPLogExporter(
|
|
115
|
+
endpoint=log_endpoint,
|
|
116
|
+
session=beacon_otel_session(
|
|
117
|
+
cast("str", config.DATA_PLANE_ID),
|
|
118
|
+
cast("str", config.DATA_PLANE_ORG_ID),
|
|
119
|
+
cast("str", config.DATA_PLANE_JWT_SECRET),
|
|
120
|
+
),
|
|
101
121
|
)
|
|
102
|
-
|
|
122
|
+
else:
|
|
123
|
+
exporter = OTLPLogExporter(
|
|
124
|
+
endpoint=log_endpoint,
|
|
125
|
+
headers={
|
|
126
|
+
"X-Langchain-License-Key": config.LANGGRAPH_CLOUD_LICENSE_KEY,
|
|
127
|
+
},
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
_logger_provider = LoggerProvider(resource=Resource.create(resource_attributes))
|
|
131
|
+
_logger_provider.add_log_record_processor(BatchLogRecordProcessor(exporter))
|
|
103
132
|
handler = AttrFilteredLoggingHandler(logger_provider=_logger_provider)
|
|
104
133
|
handler.setFormatter(OTLPFormatter("%(message)s", None, "%"))
|
|
105
134
|
logging.getLogger().addHandler(handler)
|
|
106
135
|
|
|
107
136
|
logger.info(
|
|
108
137
|
"Self-hosted logs initialized successfully",
|
|
109
|
-
endpoint=
|
|
138
|
+
endpoint=log_endpoint,
|
|
110
139
|
)
|
|
111
140
|
except Exception as e:
|
|
112
141
|
logger.exception("Failed to initialize self-hosted logs", exc_info=e)
|
langgraph_api/server.py
CHANGED
|
@@ -30,6 +30,7 @@ from starlette.types import Receive, Scope, Send
|
|
|
30
30
|
|
|
31
31
|
import langgraph_api.config as config
|
|
32
32
|
from langgraph_api.api import (
|
|
33
|
+
custom_app_lifespan,
|
|
33
34
|
middleware_for_protected_routes,
|
|
34
35
|
protected_routes,
|
|
35
36
|
shadowable_meta_routes,
|
|
@@ -234,9 +235,10 @@ if user_router:
|
|
|
234
235
|
update_openapi_spec(app)
|
|
235
236
|
|
|
236
237
|
# Merge lifespans (base + user)
|
|
237
|
-
user_lifespan = app.router.lifespan_context
|
|
238
238
|
validate_router_lifespan_hooks(app.router)
|
|
239
|
-
app.router.lifespan_context = timing.combine_lifespans(
|
|
239
|
+
app.router.lifespan_context = timing.combine_lifespans(
|
|
240
|
+
lifespan, custom_app_lifespan
|
|
241
|
+
)
|
|
240
242
|
|
|
241
243
|
# Merge exception handlers (base + user)
|
|
242
244
|
for k, v in exception_handlers.items():
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
langgraph_api/__init__.py,sha256=
|
|
1
|
+
langgraph_api/__init__.py,sha256=5ZhhvR8DrRSoagQmnUmoCJhIF6VMGURhyWB6-ohDQmU,28
|
|
2
2
|
langgraph_api/_factory_utils.py,sha256=_FqW0olMu2PGLsnRq0ylt3a4jDCe12YIFHExMXI6Nyo,5976
|
|
3
3
|
langgraph_api/asgi_transport.py,sha256=XApY3lIWBZTMbbsl8dDJzl0cLGirmAGE0SifqZUnXvs,11896
|
|
4
4
|
langgraph_api/asyncio.py,sha256=smqyAoO9nIyPPQRw1IVSCh85fwoZP4PEo-5dDhxGruc,10676
|
|
5
|
+
langgraph_api/beacon_data_plane_jwt_auth.py,sha256=SoX2M-_u7oas6zWyss2LBs83E6ynBCFOoD5vVJEHRSU,2297
|
|
5
6
|
langgraph_api/cache.py,sha256=LuB3Te0UdXC8a-uEJoRHoe5XsgXulXI59f_Q6DCJNKc,13005
|
|
6
7
|
langgraph_api/cli.py,sha256=ATtS9s9Cx7QNiGPJceKnMCko29A25ZA-xz39fdxmgfg,22389
|
|
7
8
|
langgraph_api/command.py,sha256=d-k8h6H4ix1n7fSZ-Zb01NbSkEyqrD6cMKfDFXEIYEw,821
|
|
@@ -18,14 +19,14 @@ langgraph_api/metrics_collector.py,sha256=gMLHL18rJyYl985AOmu9eH7W1ttdRdkPHzeycz
|
|
|
18
19
|
langgraph_api/metrics_otlp.py,sha256=YppnNCHmQBj7wZbtKYLJS68_tbD6sGTJXGKIySqUTjU,29657
|
|
19
20
|
langgraph_api/otel_context.py,sha256=DWFwW4Yu88QY4W2J0IRcURR450Th9J2DupvDDkSkMBA,7166
|
|
20
21
|
langgraph_api/patch.py,sha256=ViUknYvyQWS6y0f5XuaEoci2qB_mQv8vZl-oaUxsI6M,1448
|
|
21
|
-
langgraph_api/queue_entrypoint.py,sha256=
|
|
22
|
+
langgraph_api/queue_entrypoint.py,sha256=Bh8YPWN_2Dj3hxnYE9T3w9Igc8bIq7UcT8dngOOX36k,11535
|
|
22
23
|
langgraph_api/release_tags.py,sha256=Dd2L2Mdh7c5D7RhFvdGCXROLH3WI1QKXbEqgjITFFoQ,9437
|
|
23
24
|
langgraph_api/route.py,sha256=_KE8A8Q-J-QfqjGlyM2Kc6n5cirmgt8xmI5-pI8kVEE,8837
|
|
24
25
|
langgraph_api/schema.py,sha256=EkarLwAUKApevb03AmLRumrAVAc2EsG_SLhYFPncSzk,15318
|
|
25
|
-
langgraph_api/self_hosted_logs.py,sha256=
|
|
26
|
+
langgraph_api/self_hosted_logs.py,sha256=rzzr9iVwJgNbDHjrxvuTODCtzpdr9-9X6UAB-3OaXU0,5742
|
|
26
27
|
langgraph_api/self_hosted_metrics.py,sha256=pWsQQ-2ukoFIbmVfzNOSkwCqZ5Cnts6pRSWTII44Ll4,16844
|
|
27
28
|
langgraph_api/serde.py,sha256=V3fO9bkUOlBX3okw5Qi31nlcr59fcuXMgL7DHNyarZY,8855
|
|
28
|
-
langgraph_api/server.py,sha256=
|
|
29
|
+
langgraph_api/server.py,sha256=R0z4R6DjnRwsqjDOjWD-7R3nKoQcS6v7UriG1LuzA1o,11213
|
|
29
30
|
langgraph_api/sse.py,sha256=cChZ7raQUHp8p5BreE_5wMBR8lFO0n7746sV8_HQOrc,4822
|
|
30
31
|
langgraph_api/state.py,sha256=A6jx2OpcVmiFNBafjZ2tX1x6s_AhBk5EW2drd-vaqIc,4396
|
|
31
32
|
langgraph_api/store.py,sha256=i-jVfWzDEmPESVofh1tNWbo0HMLiLz_BSKKV4QjN-dE,5027
|
|
@@ -37,10 +38,10 @@ langgraph_api/validation.py,sha256=4wBPaPbgZU3oysd5XSYi3RRQN7MXM_qIdiifSpnjl10,1
|
|
|
37
38
|
langgraph_api/webhook.py,sha256=qXEtkE6orek2POeOQmPRsEarJgXIYp-LBrZB-OwITxc,9572
|
|
38
39
|
langgraph_api/worker.py,sha256=zWd14iZ9m9TLb9yD_QwTMxHcfM8dd3bhKtM1lz86M80,21256
|
|
39
40
|
langgraph_api/_checkpointer/__init__.py,sha256=ofJTJLGy7Hsuzhj-2dpfDvrDloM0BzlhTzvZOdR9K8U,2223
|
|
40
|
-
langgraph_api/_checkpointer/_adapter.py,sha256=
|
|
41
|
+
langgraph_api/_checkpointer/_adapter.py,sha256=idGYL-FwyVtUXcmhvfYboARfQAkoj0aFD40L5h9N7F8,22289
|
|
41
42
|
langgraph_api/_checkpointer/protocol.py,sha256=udgYKMNtKWG_eLDwYkHXV3b2bZLZg8Rsfm3fjkhU-rU,3635
|
|
42
|
-
langgraph_api/api/__init__.py,sha256=
|
|
43
|
-
langgraph_api/api/a2a.py,sha256=
|
|
43
|
+
langgraph_api/api/__init__.py,sha256=88elgNmz6Y1tIi81dzhHa94KVhLvvIqyEJjcov4aH5U,9463
|
|
44
|
+
langgraph_api/api/a2a.py,sha256=yfVWREWZZYLUUsOEuT8dnTIyQsNIa7CrNfIlGQqfBLQ,113688
|
|
44
45
|
langgraph_api/api/assistants.py,sha256=YelrvQWKNLIoqJheQ1riK2dhCTH3GenbmWyD5fEp-ok,21936
|
|
45
46
|
langgraph_api/api/event_streaming.py,sha256=jvoBp5qHI92JJHsx4f9LEpb0i0VE31G_qFhS5JQWtlA,17809
|
|
46
47
|
langgraph_api/api/meta.py,sha256=5s017GfMs-Ghq6Z5K6yW4UjbVFzCsmeXKn4UEJmfs98,4757
|
|
@@ -65,7 +66,7 @@ langgraph_api/auth/studio_user.py,sha256=gNCicIo6cYaHmFj2sEdsvDYkKW7NWfGXGS2tTAM
|
|
|
65
66
|
langgraph_api/auth/langsmith/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
66
67
|
langgraph_api/auth/langsmith/backend.py,sha256=Y6-VxD7zfV1jzGdjmQ66CgNa3SenLbo3d_375CcKZ9U,3770
|
|
67
68
|
langgraph_api/auth/langsmith/client.py,sha256=79kwCVeHU64nsHsxWipfZhf44lM6vfs2nlfTxlJF6LU,4142
|
|
68
|
-
langgraph_api/config/__init__.py,sha256=
|
|
69
|
+
langgraph_api/config/__init__.py,sha256=4hZs8qHDt5Yx9Pr8bvPCsnf5BjAokrD4BLCvtGNbwkM,27403
|
|
69
70
|
langgraph_api/config/_parse.py,sha256=RpfZgFAPJlRMv13AzGr3kAYbIrqHcgjzO8IgeboTw4A,3922
|
|
70
71
|
langgraph_api/config/schemas.py,sha256=cHzVvepthpD7DDeWE2ytkOHah-iDNH7xRx9dSWUatQI,20864
|
|
71
72
|
langgraph_api/encryption/__init__.py,sha256=gaCZ00CocSbqSqrDn6XJHaSp2CZCnC8qnrD9G4fbzyI,363
|
|
@@ -95,7 +96,7 @@ langgraph_api/grpc/ops/runs.py,sha256=4-Mp-4MgDAn8uerLrOpmXuUqOdfcnzSmb3hSRlO8Hh
|
|
|
95
96
|
langgraph_api/grpc/ops/threads.py,sha256=SJraV838nZ0m6wRrCia7sLYh8KK1WfY7x5xmcspZLek,48140
|
|
96
97
|
langgraph_api/grpc/servicers/__init__.py,sha256=l8yqTA_gMbIj0xHw5-RZYo0JbjL-EpFD8RxPMgW_-bA,422
|
|
97
98
|
langgraph_api/grpc/servicers/checkpointer.py,sha256=aeB2hzsDxbb9i-ildx-kylpDxaYtpAUSG6rphOzbDOs,10482
|
|
98
|
-
langgraph_api/grpc/servicers/encryption.py,sha256=
|
|
99
|
+
langgraph_api/grpc/servicers/encryption.py,sha256=UuBjpUK-tCbPXfx2-AFn0vbmeWw_UBe7xl8pMbwQdbA,15083
|
|
99
100
|
langgraph_api/js/.gitignore,sha256=l5yI6G_V6F1600I1IjiUKn87f4uYIrBAYU1MOyBBhg4,59
|
|
100
101
|
langgraph_api/js/.prettierrc,sha256=0es3ovvyNIqIw81rPQsdt1zCQcOdBqyR_DMbFE4Ifms,19
|
|
101
102
|
langgraph_api/js/.yarnclean,sha256=a5Vf1gAk2gxjcNrGidXoA3URy1fI3Ow5F3zDuVQg5wQ,728
|
|
@@ -165,7 +166,7 @@ hatch_build.py,sha256=Nn9N-EFfVqmH5q3iCUrhqX6zc7qT1jLI3gWSBN2xgYQ,1738
|
|
|
165
166
|
logging.json,sha256=sBy8HDDPuucpLbLUD6e8t5fsiQjJoklZsbVHi2qQ7aQ,367
|
|
166
167
|
openapi.json,sha256=C009Hi2gZaUKU4OOf2P12Qdjodx1mxZd16RAm1lZiIw,219991
|
|
167
168
|
langgraph_grpc_common/__init__.py,sha256=sO7jFHekQxt6zZPhTPZAWLYLB_S8w7-VuJ7NyWFlFNo,97
|
|
168
|
-
langgraph_grpc_common/checkpointer.py,sha256=-
|
|
169
|
+
langgraph_grpc_common/checkpointer.py,sha256=-F51xCHjP3A2gWfwVTTm7x7w9YsH78qJnZ57zTYAwdI,15344
|
|
169
170
|
langgraph_grpc_common/serde.py,sha256=8q290nZ6TdxbEhCTzwEs44XGi2b-3pfrUeMYY5WGlTY,6179
|
|
170
171
|
langgraph_grpc_common/store.py,sha256=YCO8oUssjnFq-n1syBsA_wxkwo8Sp0bgnDrpYjUxv40,4030
|
|
171
172
|
langgraph_grpc_common/conversion/__init__.py,sha256=pLsOrRtmF9YkgmCPhcfym6lYOB7PCJTRItyo1YK_AT8,208
|
|
@@ -177,18 +178,18 @@ langgraph_grpc_common/conversion/store.py,sha256=eQQ7YHrMYRsd1jN9LbqvCxGQGErySV_
|
|
|
177
178
|
langgraph_grpc_common/conversion/struct.py,sha256=yk4jpPdywzn8UosAohun21lU5D_IIglOY9rkBhiG-gA,2072
|
|
178
179
|
langgraph_grpc_common/conversion/value.py,sha256=KW-z0gvpvCq8iWUjjtx8F3XQGM7XsL-D9GzPzdtuMsA,10206
|
|
179
180
|
langgraph_grpc_common/proto/__init__.py,sha256=eBejrrULiDdasi3_Hga_AJW8ZPMkjKyyAeW9Ey640Ks,1375
|
|
180
|
-
langgraph_grpc_common/proto/checkpointer_pb2.py,sha256=
|
|
181
|
-
langgraph_grpc_common/proto/checkpointer_pb2.pyi,sha256=
|
|
181
|
+
langgraph_grpc_common/proto/checkpointer_pb2.py,sha256=dQMt9vNXqXDvTU6euvH52uliDsL44BSppJIsAySmPfc,9039
|
|
182
|
+
langgraph_grpc_common/proto/checkpointer_pb2.pyi,sha256=Jjr9-oeKGk1wuIFf8m7zoCb9lHty1Bh9u-jxvRvqYLI,23319
|
|
182
183
|
langgraph_grpc_common/proto/checkpointer_pb2_grpc.py,sha256=FQJK-lC8alOkLltA51aq1ABam62rVerTZRZccTFqUjw,20152
|
|
183
184
|
langgraph_grpc_common/proto/checkpointer_pb2_grpc.pyi,sha256=8fPL90WjHnMaREG7l9qYt5fQePeT8YFWQ0qVy56QtYw,10210
|
|
184
185
|
langgraph_grpc_common/proto/core_api_pb2.py,sha256=r5KLcYAtjqTiKzv671Vwa6teYdsV9gHFkorK5HOs6Aw,51151
|
|
185
186
|
langgraph_grpc_common/proto/core_api_pb2.pyi,sha256=sulKXZ_SD94mQKkz7zKHum1XGo3KDfFSnR09vkV15A0,208955
|
|
186
187
|
langgraph_grpc_common/proto/core_api_pb2_grpc.py,sha256=bONBb3GptNvrgOSI4accjE_f9IA8xxbfarDs1Knrosg,81198
|
|
187
188
|
langgraph_grpc_common/proto/core_api_pb2_grpc.pyi,sha256=XLu2-5YZHm9QyZWjWsnUWe7uN6n1-avVvsyR6N5D3fs,29325
|
|
188
|
-
langgraph_grpc_common/proto/encryption_pb2.py,sha256=
|
|
189
|
-
langgraph_grpc_common/proto/encryption_pb2.pyi,sha256=
|
|
190
|
-
langgraph_grpc_common/proto/encryption_pb2_grpc.py,sha256=
|
|
191
|
-
langgraph_grpc_common/proto/encryption_pb2_grpc.pyi,sha256=
|
|
189
|
+
langgraph_grpc_common/proto/encryption_pb2.py,sha256=CEDsn3bJv2e_QzAFsvp2845Nac_lBRPGsb_9pFJthxE,4119
|
|
190
|
+
langgraph_grpc_common/proto/encryption_pb2.pyi,sha256=YWilpCKKzV9u2BA8i-Pq4gStaxhBgqo_fAxTLnreG40,10647
|
|
191
|
+
langgraph_grpc_common/proto/encryption_pb2_grpc.py,sha256=H05u5VP1bVgYCtiELRb44DOr0wlmA6w8WtMCuqAyYLk,8681
|
|
192
|
+
langgraph_grpc_common/proto/encryption_pb2_grpc.pyi,sha256=KJzuCnp3xKuy-hAwRNHZEF75DQ6scmiq_QMTDY-BSbY,4428
|
|
192
193
|
langgraph_grpc_common/proto/engine_common_pb2.py,sha256=CdNIXXE924mrseYyOcD6x0O5zLcNeU8ihdiCxlamHH8,29198
|
|
193
194
|
langgraph_grpc_common/proto/engine_common_pb2.pyi,sha256=BM3-HPagho_LA74pg4SAX1FV_Lnn6jXut2CKArFBu88,115071
|
|
194
195
|
langgraph_grpc_common/proto/engine_common_pb2_grpc.py,sha256=dqNIakFHauOuu0B6VK7z_VRb6yuY-jdA11glk0IgdkM,893
|
|
@@ -237,8 +238,8 @@ langgraph_grpc_common/proto/store_pb2.py,sha256=thQ5HBLblZ7Adayp1U8ty_lrpn-7XEHn
|
|
|
237
238
|
langgraph_grpc_common/proto/store_pb2.pyi,sha256=_dcxyxV8moR6H67qU42r2h2p_Ub-PZSHs3OoS9KMdSY,27782
|
|
238
239
|
langgraph_grpc_common/proto/store_pb2_grpc.py,sha256=Kyt8eV2NXkjh3be_BYytGdD19kNf74961cruonMvCrw,3300
|
|
239
240
|
langgraph_grpc_common/proto/store_pb2_grpc.pyi,sha256=oS1h2cDoq2OjyM8xazcmc8LwRDC5oCAD0zto2QUmPQw,2027
|
|
240
|
-
langgraph_api-0.14.0.
|
|
241
|
-
langgraph_api-0.14.0.
|
|
242
|
-
langgraph_api-0.14.0.
|
|
243
|
-
langgraph_api-0.14.0.
|
|
244
|
-
langgraph_api-0.14.0.
|
|
241
|
+
langgraph_api-0.14.0.dev6.dist-info/METADATA,sha256=Y8KYhDNzjBif5sHNqYkfnqy9YrbQmUcKERm8yBnfYeQ,4630
|
|
242
|
+
langgraph_api-0.14.0.dev6.dist-info/WHEEL,sha256=zOwg4jB6zX2kU910N-cMawjivD6tO8NEWvE12je1bVk,87
|
|
243
|
+
langgraph_api-0.14.0.dev6.dist-info/entry_points.txt,sha256=hGedv8n7cgi41PypMfinwS_HfCwA7xJIfS0jAp8htV8,78
|
|
244
|
+
langgraph_api-0.14.0.dev6.dist-info/licenses/LICENSE,sha256=ZPwVR73Biwm3sK6vR54djCrhaRiM4cAD2zvOQZV8Xis,3859
|
|
245
|
+
langgraph_api-0.14.0.dev6.dist-info/RECORD,,
|
|
@@ -16,6 +16,7 @@ from typing import TYPE_CHECKING, Any, TypeVar, cast
|
|
|
16
16
|
|
|
17
17
|
import grpc
|
|
18
18
|
import grpc.aio
|
|
19
|
+
import orjson
|
|
19
20
|
from langgraph.checkpoint.base import (
|
|
20
21
|
BaseCheckpointSaver,
|
|
21
22
|
ChannelVersions,
|
|
@@ -43,6 +44,7 @@ if TYPE_CHECKING:
|
|
|
43
44
|
T = TypeVar("T")
|
|
44
45
|
RetryFn = Callable[[Callable[[], Awaitable[Any]], str], Awaitable[Any]]
|
|
45
46
|
StubProvider = Callable[[], Awaitable["CheckpointerStub"]]
|
|
47
|
+
EncryptionContextProvider = Callable[[], Mapping[str, Any]]
|
|
46
48
|
|
|
47
49
|
|
|
48
50
|
class GrpcCheckpointer(BaseCheckpointSaver):
|
|
@@ -57,6 +59,7 @@ class GrpcCheckpointer(BaseCheckpointSaver):
|
|
|
57
59
|
retry: RetryFn | None = None,
|
|
58
60
|
retry_context_prefix: str | None = None,
|
|
59
61
|
serializer: SerializerProtocol | None = None,
|
|
62
|
+
encryption_context_provider: EncryptionContextProvider | None = None,
|
|
60
63
|
) -> None:
|
|
61
64
|
"""Construct a gRPC checkpointer client.
|
|
62
65
|
|
|
@@ -74,18 +77,34 @@ class GrpcCheckpointer(BaseCheckpointSaver):
|
|
|
74
77
|
so unrelated gRPC ops sharing
|
|
75
78
|
``langgraph_grpc_common.conversion.*`` keep using the
|
|
76
79
|
process-wide default
|
|
80
|
+
encryption_context_provider: Optional callable returning the
|
|
81
|
+
request-scoped encryption context, sent with writes so the
|
|
82
|
+
server can encrypt blobs with the caller's context. Injected
|
|
83
|
+
rather than read directly because the context lives in a
|
|
84
|
+
``ContextVar`` owned by ``langgraph_api``, which this package
|
|
85
|
+
does not depend on.
|
|
77
86
|
"""
|
|
78
87
|
super().__init__(serde=None)
|
|
79
88
|
self._get_stub = get_stub
|
|
80
89
|
self._retry = retry
|
|
81
90
|
self._retry_context_prefix = retry_context_prefix or type(self).__name__
|
|
82
91
|
self._serializer = serializer
|
|
92
|
+
self._encryption_context_provider = encryption_context_provider
|
|
83
93
|
self.latest_iter = None
|
|
84
94
|
try:
|
|
85
95
|
self._loop: asyncio.AbstractEventLoop | None = asyncio.get_running_loop()
|
|
86
96
|
except RuntimeError:
|
|
87
97
|
self._loop = None
|
|
88
98
|
|
|
99
|
+
def _encryption_context_json(self) -> dict[str, bytes]:
|
|
100
|
+
"""Return encryption context as JSON-encoded proto map values."""
|
|
101
|
+
if self._encryption_context_provider is None:
|
|
102
|
+
return {}
|
|
103
|
+
context = self._encryption_context_provider()
|
|
104
|
+
if not context:
|
|
105
|
+
return {}
|
|
106
|
+
return {key: orjson.dumps(value) for key, value in context.items()}
|
|
107
|
+
|
|
89
108
|
def _scoped(self) -> AbstractContextManager[None]:
|
|
90
109
|
"""Bind ``self._serializer`` for the duration of a conversion call."""
|
|
91
110
|
if self._serializer is None:
|
|
@@ -168,6 +187,7 @@ class GrpcCheckpointer(BaseCheckpointSaver):
|
|
|
168
187
|
checkpoint=ckpt_conv.checkpoint_to_proto(checkpoint),
|
|
169
188
|
metadata=ckpt_conv.checkpoint_metadata_to_proto(metadata),
|
|
170
189
|
new_versions={k: str(v) for k, v in new_versions.items()},
|
|
190
|
+
encryption_context_json=self._encryption_context_json(),
|
|
171
191
|
)
|
|
172
192
|
|
|
173
193
|
async def _request() -> RunnableConfig:
|
|
@@ -202,6 +222,7 @@ class GrpcCheckpointer(BaseCheckpointSaver):
|
|
|
202
222
|
writes=ckpt_conv.writes_to_proto(writes),
|
|
203
223
|
task_id=task_id,
|
|
204
224
|
task_path=task_path,
|
|
225
|
+
encryption_context_json=self._encryption_context_json(),
|
|
205
226
|
)
|
|
206
227
|
|
|
207
228
|
async def _request() -> None:
|
|
@@ -26,7 +26,7 @@ from google.protobuf import empty_pb2 as google_dot_protobuf_dot_empty__pb2
|
|
|
26
26
|
from . import engine_common_pb2 as engine__common__pb2
|
|
27
27
|
|
|
28
28
|
|
|
29
|
-
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x12\x63heckpointer.proto\x12\x0c\x63heckpointer\x1a\x1bgoogle/protobuf/empty.proto\x1a\x13\x65ngine-common.proto\"\
|
|
29
|
+
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x12\x63heckpointer.proto\x12\x0c\x63heckpointer\x1a\x1bgoogle/protobuf/empty.proto\x1a\x13\x65ngine-common.proto\"\xab\x03\n\nPutRequest\x12\x32\n\x06\x63onfig\x18\x01 \x01(\x0b\x32\".engineCommon.EngineRunnableConfig\x12,\n\ncheckpoint\x18\x02 \x01(\x0b\x32\x18.engineCommon.Checkpoint\x12\x32\n\x08metadata\x18\x03 \x01(\x0b\x32 .engineCommon.CheckpointMetadata\x12?\n\x0cnew_versions\x18\x04 \x03(\x0b\x32).checkpointer.PutRequest.NewVersionsEntry\x12T\n\x17\x65ncryption_context_json\x18\x05 \x03(\x0b\x32\x33.checkpointer.PutRequest.EncryptionContextJsonEntry\x1a\x32\n\x10NewVersionsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x1a<\n\x1a\x45ncryptionContextJsonEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x0c:\x02\x38\x01\"\xa9\x02\n\x10PutWritesRequest\x12\x32\n\x06\x63onfig\x18\x01 \x01(\x0b\x32\".engineCommon.EngineRunnableConfig\x12#\n\x06writes\x18\x02 \x03(\x0b\x32\x13.engineCommon.Write\x12\x0f\n\x07task_id\x18\x03 \x01(\t\x12\x11\n\ttask_path\x18\x04 \x01(\t\x12Z\n\x17\x65ncryption_context_json\x18\x05 \x03(\x0b\x32\x39.checkpointer.PutWritesRequest.EncryptionContextJsonEntry\x1a<\n\x1a\x45ncryptionContextJsonEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x0c:\x02\x38\x01\"\x86\x01\n\x0c\x43\x61pabilities\x12\x1e\n\x16supports_delete_thread\x18\x01 \x01(\x08\x12\x16\n\x0esupports_prune\x18\x02 \x01(\x08\x12 \n\x18supports_delete_for_runs\x18\x03 \x01(\x08\x12\x1c\n\x14supports_copy_thread\x18\x04 \x01(\x08\"\xa8\x01\n\x0bListRequest\x12\x32\n\x06\x63onfig\x18\x01 \x01(\x0b\x32\".engineCommon.EngineRunnableConfig\x12\x13\n\x0b\x66ilter_json\x18\x02 \x01(\x0c\x12\x32\n\x06\x62\x65\x66ore\x18\x03 \x01(\x0b\x32\".engineCommon.EngineRunnableConfig\x12\x12\n\x05limit\x18\x04 \x01(\x03H\x00\x88\x01\x01\x42\x08\n\x06_limit\"(\n\x13\x44\x65leteThreadRequest\x12\x11\n\tthread_id\x18\x01 \x01(\t\"\'\n\x14\x44\x65leteForRunsRequest\x12\x0f\n\x07run_ids\x18\x01 \x03(\t\"A\n\x11\x43opyThreadRequest\x12\x16\n\x0e\x66rom_thread_id\x18\x01 \x01(\t\x12\x14\n\x0cto_thread_id\x18\x02 \x01(\t\"\xa1\x01\n\x0cPruneRequest\x12\x12\n\nthread_ids\x18\x01 \x03(\t\x12:\n\x08strategy\x18\x02 \x01(\x0e\x32(.checkpointer.PruneRequest.PruneStrategy\"A\n\rPruneStrategy\x12\x0f\n\x0bUNSPECIFIED\x10\x00\x12\x0f\n\x0bKEEP_LATEST\x10\x01\x12\x0e\n\nDELETE_ALL\x10\x02\"E\n\x0fGetTupleRequest\x12\x32\n\x06\x63onfig\x18\x01 \x01(\x0b\x32\".engineCommon.EngineRunnableConfig\"F\n\x0bPutResponse\x12\x37\n\x0bnext_config\x18\x01 \x01(\x0b\x32\".engineCommon.EngineRunnableConfig\"H\n\x0cListResponse\x12\x38\n\x11\x63heckpoint_tuples\x18\x01 \x03(\x0b\x32\x1d.engineCommon.CheckpointTuple\"e\n\x10GetTupleResponse\x12<\n\x10\x63heckpoint_tuple\x18\x01 \x01(\x0b\x32\x1d.engineCommon.CheckpointTupleH\x00\x88\x01\x01\x42\x13\n\x11_checkpoint_tuple\"~\n\x18\x44\x65ltaChannelHistoryEntry\x12-\n\x04seed\x18\x01 \x01(\x0b\x32\x1a.engineCommon.ChannelValueH\x00\x88\x01\x01\x12*\n\x06writes\x18\x02 \x03(\x0b\x32\x1a.engineCommon.PendingWriteB\x07\n\x05_seed\"r\n\x1dGetDeltaChannelHistoryRequest\x12\x11\n\tthread_id\x18\x01 \x01(\t\x12\x15\n\rcheckpoint_ns\x18\x02 \x01(\t\x12\x15\n\rcheckpoint_id\x18\x03 \x01(\t\x12\x10\n\x08\x63hannels\x18\x04 \x03(\t\"\xc4\x01\n\x1eGetDeltaChannelHistoryResponse\x12J\n\x07\x65ntries\x18\x01 \x03(\x0b\x32\x39.checkpointer.GetDeltaChannelHistoryResponse.EntriesEntry\x1aV\n\x0c\x45ntriesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x35\n\x05value\x18\x02 \x01(\x0b\x32&.checkpointer.DeltaChannelHistoryEntry:\x02\x38\x01\x32\xf1\x05\n\x0c\x43heckpointer\x12:\n\x03Put\x12\x18.checkpointer.PutRequest\x1a\x19.checkpointer.PutResponse\x12\x43\n\tPutWrites\x12\x1e.checkpointer.PutWritesRequest\x1a\x16.google.protobuf.Empty\x12\x45\n\x0fGetCapabilities\x12\x16.google.protobuf.Empty\x1a\x1a.checkpointer.Capabilities\x12=\n\x04List\x12\x19.checkpointer.ListRequest\x1a\x1a.checkpointer.ListResponse\x12I\n\x08GetTuple\x12\x1d.checkpointer.GetTupleRequest\x1a\x1e.checkpointer.GetTupleResponse\x12I\n\x0c\x44\x65leteThread\x12!.checkpointer.DeleteThreadRequest\x1a\x16.google.protobuf.Empty\x12K\n\rDeleteForRuns\x12\".checkpointer.DeleteForRunsRequest\x1a\x16.google.protobuf.Empty\x12\x45\n\nCopyThread\x12\x1f.checkpointer.CopyThreadRequest\x1a\x16.google.protobuf.Empty\x12;\n\x05Prune\x12\x1a.checkpointer.PruneRequest\x1a\x16.google.protobuf.Empty\x12s\n\x16GetDeltaChannelHistory\x12+.checkpointer.GetDeltaChannelHistoryRequest\x1a,.checkpointer.GetDeltaChannelHistoryResponseB?Z=github.com/langchain-ai/langgraph-api/core/internal/engine/pbb\x06proto3')
|
|
30
30
|
|
|
31
31
|
_globals = globals()
|
|
32
32
|
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
|
@@ -36,44 +36,52 @@ if not _descriptor._USE_C_DESCRIPTORS:
|
|
|
36
36
|
_globals['DESCRIPTOR']._serialized_options = b'Z=github.com/langchain-ai/langgraph-api/core/internal/engine/pb'
|
|
37
37
|
_globals['_PUTREQUEST_NEWVERSIONSENTRY']._loaded_options = None
|
|
38
38
|
_globals['_PUTREQUEST_NEWVERSIONSENTRY']._serialized_options = b'8\001'
|
|
39
|
+
_globals['_PUTREQUEST_ENCRYPTIONCONTEXTJSONENTRY']._loaded_options = None
|
|
40
|
+
_globals['_PUTREQUEST_ENCRYPTIONCONTEXTJSONENTRY']._serialized_options = b'8\001'
|
|
41
|
+
_globals['_PUTWRITESREQUEST_ENCRYPTIONCONTEXTJSONENTRY']._loaded_options = None
|
|
42
|
+
_globals['_PUTWRITESREQUEST_ENCRYPTIONCONTEXTJSONENTRY']._serialized_options = b'8\001'
|
|
39
43
|
_globals['_GETDELTACHANNELHISTORYRESPONSE_ENTRIESENTRY']._loaded_options = None
|
|
40
44
|
_globals['_GETDELTACHANNELHISTORYRESPONSE_ENTRIESENTRY']._serialized_options = b'8\001'
|
|
41
45
|
_globals['_PUTREQUEST']._serialized_start=87
|
|
42
|
-
_globals['_PUTREQUEST']._serialized_end=
|
|
43
|
-
_globals['_PUTREQUEST_NEWVERSIONSENTRY']._serialized_start=
|
|
44
|
-
_globals['_PUTREQUEST_NEWVERSIONSENTRY']._serialized_end=
|
|
45
|
-
_globals['
|
|
46
|
-
_globals['
|
|
47
|
-
_globals['
|
|
48
|
-
_globals['
|
|
49
|
-
_globals['
|
|
50
|
-
_globals['
|
|
51
|
-
_globals['
|
|
52
|
-
_globals['
|
|
53
|
-
_globals['
|
|
54
|
-
_globals['
|
|
55
|
-
_globals['
|
|
56
|
-
_globals['
|
|
57
|
-
_globals['
|
|
58
|
-
_globals['
|
|
59
|
-
_globals['
|
|
60
|
-
_globals['
|
|
61
|
-
_globals['
|
|
62
|
-
_globals['
|
|
63
|
-
_globals['
|
|
64
|
-
_globals['
|
|
65
|
-
_globals['
|
|
66
|
-
_globals['
|
|
67
|
-
_globals['
|
|
68
|
-
_globals['
|
|
69
|
-
_globals['
|
|
70
|
-
_globals['
|
|
71
|
-
_globals['
|
|
72
|
-
_globals['
|
|
73
|
-
_globals['
|
|
74
|
-
_globals['
|
|
75
|
-
_globals['
|
|
76
|
-
_globals['
|
|
77
|
-
_globals['
|
|
78
|
-
_globals['
|
|
46
|
+
_globals['_PUTREQUEST']._serialized_end=514
|
|
47
|
+
_globals['_PUTREQUEST_NEWVERSIONSENTRY']._serialized_start=402
|
|
48
|
+
_globals['_PUTREQUEST_NEWVERSIONSENTRY']._serialized_end=452
|
|
49
|
+
_globals['_PUTREQUEST_ENCRYPTIONCONTEXTJSONENTRY']._serialized_start=454
|
|
50
|
+
_globals['_PUTREQUEST_ENCRYPTIONCONTEXTJSONENTRY']._serialized_end=514
|
|
51
|
+
_globals['_PUTWRITESREQUEST']._serialized_start=517
|
|
52
|
+
_globals['_PUTWRITESREQUEST']._serialized_end=814
|
|
53
|
+
_globals['_PUTWRITESREQUEST_ENCRYPTIONCONTEXTJSONENTRY']._serialized_start=454
|
|
54
|
+
_globals['_PUTWRITESREQUEST_ENCRYPTIONCONTEXTJSONENTRY']._serialized_end=514
|
|
55
|
+
_globals['_CAPABILITIES']._serialized_start=817
|
|
56
|
+
_globals['_CAPABILITIES']._serialized_end=951
|
|
57
|
+
_globals['_LISTREQUEST']._serialized_start=954
|
|
58
|
+
_globals['_LISTREQUEST']._serialized_end=1122
|
|
59
|
+
_globals['_DELETETHREADREQUEST']._serialized_start=1124
|
|
60
|
+
_globals['_DELETETHREADREQUEST']._serialized_end=1164
|
|
61
|
+
_globals['_DELETEFORRUNSREQUEST']._serialized_start=1166
|
|
62
|
+
_globals['_DELETEFORRUNSREQUEST']._serialized_end=1205
|
|
63
|
+
_globals['_COPYTHREADREQUEST']._serialized_start=1207
|
|
64
|
+
_globals['_COPYTHREADREQUEST']._serialized_end=1272
|
|
65
|
+
_globals['_PRUNEREQUEST']._serialized_start=1275
|
|
66
|
+
_globals['_PRUNEREQUEST']._serialized_end=1436
|
|
67
|
+
_globals['_PRUNEREQUEST_PRUNESTRATEGY']._serialized_start=1371
|
|
68
|
+
_globals['_PRUNEREQUEST_PRUNESTRATEGY']._serialized_end=1436
|
|
69
|
+
_globals['_GETTUPLEREQUEST']._serialized_start=1438
|
|
70
|
+
_globals['_GETTUPLEREQUEST']._serialized_end=1507
|
|
71
|
+
_globals['_PUTRESPONSE']._serialized_start=1509
|
|
72
|
+
_globals['_PUTRESPONSE']._serialized_end=1579
|
|
73
|
+
_globals['_LISTRESPONSE']._serialized_start=1581
|
|
74
|
+
_globals['_LISTRESPONSE']._serialized_end=1653
|
|
75
|
+
_globals['_GETTUPLERESPONSE']._serialized_start=1655
|
|
76
|
+
_globals['_GETTUPLERESPONSE']._serialized_end=1756
|
|
77
|
+
_globals['_DELTACHANNELHISTORYENTRY']._serialized_start=1758
|
|
78
|
+
_globals['_DELTACHANNELHISTORYENTRY']._serialized_end=1884
|
|
79
|
+
_globals['_GETDELTACHANNELHISTORYREQUEST']._serialized_start=1886
|
|
80
|
+
_globals['_GETDELTACHANNELHISTORYREQUEST']._serialized_end=2000
|
|
81
|
+
_globals['_GETDELTACHANNELHISTORYRESPONSE']._serialized_start=2003
|
|
82
|
+
_globals['_GETDELTACHANNELHISTORYRESPONSE']._serialized_end=2199
|
|
83
|
+
_globals['_GETDELTACHANNELHISTORYRESPONSE_ENTRIESENTRY']._serialized_start=2113
|
|
84
|
+
_globals['_GETDELTACHANNELHISTORYRESPONSE_ENTRIESENTRY']._serialized_end=2199
|
|
85
|
+
_globals['_CHECKPOINTER']._serialized_start=2202
|
|
86
|
+
_globals['_CHECKPOINTER']._serialized_end=2955
|
|
79
87
|
# @@protoc_insertion_point(module_scope)
|
|
@@ -22,6 +22,12 @@ DESCRIPTOR: _descriptor.FileDescriptor
|
|
|
22
22
|
|
|
23
23
|
@_typing.final
|
|
24
24
|
class PutRequest(_message.Message):
|
|
25
|
+
"""encryption_context_json: request-scoped encryption context, keyed by name
|
|
26
|
+
with JSON-encoded values (e.g. {"tenant_id": "\\"acme\\""}). Writes must pass
|
|
27
|
+
it because it lives in a Python ContextVar the core server cannot observe;
|
|
28
|
+
reads recover it from the stored blob wrapper.
|
|
29
|
+
"""
|
|
30
|
+
|
|
25
31
|
DESCRIPTOR: _descriptor.Descriptor
|
|
26
32
|
|
|
27
33
|
@_typing.final
|
|
@@ -44,10 +50,31 @@ class PutRequest(_message.Message):
|
|
|
44
50
|
def ClearField(self, field_name: _ClearFieldArgType) -> None: ...
|
|
45
51
|
def WhichOneof(self, oneof_group: _Never) -> None: ...
|
|
46
52
|
|
|
53
|
+
@_typing.final
|
|
54
|
+
class EncryptionContextJsonEntry(_message.Message):
|
|
55
|
+
DESCRIPTOR: _descriptor.Descriptor
|
|
56
|
+
|
|
57
|
+
KEY_FIELD_NUMBER: _builtins.int
|
|
58
|
+
VALUE_FIELD_NUMBER: _builtins.int
|
|
59
|
+
key: _builtins.str
|
|
60
|
+
value: _builtins.bytes
|
|
61
|
+
def __init__(
|
|
62
|
+
self,
|
|
63
|
+
*,
|
|
64
|
+
key: _builtins.str = ...,
|
|
65
|
+
value: _builtins.bytes = ...,
|
|
66
|
+
) -> None: ...
|
|
67
|
+
_HasFieldArgType: _TypeAlias = _Never # noqa: Y015
|
|
68
|
+
def HasField(self, field_name: _HasFieldArgType) -> _builtins.bool: ...
|
|
69
|
+
_ClearFieldArgType: _TypeAlias = _typing.Literal["key", b"key", "value", b"value"] # noqa: Y015
|
|
70
|
+
def ClearField(self, field_name: _ClearFieldArgType) -> None: ...
|
|
71
|
+
def WhichOneof(self, oneof_group: _Never) -> None: ...
|
|
72
|
+
|
|
47
73
|
CONFIG_FIELD_NUMBER: _builtins.int
|
|
48
74
|
CHECKPOINT_FIELD_NUMBER: _builtins.int
|
|
49
75
|
METADATA_FIELD_NUMBER: _builtins.int
|
|
50
76
|
NEW_VERSIONS_FIELD_NUMBER: _builtins.int
|
|
77
|
+
ENCRYPTION_CONTEXT_JSON_FIELD_NUMBER: _builtins.int
|
|
51
78
|
@_builtins.property
|
|
52
79
|
def config(self) -> _engine_common_pb2.EngineRunnableConfig: ...
|
|
53
80
|
@_builtins.property
|
|
@@ -56,6 +83,8 @@ class PutRequest(_message.Message):
|
|
|
56
83
|
def metadata(self) -> _engine_common_pb2.CheckpointMetadata: ...
|
|
57
84
|
@_builtins.property
|
|
58
85
|
def new_versions(self) -> _containers.ScalarMap[_builtins.str, _builtins.str]: ...
|
|
86
|
+
@_builtins.property
|
|
87
|
+
def encryption_context_json(self) -> _containers.ScalarMap[_builtins.str, _builtins.bytes]: ...
|
|
59
88
|
def __init__(
|
|
60
89
|
self,
|
|
61
90
|
*,
|
|
@@ -63,10 +92,11 @@ class PutRequest(_message.Message):
|
|
|
63
92
|
checkpoint: _engine_common_pb2.Checkpoint | None = ...,
|
|
64
93
|
metadata: _engine_common_pb2.CheckpointMetadata | None = ...,
|
|
65
94
|
new_versions: _abc.Mapping[_builtins.str, _builtins.str] | None = ...,
|
|
95
|
+
encryption_context_json: _abc.Mapping[_builtins.str, _builtins.bytes] | None = ...,
|
|
66
96
|
) -> None: ...
|
|
67
97
|
_HasFieldArgType: _TypeAlias = _typing.Literal["checkpoint", b"checkpoint", "config", b"config", "metadata", b"metadata"] # noqa: Y015
|
|
68
98
|
def HasField(self, field_name: _HasFieldArgType) -> _builtins.bool: ...
|
|
69
|
-
_ClearFieldArgType: _TypeAlias = _typing.Literal["checkpoint", b"checkpoint", "config", b"config", "metadata", b"metadata", "new_versions", b"new_versions"] # noqa: Y015
|
|
99
|
+
_ClearFieldArgType: _TypeAlias = _typing.Literal["checkpoint", b"checkpoint", "config", b"config", "encryption_context_json", b"encryption_context_json", "metadata", b"metadata", "new_versions", b"new_versions"] # noqa: Y015
|
|
70
100
|
def ClearField(self, field_name: _ClearFieldArgType) -> None: ...
|
|
71
101
|
def WhichOneof(self, oneof_group: _Never) -> None: ...
|
|
72
102
|
|
|
@@ -76,16 +106,39 @@ Global___PutRequest: _TypeAlias = PutRequest # noqa: Y015
|
|
|
76
106
|
class PutWritesRequest(_message.Message):
|
|
77
107
|
DESCRIPTOR: _descriptor.Descriptor
|
|
78
108
|
|
|
109
|
+
@_typing.final
|
|
110
|
+
class EncryptionContextJsonEntry(_message.Message):
|
|
111
|
+
DESCRIPTOR: _descriptor.Descriptor
|
|
112
|
+
|
|
113
|
+
KEY_FIELD_NUMBER: _builtins.int
|
|
114
|
+
VALUE_FIELD_NUMBER: _builtins.int
|
|
115
|
+
key: _builtins.str
|
|
116
|
+
value: _builtins.bytes
|
|
117
|
+
def __init__(
|
|
118
|
+
self,
|
|
119
|
+
*,
|
|
120
|
+
key: _builtins.str = ...,
|
|
121
|
+
value: _builtins.bytes = ...,
|
|
122
|
+
) -> None: ...
|
|
123
|
+
_HasFieldArgType: _TypeAlias = _Never # noqa: Y015
|
|
124
|
+
def HasField(self, field_name: _HasFieldArgType) -> _builtins.bool: ...
|
|
125
|
+
_ClearFieldArgType: _TypeAlias = _typing.Literal["key", b"key", "value", b"value"] # noqa: Y015
|
|
126
|
+
def ClearField(self, field_name: _ClearFieldArgType) -> None: ...
|
|
127
|
+
def WhichOneof(self, oneof_group: _Never) -> None: ...
|
|
128
|
+
|
|
79
129
|
CONFIG_FIELD_NUMBER: _builtins.int
|
|
80
130
|
WRITES_FIELD_NUMBER: _builtins.int
|
|
81
131
|
TASK_ID_FIELD_NUMBER: _builtins.int
|
|
82
132
|
TASK_PATH_FIELD_NUMBER: _builtins.int
|
|
133
|
+
ENCRYPTION_CONTEXT_JSON_FIELD_NUMBER: _builtins.int
|
|
83
134
|
task_id: _builtins.str
|
|
84
135
|
task_path: _builtins.str
|
|
85
136
|
@_builtins.property
|
|
86
137
|
def config(self) -> _engine_common_pb2.EngineRunnableConfig: ...
|
|
87
138
|
@_builtins.property
|
|
88
139
|
def writes(self) -> _containers.RepeatedCompositeFieldContainer[_engine_common_pb2.Write]: ...
|
|
140
|
+
@_builtins.property
|
|
141
|
+
def encryption_context_json(self) -> _containers.ScalarMap[_builtins.str, _builtins.bytes]: ...
|
|
89
142
|
def __init__(
|
|
90
143
|
self,
|
|
91
144
|
*,
|
|
@@ -93,10 +146,11 @@ class PutWritesRequest(_message.Message):
|
|
|
93
146
|
writes: _abc.Iterable[_engine_common_pb2.Write] | None = ...,
|
|
94
147
|
task_id: _builtins.str = ...,
|
|
95
148
|
task_path: _builtins.str = ...,
|
|
149
|
+
encryption_context_json: _abc.Mapping[_builtins.str, _builtins.bytes] | None = ...,
|
|
96
150
|
) -> None: ...
|
|
97
151
|
_HasFieldArgType: _TypeAlias = _typing.Literal["config", b"config"] # noqa: Y015
|
|
98
152
|
def HasField(self, field_name: _HasFieldArgType) -> _builtins.bool: ...
|
|
99
|
-
_ClearFieldArgType: _TypeAlias = _typing.Literal["config", b"config", "task_id", b"task_id", "task_path", b"task_path", "writes", b"writes"] # noqa: Y015
|
|
153
|
+
_ClearFieldArgType: _TypeAlias = _typing.Literal["config", b"config", "encryption_context_json", b"encryption_context_json", "task_id", b"task_id", "task_path", b"task_path", "writes", b"writes"] # noqa: Y015
|
|
100
154
|
def ClearField(self, field_name: _ClearFieldArgType) -> None: ...
|
|
101
155
|
def WhichOneof(self, oneof_group: _Never) -> None: ...
|
|
102
156
|
|
|
@@ -24,7 +24,7 @@ _sym_db = _symbol_database.Default()
|
|
|
24
24
|
|
|
25
25
|
|
|
26
26
|
|
|
27
|
-
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x10\x65ncryption.proto\x12\nencryption\"\xbf\x01\n\x11\x45ncryptionContext\x12\x12\n\x05model\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x12\n\x05\x66ield\x18\x02 \x01(\tH\x01\x88\x01\x01\x12=\n\x08metadata\x18\x03 \x03(\x0b\x32+.encryption.EncryptionContext.MetadataEntry\x1a/\n\rMetadataEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x0c:\x02\x38\x01\x42\x08\n\x06_modelB\x08\n\x06_field\"R\n\x12\x45ncryptJSONRequest\x12\x0c\n\x04\x64\x61ta\x18\x01 \x01(\x0c\x12.\n\x07\x63ontext\x18\x02 \x01(\x0b\x32\x1d.encryption.EncryptionContext\"^\n\x12\x44\x65\x63ryptJSONRequest\x12\x0c\n\x04\x64\x61ta\x18\x01 \x01(\x0c\x12\x12\n\x05\x66ield\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x12\n\x05model\x18\x03 \x01(\tH\x01\x88\x01\x01\x42\x08\n\x06_fieldB\x08\n\x06_model\"\x1f\n\x0f\x45ncryptResponse\x12\x0c\n\x04\x64\x61ta\x18\x01 \x01(\x0c\"\
|
|
27
|
+
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x10\x65ncryption.proto\x12\nencryption\"\xbf\x01\n\x11\x45ncryptionContext\x12\x12\n\x05model\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x12\n\x05\x66ield\x18\x02 \x01(\tH\x01\x88\x01\x01\x12=\n\x08metadata\x18\x03 \x03(\x0b\x32+.encryption.EncryptionContext.MetadataEntry\x1a/\n\rMetadataEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x0c:\x02\x38\x01\x42\x08\n\x06_modelB\x08\n\x06_field\"R\n\x12\x45ncryptJSONRequest\x12\x0c\n\x04\x64\x61ta\x18\x01 \x01(\x0c\x12.\n\x07\x63ontext\x18\x02 \x01(\x0b\x32\x1d.encryption.EncryptionContext\"^\n\x12\x44\x65\x63ryptJSONRequest\x12\x0c\n\x04\x64\x61ta\x18\x01 \x01(\x0c\x12\x12\n\x05\x66ield\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x12\n\x05model\x18\x03 \x01(\tH\x01\x88\x01\x01\x42\x08\n\x06_fieldB\x08\n\x06_model\"R\n\x12\x45ncryptBlobRequest\x12\x0c\n\x04\x64\x61ta\x18\x01 \x01(\x0c\x12.\n\x07\x63ontext\x18\x02 \x01(\x0b\x32\x1d.encryption.EncryptionContext\"R\n\x12\x44\x65\x63ryptBlobRequest\x12\x0c\n\x04\x64\x61ta\x18\x01 \x01(\x0c\x12.\n\x07\x63ontext\x18\x02 \x01(\x0b\x32\x1d.encryption.EncryptionContext\"\x1f\n\x0f\x45ncryptResponse\x12\x0c\n\x04\x64\x61ta\x18\x01 \x01(\x0c\"4\n\x0f\x44\x65\x63ryptResponse\x12\x0c\n\x04\x64\x61ta\x18\x01 \x01(\x0c\x12\x13\n\x0breplacement\x18\x02 \x01(\x0c\x32\xbc\x02\n\nEncryption\x12J\n\x0b\x45ncryptJSON\x12\x1e.encryption.EncryptJSONRequest\x1a\x1b.encryption.EncryptResponse\x12J\n\x0b\x44\x65\x63ryptJSON\x12\x1e.encryption.DecryptJSONRequest\x1a\x1b.encryption.DecryptResponse\x12J\n\x0b\x45ncryptBlob\x12\x1e.encryption.EncryptBlobRequest\x1a\x1b.encryption.EncryptResponse\x12J\n\x0b\x44\x65\x63ryptBlob\x12\x1e.encryption.DecryptBlobRequest\x1a\x1b.encryption.DecryptResponseBAZ?github.com/langchain-ai/langgraph-api/core/internal/core-api/pbb\x06proto3')
|
|
28
28
|
|
|
29
29
|
_globals = globals()
|
|
30
30
|
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
|
@@ -42,10 +42,14 @@ if not _descriptor._USE_C_DESCRIPTORS:
|
|
|
42
42
|
_globals['_ENCRYPTJSONREQUEST']._serialized_end=308
|
|
43
43
|
_globals['_DECRYPTJSONREQUEST']._serialized_start=310
|
|
44
44
|
_globals['_DECRYPTJSONREQUEST']._serialized_end=404
|
|
45
|
-
_globals['
|
|
46
|
-
_globals['
|
|
47
|
-
_globals['
|
|
48
|
-
_globals['
|
|
49
|
-
_globals['
|
|
50
|
-
_globals['
|
|
45
|
+
_globals['_ENCRYPTBLOBREQUEST']._serialized_start=406
|
|
46
|
+
_globals['_ENCRYPTBLOBREQUEST']._serialized_end=488
|
|
47
|
+
_globals['_DECRYPTBLOBREQUEST']._serialized_start=490
|
|
48
|
+
_globals['_DECRYPTBLOBREQUEST']._serialized_end=572
|
|
49
|
+
_globals['_ENCRYPTRESPONSE']._serialized_start=574
|
|
50
|
+
_globals['_ENCRYPTRESPONSE']._serialized_end=605
|
|
51
|
+
_globals['_DECRYPTRESPONSE']._serialized_start=607
|
|
52
|
+
_globals['_DECRYPTRESPONSE']._serialized_end=659
|
|
53
|
+
_globals['_ENCRYPTION']._serialized_start=662
|
|
54
|
+
_globals['_ENCRYPTION']._serialized_end=978
|
|
51
55
|
# @@protoc_insertion_point(module_scope)
|
|
@@ -148,6 +148,62 @@ class DecryptJSONRequest(_message.Message):
|
|
|
148
148
|
|
|
149
149
|
Global___DecryptJSONRequest: _TypeAlias = DecryptJSONRequest # noqa: Y015
|
|
150
150
|
|
|
151
|
+
@_typing.final
|
|
152
|
+
class EncryptBlobRequest(_message.Message):
|
|
153
|
+
"""Request to encrypt binary data (e.g., checkpoint channel values and writes)."""
|
|
154
|
+
|
|
155
|
+
DESCRIPTOR: _descriptor.Descriptor
|
|
156
|
+
|
|
157
|
+
DATA_FIELD_NUMBER: _builtins.int
|
|
158
|
+
CONTEXT_FIELD_NUMBER: _builtins.int
|
|
159
|
+
data: _builtins.bytes
|
|
160
|
+
"""The plaintext blob bytes"""
|
|
161
|
+
@_builtins.property
|
|
162
|
+
def context(self) -> Global___EncryptionContext:
|
|
163
|
+
"""Encryption context for key derivation"""
|
|
164
|
+
|
|
165
|
+
def __init__(
|
|
166
|
+
self,
|
|
167
|
+
*,
|
|
168
|
+
data: _builtins.bytes = ...,
|
|
169
|
+
context: Global___EncryptionContext | None = ...,
|
|
170
|
+
) -> None: ...
|
|
171
|
+
_HasFieldArgType: _TypeAlias = _typing.Literal["context", b"context"] # noqa: Y015
|
|
172
|
+
def HasField(self, field_name: _HasFieldArgType) -> _builtins.bool: ...
|
|
173
|
+
_ClearFieldArgType: _TypeAlias = _typing.Literal["context", b"context", "data", b"data"] # noqa: Y015
|
|
174
|
+
def ClearField(self, field_name: _ClearFieldArgType) -> None: ...
|
|
175
|
+
def WhichOneof(self, oneof_group: _Never) -> None: ...
|
|
176
|
+
|
|
177
|
+
Global___EncryptBlobRequest: _TypeAlias = EncryptBlobRequest # noqa: Y015
|
|
178
|
+
|
|
179
|
+
@_typing.final
|
|
180
|
+
class DecryptBlobRequest(_message.Message):
|
|
181
|
+
"""Request to decrypt binary data."""
|
|
182
|
+
|
|
183
|
+
DESCRIPTOR: _descriptor.Descriptor
|
|
184
|
+
|
|
185
|
+
DATA_FIELD_NUMBER: _builtins.int
|
|
186
|
+
CONTEXT_FIELD_NUMBER: _builtins.int
|
|
187
|
+
data: _builtins.bytes
|
|
188
|
+
"""The encrypted blob bytes"""
|
|
189
|
+
@_builtins.property
|
|
190
|
+
def context(self) -> Global___EncryptionContext:
|
|
191
|
+
"""Encryption context, recovered from the stored blob wrapper"""
|
|
192
|
+
|
|
193
|
+
def __init__(
|
|
194
|
+
self,
|
|
195
|
+
*,
|
|
196
|
+
data: _builtins.bytes = ...,
|
|
197
|
+
context: Global___EncryptionContext | None = ...,
|
|
198
|
+
) -> None: ...
|
|
199
|
+
_HasFieldArgType: _TypeAlias = _typing.Literal["context", b"context"] # noqa: Y015
|
|
200
|
+
def HasField(self, field_name: _HasFieldArgType) -> _builtins.bool: ...
|
|
201
|
+
_ClearFieldArgType: _TypeAlias = _typing.Literal["context", b"context", "data", b"data"] # noqa: Y015
|
|
202
|
+
def ClearField(self, field_name: _ClearFieldArgType) -> None: ...
|
|
203
|
+
def WhichOneof(self, oneof_group: _Never) -> None: ...
|
|
204
|
+
|
|
205
|
+
Global___DecryptBlobRequest: _TypeAlias = DecryptBlobRequest # noqa: Y015
|
|
206
|
+
|
|
151
207
|
@_typing.final
|
|
152
208
|
class EncryptResponse(_message.Message):
|
|
153
209
|
"""Response from encryption operations."""
|
|
@@ -177,16 +233,20 @@ class DecryptResponse(_message.Message):
|
|
|
177
233
|
DESCRIPTOR: _descriptor.Descriptor
|
|
178
234
|
|
|
179
235
|
DATA_FIELD_NUMBER: _builtins.int
|
|
236
|
+
REPLACEMENT_FIELD_NUMBER: _builtins.int
|
|
180
237
|
data: _builtins.bytes
|
|
181
238
|
"""The decrypted plaintext data as bytes"""
|
|
239
|
+
replacement: _builtins.bytes
|
|
240
|
+
"""Optional replacement ciphertext for the original storage location."""
|
|
182
241
|
def __init__(
|
|
183
242
|
self,
|
|
184
243
|
*,
|
|
185
244
|
data: _builtins.bytes = ...,
|
|
245
|
+
replacement: _builtins.bytes = ...,
|
|
186
246
|
) -> None: ...
|
|
187
247
|
_HasFieldArgType: _TypeAlias = _Never # noqa: Y015
|
|
188
248
|
def HasField(self, field_name: _HasFieldArgType) -> _builtins.bool: ...
|
|
189
|
-
_ClearFieldArgType: _TypeAlias = _typing.Literal["data", b"data"] # noqa: Y015
|
|
249
|
+
_ClearFieldArgType: _TypeAlias = _typing.Literal["data", b"data", "replacement", b"replacement"] # noqa: Y015
|
|
190
250
|
def ClearField(self, field_name: _ClearFieldArgType) -> None: ...
|
|
191
251
|
def WhichOneof(self, oneof_group: _Never) -> None: ...
|
|
192
252
|
|
|
@@ -45,6 +45,16 @@ class EncryptionStub:
|
|
|
45
45
|
request_serializer=encryption__pb2.DecryptJSONRequest.SerializeToString,
|
|
46
46
|
response_deserializer=encryption__pb2.DecryptResponse.FromString,
|
|
47
47
|
_registered_method=True)
|
|
48
|
+
self.EncryptBlob = channel.unary_unary(
|
|
49
|
+
'/encryption.Encryption/EncryptBlob',
|
|
50
|
+
request_serializer=encryption__pb2.EncryptBlobRequest.SerializeToString,
|
|
51
|
+
response_deserializer=encryption__pb2.EncryptResponse.FromString,
|
|
52
|
+
_registered_method=True)
|
|
53
|
+
self.DecryptBlob = channel.unary_unary(
|
|
54
|
+
'/encryption.Encryption/DecryptBlob',
|
|
55
|
+
request_serializer=encryption__pb2.DecryptBlobRequest.SerializeToString,
|
|
56
|
+
response_deserializer=encryption__pb2.DecryptResponse.FromString,
|
|
57
|
+
_registered_method=True)
|
|
48
58
|
|
|
49
59
|
|
|
50
60
|
class EncryptionServicer:
|
|
@@ -65,6 +75,20 @@ class EncryptionServicer:
|
|
|
65
75
|
context.set_details('Method not implemented!')
|
|
66
76
|
raise NotImplementedError('Method not implemented!')
|
|
67
77
|
|
|
78
|
+
def EncryptBlob(self, request, context):
|
|
79
|
+
"""Encrypt blob data (checkpoint channel values and pending writes).
|
|
80
|
+
"""
|
|
81
|
+
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
|
|
82
|
+
context.set_details('Method not implemented!')
|
|
83
|
+
raise NotImplementedError('Method not implemented!')
|
|
84
|
+
|
|
85
|
+
def DecryptBlob(self, request, context):
|
|
86
|
+
"""Decrypt blob data. Context is supplied by the caller from the blob wrapper.
|
|
87
|
+
"""
|
|
88
|
+
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
|
|
89
|
+
context.set_details('Method not implemented!')
|
|
90
|
+
raise NotImplementedError('Method not implemented!')
|
|
91
|
+
|
|
68
92
|
|
|
69
93
|
def add_EncryptionServicer_to_server(servicer, server):
|
|
70
94
|
rpc_method_handlers = {
|
|
@@ -78,6 +102,16 @@ def add_EncryptionServicer_to_server(servicer, server):
|
|
|
78
102
|
request_deserializer=encryption__pb2.DecryptJSONRequest.FromString,
|
|
79
103
|
response_serializer=encryption__pb2.DecryptResponse.SerializeToString,
|
|
80
104
|
),
|
|
105
|
+
'EncryptBlob': grpc.unary_unary_rpc_method_handler(
|
|
106
|
+
servicer.EncryptBlob,
|
|
107
|
+
request_deserializer=encryption__pb2.EncryptBlobRequest.FromString,
|
|
108
|
+
response_serializer=encryption__pb2.EncryptResponse.SerializeToString,
|
|
109
|
+
),
|
|
110
|
+
'DecryptBlob': grpc.unary_unary_rpc_method_handler(
|
|
111
|
+
servicer.DecryptBlob,
|
|
112
|
+
request_deserializer=encryption__pb2.DecryptBlobRequest.FromString,
|
|
113
|
+
response_serializer=encryption__pb2.DecryptResponse.SerializeToString,
|
|
114
|
+
),
|
|
81
115
|
}
|
|
82
116
|
generic_handler = grpc.method_handlers_generic_handler(
|
|
83
117
|
'encryption.Encryption', rpc_method_handlers)
|
|
@@ -143,3 +177,57 @@ class Encryption:
|
|
|
143
177
|
timeout,
|
|
144
178
|
metadata,
|
|
145
179
|
_registered_method=True)
|
|
180
|
+
|
|
181
|
+
@staticmethod
|
|
182
|
+
def EncryptBlob(request,
|
|
183
|
+
target,
|
|
184
|
+
options=(),
|
|
185
|
+
channel_credentials=None,
|
|
186
|
+
call_credentials=None,
|
|
187
|
+
insecure=False,
|
|
188
|
+
compression=None,
|
|
189
|
+
wait_for_ready=None,
|
|
190
|
+
timeout=None,
|
|
191
|
+
metadata=None):
|
|
192
|
+
return grpc.experimental.unary_unary(
|
|
193
|
+
request,
|
|
194
|
+
target,
|
|
195
|
+
'/encryption.Encryption/EncryptBlob',
|
|
196
|
+
encryption__pb2.EncryptBlobRequest.SerializeToString,
|
|
197
|
+
encryption__pb2.EncryptResponse.FromString,
|
|
198
|
+
options,
|
|
199
|
+
channel_credentials,
|
|
200
|
+
insecure,
|
|
201
|
+
call_credentials,
|
|
202
|
+
compression,
|
|
203
|
+
wait_for_ready,
|
|
204
|
+
timeout,
|
|
205
|
+
metadata,
|
|
206
|
+
_registered_method=True)
|
|
207
|
+
|
|
208
|
+
@staticmethod
|
|
209
|
+
def DecryptBlob(request,
|
|
210
|
+
target,
|
|
211
|
+
options=(),
|
|
212
|
+
channel_credentials=None,
|
|
213
|
+
call_credentials=None,
|
|
214
|
+
insecure=False,
|
|
215
|
+
compression=None,
|
|
216
|
+
wait_for_ready=None,
|
|
217
|
+
timeout=None,
|
|
218
|
+
metadata=None):
|
|
219
|
+
return grpc.experimental.unary_unary(
|
|
220
|
+
request,
|
|
221
|
+
target,
|
|
222
|
+
'/encryption.Encryption/DecryptBlob',
|
|
223
|
+
encryption__pb2.DecryptBlobRequest.SerializeToString,
|
|
224
|
+
encryption__pb2.DecryptResponse.FromString,
|
|
225
|
+
options,
|
|
226
|
+
channel_credentials,
|
|
227
|
+
insecure,
|
|
228
|
+
call_credentials,
|
|
229
|
+
compression,
|
|
230
|
+
wait_for_ready,
|
|
231
|
+
timeout,
|
|
232
|
+
metadata,
|
|
233
|
+
_registered_method=True)
|
|
@@ -37,6 +37,10 @@ class EncryptionStub:
|
|
|
37
37
|
"""Encrypt JSON data (e.g., thread metadata, run kwargs)."""
|
|
38
38
|
DecryptJSON: _grpc.UnaryUnaryMultiCallable[_encryption_pb2.DecryptJSONRequest, _encryption_pb2.DecryptResponse]
|
|
39
39
|
"""Decrypt JSON data. Context is extracted from __encryption_context__ in the data."""
|
|
40
|
+
EncryptBlob: _grpc.UnaryUnaryMultiCallable[_encryption_pb2.EncryptBlobRequest, _encryption_pb2.EncryptResponse]
|
|
41
|
+
"""Encrypt blob data (checkpoint channel values and pending writes)."""
|
|
42
|
+
DecryptBlob: _grpc.UnaryUnaryMultiCallable[_encryption_pb2.DecryptBlobRequest, _encryption_pb2.DecryptResponse]
|
|
43
|
+
"""Decrypt blob data. Context is supplied by the caller from the blob wrapper."""
|
|
40
44
|
|
|
41
45
|
@_typing.type_check_only
|
|
42
46
|
class EncryptionAsyncStub(EncryptionStub):
|
|
@@ -47,6 +51,10 @@ class EncryptionAsyncStub(EncryptionStub):
|
|
|
47
51
|
"""Encrypt JSON data (e.g., thread metadata, run kwargs)."""
|
|
48
52
|
DecryptJSON: _aio.UnaryUnaryMultiCallable[_encryption_pb2.DecryptJSONRequest, _encryption_pb2.DecryptResponse] # type: ignore[assignment]
|
|
49
53
|
"""Decrypt JSON data. Context is extracted from __encryption_context__ in the data."""
|
|
54
|
+
EncryptBlob: _aio.UnaryUnaryMultiCallable[_encryption_pb2.EncryptBlobRequest, _encryption_pb2.EncryptResponse] # type: ignore[assignment]
|
|
55
|
+
"""Encrypt blob data (checkpoint channel values and pending writes)."""
|
|
56
|
+
DecryptBlob: _aio.UnaryUnaryMultiCallable[_encryption_pb2.DecryptBlobRequest, _encryption_pb2.DecryptResponse] # type: ignore[assignment]
|
|
57
|
+
"""Decrypt blob data. Context is supplied by the caller from the blob wrapper."""
|
|
50
58
|
|
|
51
59
|
class EncryptionServicer(metaclass=_abc_1.ABCMeta):
|
|
52
60
|
"""Encryption service for custom encryption implementations."""
|
|
@@ -67,4 +75,20 @@ class EncryptionServicer(metaclass=_abc_1.ABCMeta):
|
|
|
67
75
|
) -> _typing.Union[_encryption_pb2.DecryptResponse, _abc.Awaitable[_encryption_pb2.DecryptResponse]]:
|
|
68
76
|
"""Decrypt JSON data. Context is extracted from __encryption_context__ in the data."""
|
|
69
77
|
|
|
78
|
+
@_abc_1.abstractmethod
|
|
79
|
+
def EncryptBlob(
|
|
80
|
+
self,
|
|
81
|
+
request: _encryption_pb2.EncryptBlobRequest,
|
|
82
|
+
context: _ServicerContext,
|
|
83
|
+
) -> _typing.Union[_encryption_pb2.EncryptResponse, _abc.Awaitable[_encryption_pb2.EncryptResponse]]:
|
|
84
|
+
"""Encrypt blob data (checkpoint channel values and pending writes)."""
|
|
85
|
+
|
|
86
|
+
@_abc_1.abstractmethod
|
|
87
|
+
def DecryptBlob(
|
|
88
|
+
self,
|
|
89
|
+
request: _encryption_pb2.DecryptBlobRequest,
|
|
90
|
+
context: _ServicerContext,
|
|
91
|
+
) -> _typing.Union[_encryption_pb2.DecryptResponse, _abc.Awaitable[_encryption_pb2.DecryptResponse]]:
|
|
92
|
+
"""Decrypt blob data. Context is supplied by the caller from the blob wrapper."""
|
|
93
|
+
|
|
70
94
|
def add_EncryptionServicer_to_server(servicer: EncryptionServicer, server: _typing.Union[_grpc.Server, _aio.Server]) -> None: ...
|
|
File without changes
|
{langgraph_api-0.14.0.dev4.dist-info → langgraph_api-0.14.0.dev6.dist-info}/entry_points.txt
RENAMED
|
File without changes
|
{langgraph_api-0.14.0.dev4.dist-info → langgraph_api-0.14.0.dev6.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|