langgraph-api 0.4.1__py3-none-any.whl → 0.7.3__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/api/__init__.py +111 -51
- langgraph_api/api/a2a.py +1610 -0
- langgraph_api/api/assistants.py +212 -89
- langgraph_api/api/mcp.py +3 -3
- langgraph_api/api/meta.py +52 -28
- langgraph_api/api/openapi.py +27 -17
- langgraph_api/api/profile.py +108 -0
- langgraph_api/api/runs.py +342 -195
- langgraph_api/api/store.py +19 -2
- langgraph_api/api/threads.py +209 -27
- langgraph_api/asgi_transport.py +14 -9
- langgraph_api/asyncio.py +14 -4
- langgraph_api/auth/custom.py +52 -37
- langgraph_api/auth/langsmith/backend.py +4 -3
- langgraph_api/auth/langsmith/client.py +13 -8
- langgraph_api/cli.py +230 -133
- langgraph_api/command.py +5 -3
- langgraph_api/config/__init__.py +532 -0
- langgraph_api/config/_parse.py +58 -0
- langgraph_api/config/schemas.py +431 -0
- langgraph_api/cron_scheduler.py +17 -1
- langgraph_api/encryption/__init__.py +15 -0
- langgraph_api/encryption/aes_json.py +158 -0
- langgraph_api/encryption/context.py +35 -0
- langgraph_api/encryption/custom.py +280 -0
- langgraph_api/encryption/middleware.py +632 -0
- langgraph_api/encryption/shared.py +63 -0
- langgraph_api/errors.py +12 -1
- langgraph_api/executor_entrypoint.py +11 -6
- langgraph_api/feature_flags.py +29 -0
- langgraph_api/graph.py +176 -76
- langgraph_api/grpc/client.py +313 -0
- langgraph_api/grpc/config_conversion.py +231 -0
- langgraph_api/grpc/generated/__init__.py +29 -0
- langgraph_api/grpc/generated/checkpointer_pb2.py +63 -0
- langgraph_api/grpc/generated/checkpointer_pb2.pyi +99 -0
- langgraph_api/grpc/generated/checkpointer_pb2_grpc.py +329 -0
- langgraph_api/grpc/generated/core_api_pb2.py +216 -0
- langgraph_api/grpc/generated/core_api_pb2.pyi +905 -0
- langgraph_api/grpc/generated/core_api_pb2_grpc.py +1621 -0
- langgraph_api/grpc/generated/engine_common_pb2.py +219 -0
- langgraph_api/grpc/generated/engine_common_pb2.pyi +722 -0
- langgraph_api/grpc/generated/engine_common_pb2_grpc.py +24 -0
- langgraph_api/grpc/generated/enum_cancel_run_action_pb2.py +37 -0
- langgraph_api/grpc/generated/enum_cancel_run_action_pb2.pyi +12 -0
- langgraph_api/grpc/generated/enum_cancel_run_action_pb2_grpc.py +24 -0
- langgraph_api/grpc/generated/enum_control_signal_pb2.py +37 -0
- langgraph_api/grpc/generated/enum_control_signal_pb2.pyi +16 -0
- langgraph_api/grpc/generated/enum_control_signal_pb2_grpc.py +24 -0
- langgraph_api/grpc/generated/enum_durability_pb2.py +37 -0
- langgraph_api/grpc/generated/enum_durability_pb2.pyi +16 -0
- langgraph_api/grpc/generated/enum_durability_pb2_grpc.py +24 -0
- langgraph_api/grpc/generated/enum_multitask_strategy_pb2.py +37 -0
- langgraph_api/grpc/generated/enum_multitask_strategy_pb2.pyi +16 -0
- langgraph_api/grpc/generated/enum_multitask_strategy_pb2_grpc.py +24 -0
- langgraph_api/grpc/generated/enum_run_status_pb2.py +37 -0
- langgraph_api/grpc/generated/enum_run_status_pb2.pyi +22 -0
- langgraph_api/grpc/generated/enum_run_status_pb2_grpc.py +24 -0
- langgraph_api/grpc/generated/enum_stream_mode_pb2.py +37 -0
- langgraph_api/grpc/generated/enum_stream_mode_pb2.pyi +28 -0
- langgraph_api/grpc/generated/enum_stream_mode_pb2_grpc.py +24 -0
- langgraph_api/grpc/generated/enum_thread_status_pb2.py +37 -0
- langgraph_api/grpc/generated/enum_thread_status_pb2.pyi +16 -0
- langgraph_api/grpc/generated/enum_thread_status_pb2_grpc.py +24 -0
- langgraph_api/grpc/generated/enum_thread_stream_mode_pb2.py +37 -0
- langgraph_api/grpc/generated/enum_thread_stream_mode_pb2.pyi +16 -0
- langgraph_api/grpc/generated/enum_thread_stream_mode_pb2_grpc.py +24 -0
- langgraph_api/grpc/generated/errors_pb2.py +39 -0
- langgraph_api/grpc/generated/errors_pb2.pyi +21 -0
- langgraph_api/grpc/generated/errors_pb2_grpc.py +24 -0
- langgraph_api/grpc/ops/__init__.py +370 -0
- langgraph_api/grpc/ops/assistants.py +424 -0
- langgraph_api/grpc/ops/runs.py +792 -0
- langgraph_api/grpc/ops/threads.py +1013 -0
- langgraph_api/http.py +16 -5
- langgraph_api/http_metrics.py +15 -35
- langgraph_api/http_metrics_utils.py +38 -0
- langgraph_api/js/build.mts +1 -1
- langgraph_api/js/client.http.mts +13 -7
- langgraph_api/js/client.mts +2 -5
- langgraph_api/js/package.json +29 -28
- langgraph_api/js/remote.py +56 -30
- langgraph_api/js/src/graph.mts +20 -0
- langgraph_api/js/sse.py +2 -2
- langgraph_api/js/ui.py +1 -1
- langgraph_api/js/yarn.lock +1204 -1006
- langgraph_api/logging.py +29 -2
- langgraph_api/metadata.py +99 -28
- langgraph_api/middleware/http_logger.py +7 -2
- langgraph_api/middleware/private_network.py +7 -7
- langgraph_api/models/run.py +54 -93
- langgraph_api/otel_context.py +205 -0
- langgraph_api/patch.py +5 -3
- langgraph_api/queue_entrypoint.py +154 -65
- langgraph_api/route.py +47 -5
- langgraph_api/schema.py +88 -10
- langgraph_api/self_hosted_logs.py +124 -0
- langgraph_api/self_hosted_metrics.py +450 -0
- langgraph_api/serde.py +79 -37
- langgraph_api/server.py +138 -60
- langgraph_api/state.py +4 -3
- langgraph_api/store.py +25 -16
- langgraph_api/stream.py +80 -29
- langgraph_api/thread_ttl.py +31 -13
- langgraph_api/timing/__init__.py +25 -0
- langgraph_api/timing/profiler.py +200 -0
- langgraph_api/timing/timer.py +318 -0
- langgraph_api/utils/__init__.py +53 -8
- langgraph_api/utils/cache.py +47 -10
- langgraph_api/utils/config.py +2 -1
- langgraph_api/utils/errors.py +77 -0
- langgraph_api/utils/future.py +10 -6
- langgraph_api/utils/headers.py +76 -2
- langgraph_api/utils/retriable_client.py +74 -0
- langgraph_api/utils/stream_codec.py +315 -0
- langgraph_api/utils/uuids.py +29 -62
- langgraph_api/validation.py +9 -0
- langgraph_api/webhook.py +120 -6
- langgraph_api/worker.py +55 -24
- {langgraph_api-0.4.1.dist-info → langgraph_api-0.7.3.dist-info}/METADATA +16 -8
- langgraph_api-0.7.3.dist-info/RECORD +168 -0
- {langgraph_api-0.4.1.dist-info → langgraph_api-0.7.3.dist-info}/WHEEL +1 -1
- langgraph_runtime/__init__.py +1 -0
- langgraph_runtime/routes.py +11 -0
- logging.json +1 -3
- openapi.json +839 -478
- langgraph_api/config.py +0 -387
- langgraph_api/js/isolate-0x130008000-46649-46649-v8.log +0 -4430
- langgraph_api/js/isolate-0x138008000-44681-44681-v8.log +0 -4430
- langgraph_api/js/package-lock.json +0 -3308
- langgraph_api-0.4.1.dist-info/RECORD +0 -107
- /langgraph_api/{utils.py → grpc/__init__.py} +0 -0
- {langgraph_api-0.4.1.dist-info → langgraph_api-0.7.3.dist-info}/entry_points.txt +0 -0
- {langgraph_api-0.4.1.dist-info → langgraph_api-0.7.3.dist-info}/licenses/LICENSE +0 -0
langgraph_api/auth/custom.py
CHANGED
|
@@ -23,10 +23,12 @@ from starlette.exceptions import HTTPException
|
|
|
23
23
|
from starlette.requests import HTTPConnection, Request
|
|
24
24
|
from starlette.responses import Response
|
|
25
25
|
|
|
26
|
+
from langgraph_api import timing
|
|
26
27
|
from langgraph_api.auth.langsmith.backend import LangsmithAuthBackend
|
|
27
28
|
from langgraph_api.auth.studio_user import StudioUser
|
|
28
29
|
from langgraph_api.config import LANGGRAPH_AUTH, LANGGRAPH_AUTH_TYPE
|
|
29
30
|
from langgraph_api.js.base import is_js_path
|
|
31
|
+
from langgraph_api.timing import profiled_import
|
|
30
32
|
|
|
31
33
|
logger = structlog.stdlib.get_logger(__name__)
|
|
32
34
|
|
|
@@ -233,7 +235,7 @@ def _get_custom_auth_middleware(
|
|
|
233
235
|
auth_instance._authenticate_handler,
|
|
234
236
|
disable_studio_auth,
|
|
235
237
|
)
|
|
236
|
-
logger.info(f"Loaded custom auth middleware: {
|
|
238
|
+
logger.info(f"Loaded custom auth middleware: {result!s}")
|
|
237
239
|
return result
|
|
238
240
|
|
|
239
241
|
|
|
@@ -355,34 +357,39 @@ def _solve_fastapi_dependencies(
|
|
|
355
357
|
}
|
|
356
358
|
|
|
357
359
|
async def decorator(scope: dict, request: Request):
|
|
358
|
-
async with AsyncExitStack() as
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
360
|
+
async with AsyncExitStack() as request_stack:
|
|
361
|
+
scope["fastapi_inner_astack"] = request_stack
|
|
362
|
+
async with AsyncExitStack() as stack:
|
|
363
|
+
scope["fastapi_function_astack"] = stack
|
|
364
|
+
all_solved = await asyncio.gather(
|
|
365
|
+
*(
|
|
366
|
+
solve_dependencies(
|
|
367
|
+
request=request,
|
|
368
|
+
dependant=dependent,
|
|
369
|
+
async_exit_stack=stack,
|
|
370
|
+
embed_body_fields=False,
|
|
371
|
+
)
|
|
372
|
+
for dependent in dependents.values()
|
|
366
373
|
)
|
|
367
|
-
for dependent in dependents.values()
|
|
368
374
|
)
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
+
all_injected = await asyncio.gather(
|
|
376
|
+
*(
|
|
377
|
+
_run_async(dependent.call, solved.values, is_async)
|
|
378
|
+
for dependent, solved in zip(
|
|
379
|
+
dependents.values(), all_solved, strict=False
|
|
380
|
+
)
|
|
375
381
|
)
|
|
376
382
|
)
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
383
|
+
kwargs = {
|
|
384
|
+
name: value
|
|
385
|
+
for name, value in zip(
|
|
386
|
+
dependents.keys(), all_injected, strict=False
|
|
387
|
+
)
|
|
388
|
+
}
|
|
389
|
+
other_params = _extract_arguments_from_scope(
|
|
390
|
+
scope, _param_names, request=request
|
|
391
|
+
)
|
|
392
|
+
return await fn(**(kwargs | other_params))
|
|
386
393
|
|
|
387
394
|
return decorator
|
|
388
395
|
|
|
@@ -580,6 +587,13 @@ def normalize_user(user: Any) -> BaseUser:
|
|
|
580
587
|
)
|
|
581
588
|
|
|
582
589
|
|
|
590
|
+
@timing.timer(
|
|
591
|
+
message="Loading custom auth {auth_path}",
|
|
592
|
+
metadata_fn=lambda auth_path: {"auth_path": auth_path},
|
|
593
|
+
warn_threshold_secs=5,
|
|
594
|
+
warn_message="Loading custom auth '{auth_path}' took longer than expected",
|
|
595
|
+
error_threshold_secs=10,
|
|
596
|
+
)
|
|
583
597
|
def _load_auth_obj(path: str) -> Auth | Literal["js"]:
|
|
584
598
|
"""Load an object from a path string."""
|
|
585
599
|
if ":" not in path:
|
|
@@ -595,18 +609,19 @@ def _load_auth_obj(path: str) -> Auth | Literal["js"]:
|
|
|
595
609
|
return "js"
|
|
596
610
|
|
|
597
611
|
try:
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
612
|
+
with profiled_import(path):
|
|
613
|
+
if "/" in module_name or ".py" in module_name:
|
|
614
|
+
# Load from file path
|
|
615
|
+
modname = f"dynamic_module_{hash(module_name)}"
|
|
616
|
+
modspec = importlib.util.spec_from_file_location(modname, module_name)
|
|
617
|
+
if modspec is None or modspec.loader is None:
|
|
618
|
+
raise ValueError(f"Could not load file: {module_name}")
|
|
619
|
+
module = importlib.util.module_from_spec(modspec)
|
|
620
|
+
sys.modules[modname] = module
|
|
621
|
+
modspec.loader.exec_module(module) # type: ignore[possibly-unbound-attribute]
|
|
622
|
+
else:
|
|
623
|
+
# Load from Python module
|
|
624
|
+
module = importlib.import_module(module_name)
|
|
610
625
|
|
|
611
626
|
loaded_auth = getattr(module, callable_name, None)
|
|
612
627
|
if loaded_auth is None:
|
|
@@ -30,10 +30,11 @@ class AuthCacheEntry(TypedDict):
|
|
|
30
30
|
|
|
31
31
|
|
|
32
32
|
class LangsmithAuthBackend(AuthenticationBackend):
|
|
33
|
-
def __init__(self):
|
|
33
|
+
def __init__(self, *, base_url: str | None = None):
|
|
34
34
|
from langgraph_api.utils.cache import LRUCache
|
|
35
35
|
|
|
36
36
|
self._cache = LRUCache[AuthCacheEntry](max_size=1000, ttl=60)
|
|
37
|
+
self._base_url = base_url
|
|
37
38
|
|
|
38
39
|
def _get_cache_key(self, headers):
|
|
39
40
|
"""Generate cache key from authentication headers"""
|
|
@@ -58,10 +59,10 @@ class LangsmithAuthBackend(AuthenticationBackend):
|
|
|
58
59
|
|
|
59
60
|
# Check cache first
|
|
60
61
|
cache_key = self._get_cache_key(headers)
|
|
61
|
-
if cached_entry := self._cache.get(cache_key):
|
|
62
|
+
if cached_entry := await self._cache.get(cache_key):
|
|
62
63
|
return cached_entry["credentials"], cached_entry["user"]
|
|
63
64
|
|
|
64
|
-
async with auth_client() as auth:
|
|
65
|
+
async with auth_client(base_url=self._base_url) as auth:
|
|
65
66
|
if not LANGSMITH_AUTH_VERIFY_TENANT_ID and not conn.headers.get(
|
|
66
67
|
"x-api-key"
|
|
67
68
|
):
|
|
@@ -85,8 +85,10 @@ class JsonHttpClient:
|
|
|
85
85
|
)
|
|
86
86
|
|
|
87
87
|
|
|
88
|
-
def create_client() -> JsonHttpClient:
|
|
88
|
+
def create_client(base_url: str | None = None) -> JsonHttpClient:
|
|
89
89
|
"""Create the auth http client."""
|
|
90
|
+
url = base_url if base_url is not None else LANGSMITH_AUTH_ENDPOINT
|
|
91
|
+
|
|
90
92
|
return JsonHttpClient(
|
|
91
93
|
httpx.AsyncClient(
|
|
92
94
|
transport=httpx.AsyncHTTPTransport(
|
|
@@ -97,7 +99,7 @@ def create_client() -> JsonHttpClient:
|
|
|
97
99
|
),
|
|
98
100
|
),
|
|
99
101
|
timeout=httpx.Timeout(2.0),
|
|
100
|
-
base_url=
|
|
102
|
+
base_url=url,
|
|
101
103
|
)
|
|
102
104
|
)
|
|
103
105
|
|
|
@@ -109,20 +111,23 @@ async def close_auth_client() -> None:
|
|
|
109
111
|
await _client.client.aclose()
|
|
110
112
|
|
|
111
113
|
|
|
112
|
-
async def initialize_auth_client() -> None:
|
|
114
|
+
async def initialize_auth_client(base_url: str | None = None) -> None:
|
|
113
115
|
"""Initialize the auth http client."""
|
|
114
116
|
await close_auth_client()
|
|
115
117
|
global _client
|
|
116
|
-
_client = create_client()
|
|
118
|
+
_client = create_client(base_url=base_url)
|
|
117
119
|
|
|
118
120
|
|
|
119
121
|
@asynccontextmanager
|
|
120
|
-
async def auth_client(
|
|
122
|
+
async def auth_client(
|
|
123
|
+
base_url: str | None = None,
|
|
124
|
+
) -> AsyncGenerator[JsonHttpClient, None]:
|
|
121
125
|
"""Get the auth http client."""
|
|
126
|
+
url = base_url if base_url is not None else LANGSMITH_AUTH_ENDPOINT
|
|
122
127
|
# pytest does something funny with event loops,
|
|
123
128
|
# so we can't use a global pool for tests
|
|
124
|
-
if
|
|
125
|
-
client = create_client()
|
|
129
|
+
if url.startswith("http://localhost"):
|
|
130
|
+
client = create_client(base_url=url)
|
|
126
131
|
try:
|
|
127
132
|
yield client
|
|
128
133
|
finally:
|
|
@@ -135,5 +140,5 @@ async def auth_client() -> AsyncGenerator[JsonHttpClient, None]:
|
|
|
135
140
|
if found:
|
|
136
141
|
yield _client
|
|
137
142
|
else:
|
|
138
|
-
await initialize_auth_client()
|
|
143
|
+
await initialize_auth_client(base_url=url)
|
|
139
144
|
yield _client
|