langgraph-api 0.2.125__tar.gz → 0.2.128__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.
Potentially problematic release.
This version of langgraph-api might be problematic. Click here for more details.
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/Makefile +3 -3
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/PKG-INFO +1 -1
- langgraph_api-0.2.128/langgraph_api/__init__.py +1 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/config.py +1 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/middleware/http_logger.py +17 -4
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/models/run.py +55 -64
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/stream.py +12 -3
- langgraph_api-0.2.128/langgraph_api/utils/headers.py +72 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/worker.py +10 -4
- langgraph_api-0.2.125/langgraph_api/__init__.py +0 -1
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/.gitignore +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/LICENSE +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/README.md +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/benchmark/.gitignore +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/benchmark/Makefile +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/benchmark/README.md +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/benchmark/burst.js +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/benchmark/ramp.js +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/benchmark/weather.js +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/constraints.txt +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/forbidden.txt +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/healthcheck.py +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/api/__init__.py +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/api/assistants.py +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/api/mcp.py +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/api/meta.py +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/api/openapi.py +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/api/runs.py +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/api/store.py +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/api/threads.py +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/api/ui.py +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/asgi_transport.py +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/asyncio.py +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/auth/__init__.py +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/auth/custom.py +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/auth/langsmith/__init__.py +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/auth/langsmith/backend.py +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/auth/langsmith/client.py +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/auth/middleware.py +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/auth/noop.py +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/auth/studio_user.py +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/cli.py +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/command.py +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/cron_scheduler.py +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/errors.py +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/feature_flags.py +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/graph.py +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/http.py +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/http_metrics.py +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/js/.gitignore +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/js/.prettierrc +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/js/__init__.py +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/js/base.py +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/js/build.mts +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/js/client.http.mts +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/js/client.mts +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/js/errors.py +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/js/global.d.ts +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/js/package.json +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/js/remote.py +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/js/schema.py +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/js/src/graph.mts +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/js/src/load.hooks.mjs +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/js/src/preload.mjs +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/js/src/utils/files.mts +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/js/src/utils/importMap.mts +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/js/src/utils/pythonSchemas.mts +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/js/src/utils/serde.mts +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/js/sse.py +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/js/traceblock.mts +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/js/tsconfig.json +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/js/ui.py +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/js/yarn.lock +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/logging.py +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/metadata.py +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/middleware/__init__.py +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/middleware/private_network.py +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/middleware/request_id.py +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/models/__init__.py +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/patch.py +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/queue_entrypoint.py +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/route.py +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/schema.py +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/serde.py +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/server.py +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/sse.py +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/state.py +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/store.py +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/thread_ttl.py +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/traceblock.py +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/tunneling/cloudflare.py +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/utils/__init__.py +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/utils/cache.py +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/utils/config.py +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/utils/future.py +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/utils.py +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/validation.py +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/webhook.py +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_license/__init__.py +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_license/validation.py +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_runtime/__init__.py +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_runtime/checkpoint.py +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_runtime/database.py +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_runtime/lifespan.py +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_runtime/metrics.py +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_runtime/ops.py +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_runtime/queue.py +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_runtime/retry.py +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_runtime/store.py +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/logging.json +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/openapi.json +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/pyproject.toml +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/scripts/create_license.py +0 -0
- {langgraph_api-0.2.125 → langgraph_api-0.2.128}/uv.lock +0 -0
|
@@ -54,7 +54,7 @@ start:
|
|
|
54
54
|
LANGGRAPH_RUNTIME_EDITION=inmem \
|
|
55
55
|
LANGGRAPH_AES_KEY='$(LANGGRAPH_AES_KEY)' \
|
|
56
56
|
N_JOBS_PER_WORKER=2 \
|
|
57
|
-
LANGSERVE_GRAPHS='{"agent": "./tests/graphs/agent.py:graph", "custom_lifespan": "./tests/graphs/my_router.py:graph", "single_node": "./tests/graphs/single_node.py:graph", "benchmark": "./tests/graphs/benchmark.py:graph", "other": "./tests/graphs/other.py:make_graph", "weather": "./tests/graphs/weather.py:mk_weather_graph", "searchy": "./tests/graphs/searchy.py:graph", "agent_simple": "./tests/graphs/agent_simple.py:graph", "simple_runtime": "./tests/graphs/simple_runtime.py:graph"}' \
|
|
57
|
+
LANGSERVE_GRAPHS='{"agent": "./tests/graphs/agent.py:graph", "custom_lifespan": "./tests/graphs/my_router.py:graph", "single_node": "./tests/graphs/single_node.py:graph", "benchmark": "./tests/graphs/benchmark.py:graph", "config_graph": "./tests/graphs/config_graph.py:graph", "other": "./tests/graphs/other.py:make_graph", "weather": "./tests/graphs/weather.py:mk_weather_graph", "searchy": "./tests/graphs/searchy.py:graph", "agent_simple": "./tests/graphs/agent_simple.py:graph", "simple_runtime": "./tests/graphs/simple_runtime.py:graph"}' \
|
|
58
58
|
LANGGRAPH_STORE='$(STORE_CONFIG)' \
|
|
59
59
|
LANGGRAPH_CONFIG='{"agent": {"configurable": {"model_name": "openai"}}}' \
|
|
60
60
|
LANGSMITH_LANGGRAPH_API_VARIANT=test \
|
|
@@ -77,7 +77,7 @@ start-auth-jwt:
|
|
|
77
77
|
LANGGRAPH_RUNTIME_EDITION=inmem LANGGRAPH_HTTP='$(HTTP_CONFIG)' \
|
|
78
78
|
LANGGRAPH_AES_KEY='$(LANGGRAPH_AES_KEY)' \
|
|
79
79
|
N_JOBS_PER_WORKER=2 \
|
|
80
|
-
LANGSERVE_GRAPHS='{"agent": "./tests/graphs/agent.py:graph", "other": "./tests/graphs/other.py:make_graph", "weather": "./tests/graphs/weather.py:mk_weather_graph", "searchy": "./tests/graphs/searchy.py:graph", "agent_simple": "./tests/graphs/agent_simple.py:graph", "simple_runtime": "./tests/graphs/simple_runtime.py:graph"}' \
|
|
80
|
+
LANGSERVE_GRAPHS='{"agent": "./tests/graphs/agent.py:graph", "config_graph": "./tests/graphs/config_graph.py:graph", "other": "./tests/graphs/other.py:make_graph", "weather": "./tests/graphs/weather.py:mk_weather_graph", "searchy": "./tests/graphs/searchy.py:graph", "agent_simple": "./tests/graphs/agent_simple.py:graph", "simple_runtime": "./tests/graphs/simple_runtime.py:graph"}' \
|
|
81
81
|
LANGGRAPH_STORE='$(STORE_CONFIG)' \
|
|
82
82
|
LANGGRAPH_AUTH='{"path": "tests/graphs/jwt_auth.py:auth"}' \
|
|
83
83
|
LANGSMITH_LANGGRAPH_API_VARIANT=test \
|
|
@@ -95,7 +95,7 @@ start-auth-jwt:
|
|
|
95
95
|
start-auth-fastapi-jwt:
|
|
96
96
|
LANGGRAPH_RUNTIME_EDITION=inmem LANGGRAPH_HTTP='$(HTTP_CONFIG)' \
|
|
97
97
|
N_JOBS_PER_WORKER=2 \
|
|
98
|
-
LANGSERVE_GRAPHS='{"agent": "./tests/graphs/agent.py:graph", "other": "./tests/graphs/other.py:make_graph", "weather": "./tests/graphs/weather.py:mk_weather_graph", "searchy": "./tests/graphs/searchy.py:graph", "agent_simple": "./tests/graphs/agent_simple.py:graph", "simple_runtime": "./tests/graphs/simple_runtime.py:graph"}' \
|
|
98
|
+
LANGSERVE_GRAPHS='{"agent": "./tests/graphs/agent.py:graph", "config_graph": "./tests/graphs/config_graph.py:graph", "other": "./tests/graphs/other.py:make_graph", "weather": "./tests/graphs/weather.py:mk_weather_graph", "searchy": "./tests/graphs/searchy.py:graph", "agent_simple": "./tests/graphs/agent_simple.py:graph", "simple_runtime": "./tests/graphs/simple_runtime.py:graph"}' \
|
|
99
99
|
LANGGRAPH_STORE='$(STORE_CONFIG)' \
|
|
100
100
|
LANGGRAPH_AUTH='{"path": "./tests/graphs/fastapi_jwt_auth.py:auth"}' \
|
|
101
101
|
LANGSMITH_LANGGRAPH_API_VARIANT=test \
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.2.128"
|
|
@@ -180,6 +180,7 @@ REDIS_MAX_CONNECTIONS = env("REDIS_MAX_CONNECTIONS", cast=int, default=2000)
|
|
|
180
180
|
REDIS_CONNECT_TIMEOUT = env("REDIS_CONNECT_TIMEOUT", cast=float, default=10.0)
|
|
181
181
|
REDIS_MAX_IDLE_TIME = env("REDIS_MAX_IDLE_TIME", cast=float, default=120.0)
|
|
182
182
|
REDIS_KEY_PREFIX = env("REDIS_KEY_PREFIX", cast=str, default="")
|
|
183
|
+
RUN_STATS_CACHE_SECONDS = env("RUN_STATS_CACHE_SECONDS", cast=int, default=60)
|
|
183
184
|
|
|
184
185
|
# server
|
|
185
186
|
ALLOW_PRIVATE_NETWORK = env("ALLOW_PRIVATE_NETWORK", cast=bool, default=False)
|
|
@@ -6,6 +6,7 @@ from starlette.requests import ClientDisconnect
|
|
|
6
6
|
from starlette.types import Message, Receive, Scope, Send
|
|
7
7
|
|
|
8
8
|
from langgraph_api.http_metrics import HTTP_METRICS_COLLECTOR
|
|
9
|
+
from langgraph_api.utils.headers import should_include_header
|
|
9
10
|
|
|
10
11
|
asgi = structlog.stdlib.get_logger("asgi")
|
|
11
12
|
|
|
@@ -99,12 +100,24 @@ class AccessLoggerMiddleware:
|
|
|
99
100
|
)
|
|
100
101
|
|
|
101
102
|
|
|
102
|
-
|
|
103
|
+
IGNORE_HEADERS = {
|
|
104
|
+
b"authorization",
|
|
105
|
+
b"cookie",
|
|
106
|
+
b"set-cookie",
|
|
107
|
+
b"x-api-key",
|
|
108
|
+
}
|
|
103
109
|
|
|
104
110
|
|
|
105
111
|
def _headers_to_dict(headers: list[tuple[bytes, bytes]] | None) -> dict[str, str]:
|
|
106
112
|
if headers is None:
|
|
107
113
|
return {}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
114
|
+
|
|
115
|
+
result = {}
|
|
116
|
+
for k, v in headers:
|
|
117
|
+
if k in IGNORE_HEADERS:
|
|
118
|
+
continue
|
|
119
|
+
key = k.decode()
|
|
120
|
+
if should_include_header(key):
|
|
121
|
+
result[key] = v.decode()
|
|
122
|
+
|
|
123
|
+
return result
|
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
-
import functools
|
|
3
|
-
import re
|
|
4
2
|
import time
|
|
5
3
|
import urllib.parse
|
|
6
4
|
import uuid
|
|
@@ -28,6 +26,7 @@ from langgraph_api.schema import (
|
|
|
28
26
|
StreamMode,
|
|
29
27
|
)
|
|
30
28
|
from langgraph_api.utils import AsyncConnectionProto, get_auth_ctx
|
|
29
|
+
from langgraph_api.utils.headers import should_include_header
|
|
31
30
|
from langgraph_runtime.ops import Runs, logger
|
|
32
31
|
|
|
33
32
|
|
|
@@ -180,82 +179,61 @@ LANGSMITH_TAGS = "langsmith-tags"
|
|
|
180
179
|
LANGSMITH_PROJECT = "langsmith-project"
|
|
181
180
|
|
|
182
181
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
res = []
|
|
186
|
-
i = 0
|
|
187
|
-
n = len(pat)
|
|
188
|
-
|
|
189
|
-
while i < n:
|
|
190
|
-
c = pat[i]
|
|
191
|
-
i += 1
|
|
192
|
-
|
|
193
|
-
if c == "*":
|
|
194
|
-
res.append(".*")
|
|
195
|
-
else:
|
|
196
|
-
res.append(re.escape(c))
|
|
197
|
-
|
|
198
|
-
pattern = "".join(res)
|
|
199
|
-
return re.compile(rf"(?s:{pattern})\Z")
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
@functools.lru_cache(maxsize=1)
|
|
203
|
-
def get_header_patterns() -> tuple[
|
|
204
|
-
list[re.Pattern[str] | None], list[re.Pattern[str] | None]
|
|
205
|
-
]:
|
|
206
|
-
from langgraph_api import config
|
|
207
|
-
|
|
208
|
-
if not config.HTTP_CONFIG:
|
|
209
|
-
return None, None
|
|
210
|
-
configurable = config.HTTP_CONFIG.get("configurable_headers")
|
|
211
|
-
if not configurable:
|
|
212
|
-
return None, None
|
|
213
|
-
header_includes = configurable.get("includes") or configurable.get("include") or []
|
|
214
|
-
include_patterns = []
|
|
215
|
-
for include in header_includes:
|
|
216
|
-
include_patterns.append(translate_pattern(include))
|
|
217
|
-
header_excludes = configurable.get("excludes") or configurable.get("exclude") or []
|
|
218
|
-
exclude_patterns = []
|
|
219
|
-
for exclude in header_excludes:
|
|
220
|
-
exclude_patterns.append(translate_pattern(exclude))
|
|
221
|
-
return include_patterns, exclude_patterns
|
|
182
|
+
# Default headers to exclude from run configuration for security
|
|
183
|
+
DEFAULT_RUN_HEADERS_EXCLUDE = {"x-api-key", "x-tenant-id", "x-service-key"}
|
|
222
184
|
|
|
223
185
|
|
|
224
186
|
def get_configurable_headers(headers: dict[str, str]) -> dict[str, str]:
|
|
187
|
+
"""Extract headers that should be added to run configuration.
|
|
188
|
+
|
|
189
|
+
This function handles special cases like langsmith-trace and baggage headers,
|
|
190
|
+
while respecting the configurable header patterns.
|
|
191
|
+
"""
|
|
225
192
|
configurable = {}
|
|
226
|
-
|
|
193
|
+
|
|
227
194
|
for key, value in headers.items():
|
|
228
|
-
# First handle tracing stuff
|
|
195
|
+
# First handle tracing stuff - always included regardless of patterns
|
|
229
196
|
if key == "langsmith-trace":
|
|
230
197
|
configurable[key] = value
|
|
231
198
|
if baggage := headers.get("baggage"):
|
|
232
199
|
for item in baggage.split(","):
|
|
233
|
-
|
|
234
|
-
if
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
200
|
+
baggage_key, baggage_value = item.split("=")
|
|
201
|
+
if (
|
|
202
|
+
baggage_key == LANGSMITH_METADATA
|
|
203
|
+
and baggage_key not in configurable
|
|
204
|
+
):
|
|
205
|
+
configurable[baggage_key] = orjson.loads(
|
|
206
|
+
urllib.parse.unquote(baggage_value)
|
|
207
|
+
)
|
|
208
|
+
elif baggage_key == LANGSMITH_TAGS:
|
|
209
|
+
configurable[baggage_key] = urllib.parse.unquote(
|
|
210
|
+
baggage_value
|
|
211
|
+
).split(",")
|
|
212
|
+
elif baggage_key == LANGSMITH_PROJECT:
|
|
213
|
+
configurable[baggage_key] = urllib.parse.unquote(baggage_value)
|
|
245
214
|
continue
|
|
246
215
|
|
|
247
|
-
#
|
|
216
|
+
# Check if header should be included based on patterns
|
|
217
|
+
# For run configuration, we have specific default behavior for x-* headers
|
|
248
218
|
if key.startswith("x-"):
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
219
|
+
# Check against default excludes for x-* headers
|
|
220
|
+
if key in DEFAULT_RUN_HEADERS_EXCLUDE:
|
|
221
|
+
# Check if explicitly included via patterns
|
|
222
|
+
if should_include_header(key):
|
|
223
|
+
configurable[key] = value
|
|
254
224
|
continue
|
|
255
|
-
|
|
256
|
-
|
|
225
|
+
# Other x-* headers are included by default unless patterns exclude them
|
|
226
|
+
if should_include_header(key):
|
|
227
|
+
configurable[key] = value
|
|
257
228
|
elif key == "user-agent":
|
|
258
|
-
|
|
229
|
+
# user-agent is included by default unless excluded by patterns
|
|
230
|
+
if should_include_header(key):
|
|
231
|
+
configurable[key] = value
|
|
232
|
+
else:
|
|
233
|
+
# All other headers only included if patterns allow
|
|
234
|
+
if should_include_header(key):
|
|
235
|
+
configurable[key] = value
|
|
236
|
+
|
|
259
237
|
return configurable
|
|
260
238
|
|
|
261
239
|
|
|
@@ -297,6 +275,13 @@ async def create_valid_run(
|
|
|
297
275
|
config = payload.get("config") or {}
|
|
298
276
|
context = payload.get("context") or {}
|
|
299
277
|
configurable = config.setdefault("configurable", {})
|
|
278
|
+
|
|
279
|
+
if configurable and context:
|
|
280
|
+
raise HTTPException(
|
|
281
|
+
status_code=400,
|
|
282
|
+
detail="Cannot specify both configurable and context. Prefer setting context alone. Context was introduced in LangGraph 0.6.0 and is the long term planned replacement for configurable.",
|
|
283
|
+
)
|
|
284
|
+
|
|
300
285
|
if checkpoint_id:
|
|
301
286
|
configurable["checkpoint_id"] = str(checkpoint_id)
|
|
302
287
|
if checkpoint := payload.get("checkpoint"):
|
|
@@ -322,6 +307,12 @@ async def create_valid_run(
|
|
|
322
307
|
configurable["__after_seconds__"] = after_seconds
|
|
323
308
|
put_time_start = time.time()
|
|
324
309
|
if_not_exists = payload.get("if_not_exists", "reject")
|
|
310
|
+
|
|
311
|
+
# Keep config and context in sync
|
|
312
|
+
# Configurable is either A) just internal config or B) internal config + user config (and context is empty). Either way, configurable is the default.
|
|
313
|
+
context = {**context, **configurable}
|
|
314
|
+
config["configurable"] = context
|
|
315
|
+
|
|
325
316
|
run_coro = Runs.put(
|
|
326
317
|
conn,
|
|
327
318
|
assistant_id,
|
|
@@ -348,7 +348,17 @@ async def astream_state(
|
|
|
348
348
|
yield "feedback", feedback_urls
|
|
349
349
|
|
|
350
350
|
|
|
351
|
-
async def consume(
|
|
351
|
+
async def consume(
|
|
352
|
+
stream: AnyStream,
|
|
353
|
+
run_id: str,
|
|
354
|
+
resumable: bool = False,
|
|
355
|
+
stream_modes: set[StreamMode] | None = None,
|
|
356
|
+
) -> None:
|
|
357
|
+
stream_modes = stream_modes or set()
|
|
358
|
+
if "messages-tuple" in stream_modes:
|
|
359
|
+
stream_modes.add("messages")
|
|
360
|
+
stream_modes.add("metadata")
|
|
361
|
+
|
|
352
362
|
async with aclosing(stream):
|
|
353
363
|
try:
|
|
354
364
|
async for mode, payload in stream:
|
|
@@ -356,7 +366,7 @@ async def consume(stream: AnyStream, run_id: str, resumable: bool = False) -> No
|
|
|
356
366
|
run_id,
|
|
357
367
|
mode,
|
|
358
368
|
await run_in_executor(None, json_dumpb, payload),
|
|
359
|
-
resumable=resumable,
|
|
369
|
+
resumable=resumable and mode.split("|")[0] in stream_modes,
|
|
360
370
|
)
|
|
361
371
|
except Exception as e:
|
|
362
372
|
if isinstance(e, ExceptionGroup):
|
|
@@ -365,7 +375,6 @@ async def consume(stream: AnyStream, run_id: str, resumable: bool = False) -> No
|
|
|
365
375
|
run_id,
|
|
366
376
|
"error",
|
|
367
377
|
await run_in_executor(None, json_dumpb, e),
|
|
368
|
-
resumable=resumable,
|
|
369
378
|
)
|
|
370
379
|
raise e
|
|
371
380
|
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"""Shared utilities for configurable header filtering."""
|
|
2
|
+
|
|
3
|
+
import functools
|
|
4
|
+
import re
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def translate_pattern(pat: str) -> re.Pattern[str]:
|
|
8
|
+
"""Translate a pattern to regex, supporting only literals and * wildcards to avoid RE DoS."""
|
|
9
|
+
res = []
|
|
10
|
+
i = 0
|
|
11
|
+
n = len(pat)
|
|
12
|
+
|
|
13
|
+
while i < n:
|
|
14
|
+
c = pat[i]
|
|
15
|
+
i += 1
|
|
16
|
+
|
|
17
|
+
if c == "*":
|
|
18
|
+
res.append(".*")
|
|
19
|
+
else:
|
|
20
|
+
res.append(re.escape(c))
|
|
21
|
+
|
|
22
|
+
pattern = "".join(res)
|
|
23
|
+
return re.compile(rf"(?s:{pattern})\Z")
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@functools.lru_cache(maxsize=1)
|
|
27
|
+
def get_header_patterns() -> tuple[
|
|
28
|
+
list[re.Pattern[str]] | None, list[re.Pattern[str]] | None
|
|
29
|
+
]:
|
|
30
|
+
"""Get the configured header include/exclude patterns."""
|
|
31
|
+
from langgraph_api import config
|
|
32
|
+
|
|
33
|
+
if not config.HTTP_CONFIG:
|
|
34
|
+
return None, None
|
|
35
|
+
configurable = config.HTTP_CONFIG.get("configurable_headers")
|
|
36
|
+
if not configurable:
|
|
37
|
+
return None, None
|
|
38
|
+
header_includes = configurable.get("includes") or configurable.get("include") or []
|
|
39
|
+
include_patterns = []
|
|
40
|
+
for include in header_includes:
|
|
41
|
+
include_patterns.append(translate_pattern(include))
|
|
42
|
+
header_excludes = configurable.get("excludes") or configurable.get("exclude") or []
|
|
43
|
+
exclude_patterns = []
|
|
44
|
+
for exclude in header_excludes:
|
|
45
|
+
exclude_patterns.append(translate_pattern(exclude))
|
|
46
|
+
return include_patterns or None, exclude_patterns or None
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@functools.lru_cache(maxsize=512)
|
|
50
|
+
def should_include_header(key: str) -> bool:
|
|
51
|
+
"""Check if a header should be included based on cached patterns.
|
|
52
|
+
|
|
53
|
+
This function uses cached patterns from get_header_patterns() and
|
|
54
|
+
provides efficient header filtering.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
key: The header key to check
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
True if the header should be included, False otherwise
|
|
61
|
+
"""
|
|
62
|
+
include_patterns, exclude_patterns = get_header_patterns()
|
|
63
|
+
|
|
64
|
+
# Handle configurable behavior
|
|
65
|
+
if exclude_patterns and any(pattern.match(key) for pattern in exclude_patterns):
|
|
66
|
+
return False
|
|
67
|
+
if include_patterns:
|
|
68
|
+
# If include patterns are specified, only include headers matching them
|
|
69
|
+
return any(pattern.match(key) for pattern in include_patterns)
|
|
70
|
+
|
|
71
|
+
# Default behavior - include if not excluded
|
|
72
|
+
return True
|
|
@@ -20,7 +20,7 @@ from langgraph_api.config import (
|
|
|
20
20
|
from langgraph_api.errors import UserInterrupt, UserRollback, UserTimeout
|
|
21
21
|
from langgraph_api.js.errors import RemoteException
|
|
22
22
|
from langgraph_api.metadata import incr_runs
|
|
23
|
-
from langgraph_api.schema import Run
|
|
23
|
+
from langgraph_api.schema import Run, StreamMode
|
|
24
24
|
from langgraph_api.state import state_snapshot_to_thread_state
|
|
25
25
|
from langgraph_api.stream import AnyStream, astream_state, consume
|
|
26
26
|
from langgraph_api.utils import with_user
|
|
@@ -54,6 +54,7 @@ async def set_auth_ctx_for_run(
|
|
|
54
54
|
user = normalize_user(user)
|
|
55
55
|
# Reapply normalization to the kwargs
|
|
56
56
|
run_kwargs["config"]["configurable"]["langgraph_auth_user"] = user
|
|
57
|
+
run_kwargs["context"]["langgraph_auth_user"] = user
|
|
57
58
|
except Exception:
|
|
58
59
|
user = SimpleUser(user_id) if user_id is not None else None
|
|
59
60
|
permissions = None
|
|
@@ -130,9 +131,11 @@ async def worker(
|
|
|
130
131
|
break
|
|
131
132
|
|
|
132
133
|
# Wrap the graph execution to separate user errors from server errors
|
|
133
|
-
async def wrap_user_errors(
|
|
134
|
+
async def wrap_user_errors(
|
|
135
|
+
stream: AnyStream, run_id: str, resumable: bool, stream_modes: set[StreamMode]
|
|
136
|
+
):
|
|
134
137
|
try:
|
|
135
|
-
await consume(stream, run_id, resumable)
|
|
138
|
+
await consume(stream, run_id, resumable, stream_modes)
|
|
136
139
|
except Exception as e:
|
|
137
140
|
if not isinstance(e, UserRollback | UserInterrupt):
|
|
138
141
|
logger.error(
|
|
@@ -184,8 +187,11 @@ async def worker(
|
|
|
184
187
|
on_checkpoint=on_checkpoint,
|
|
185
188
|
on_task_result=on_task_result,
|
|
186
189
|
)
|
|
190
|
+
stream_modes: set[StreamMode] = set(
|
|
191
|
+
run["kwargs"].get("stream_mode", [])
|
|
192
|
+
)
|
|
187
193
|
await asyncio.wait_for(
|
|
188
|
-
wrap_user_errors(stream, run_id, resumable),
|
|
194
|
+
wrap_user_errors(stream, run_id, resumable, stream_modes),
|
|
189
195
|
BG_JOB_TIMEOUT_SECS,
|
|
190
196
|
)
|
|
191
197
|
except (Exception, asyncio.CancelledError) as ee:
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.2.125"
|
|
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
|
|
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
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{langgraph_api-0.2.125 → langgraph_api-0.2.128}/langgraph_api/js/src/utils/pythonSchemas.mts
RENAMED
|
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
|
|
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
|
|
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
|
|
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
|