langgraph-api 0.4.7__py3-none-any.whl → 0.4.11__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.
Potentially problematic release.
This version of langgraph-api might be problematic. Click here for more details.
- langgraph_api/__init__.py +1 -1
- langgraph_api/api/__init__.py +4 -0
- langgraph_api/api/a2a.py +1128 -0
- langgraph_api/api/assistants.py +8 -0
- langgraph_api/api/threads.py +47 -14
- langgraph_api/command.py +4 -2
- langgraph_api/graph.py +3 -3
- langgraph_api/js/remote.py +15 -11
- langgraph_api/models/run.py +2 -67
- langgraph_api/state.py +1 -1
- langgraph_api/thread_ttl.py +4 -1
- langgraph_api/utils/headers.py +68 -2
- {langgraph_api-0.4.7.dist-info → langgraph_api-0.4.11.dist-info}/METADATA +2 -2
- {langgraph_api-0.4.7.dist-info → langgraph_api-0.4.11.dist-info}/RECORD +18 -21
- openapi.json +219 -1
- 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/utils.py +0 -0
- {langgraph_api-0.4.7.dist-info → langgraph_api-0.4.11.dist-info}/WHEEL +0 -0
- {langgraph_api-0.4.7.dist-info → langgraph_api-0.4.11.dist-info}/entry_points.txt +0 -0
- {langgraph_api-0.4.7.dist-info → langgraph_api-0.4.11.dist-info}/licenses/LICENSE +0 -0
langgraph_api/api/assistants.py
CHANGED
|
@@ -24,6 +24,7 @@ from langgraph_api.utils import (
|
|
|
24
24
|
validate_select_columns,
|
|
25
25
|
validate_uuid,
|
|
26
26
|
)
|
|
27
|
+
from langgraph_api.utils.headers import get_configurable_headers
|
|
27
28
|
from langgraph_api.validation import (
|
|
28
29
|
AssistantCountRequest,
|
|
29
30
|
AssistantCreate,
|
|
@@ -240,6 +241,9 @@ async def get_assistant_graph(
|
|
|
240
241
|
assistant_ = await Assistants.get(conn, assistant_id)
|
|
241
242
|
assistant = await fetchone(assistant_)
|
|
242
243
|
config = await ajson_loads(assistant["config"])
|
|
244
|
+
configurable = config.setdefault("configurable", {})
|
|
245
|
+
configurable.update(get_configurable_headers(request.headers))
|
|
246
|
+
|
|
243
247
|
async with get_graph(
|
|
244
248
|
assistant["graph_id"],
|
|
245
249
|
config,
|
|
@@ -294,6 +298,8 @@ async def get_assistant_subgraphs(
|
|
|
294
298
|
assistant_ = await Assistants.get(conn, assistant_id)
|
|
295
299
|
assistant = await fetchone(assistant_)
|
|
296
300
|
config = await ajson_loads(assistant["config"])
|
|
301
|
+
configurable = config.setdefault("configurable", {})
|
|
302
|
+
configurable.update(get_configurable_headers(request.headers))
|
|
297
303
|
async with get_graph(
|
|
298
304
|
assistant["graph_id"],
|
|
299
305
|
config,
|
|
@@ -340,6 +346,8 @@ async def get_assistant_schemas(
|
|
|
340
346
|
# TODO Implementa cache so we can de-dent and release this connection.
|
|
341
347
|
assistant = await fetchone(assistant_)
|
|
342
348
|
config = await ajson_loads(assistant["config"])
|
|
349
|
+
configurable = config.setdefault("configurable", {})
|
|
350
|
+
configurable.update(get_configurable_headers(request.headers))
|
|
343
351
|
async with get_graph(
|
|
344
352
|
assistant["graph_id"],
|
|
345
353
|
config,
|
langgraph_api/api/threads.py
CHANGED
|
@@ -16,6 +16,7 @@ from langgraph_api.utils import (
|
|
|
16
16
|
validate_stream_id,
|
|
17
17
|
validate_uuid,
|
|
18
18
|
)
|
|
19
|
+
from langgraph_api.utils.headers import get_configurable_headers
|
|
19
20
|
from langgraph_api.validation import (
|
|
20
21
|
ThreadCountRequest,
|
|
21
22
|
ThreadCreate,
|
|
@@ -47,12 +48,17 @@ async def create_thread(
|
|
|
47
48
|
if_exists=payload.get("if_exists") or "raise",
|
|
48
49
|
ttl=payload.get("ttl"),
|
|
49
50
|
)
|
|
50
|
-
|
|
51
|
+
config = {
|
|
52
|
+
"configurable": {
|
|
53
|
+
**get_configurable_headers(request.headers),
|
|
54
|
+
"thread_id": thread_id,
|
|
55
|
+
}
|
|
56
|
+
}
|
|
51
57
|
if supersteps := payload.get("supersteps"):
|
|
52
58
|
try:
|
|
53
59
|
await Threads.State.bulk(
|
|
54
60
|
conn,
|
|
55
|
-
config=
|
|
61
|
+
config=config,
|
|
56
62
|
supersteps=supersteps,
|
|
57
63
|
)
|
|
58
64
|
except HTTPException as e:
|
|
@@ -77,6 +83,7 @@ async def search_threads(
|
|
|
77
83
|
status=payload.get("status"),
|
|
78
84
|
values=payload.get("values"),
|
|
79
85
|
metadata=payload.get("metadata"),
|
|
86
|
+
ids=payload.get("ids"),
|
|
80
87
|
limit=limit,
|
|
81
88
|
offset=offset,
|
|
82
89
|
sort_by=payload.get("sort_by"),
|
|
@@ -114,10 +121,14 @@ async def get_thread_state(
|
|
|
114
121
|
validate_uuid(thread_id, "Invalid thread ID: must be a UUID")
|
|
115
122
|
subgraphs = request.query_params.get("subgraphs") in ("true", "True")
|
|
116
123
|
async with connect() as conn:
|
|
124
|
+
config = {
|
|
125
|
+
"configurable": {
|
|
126
|
+
**get_configurable_headers(request.headers),
|
|
127
|
+
"thread_id": thread_id,
|
|
128
|
+
}
|
|
129
|
+
}
|
|
117
130
|
state = state_snapshot_to_thread_state(
|
|
118
|
-
await Threads.State.get(
|
|
119
|
-
conn, {"configurable": {"thread_id": thread_id}}, subgraphs=subgraphs
|
|
120
|
-
)
|
|
131
|
+
await Threads.State.get(conn, config=config, subgraphs=subgraphs)
|
|
121
132
|
)
|
|
122
133
|
return ApiResponse(state)
|
|
123
134
|
|
|
@@ -131,15 +142,17 @@ async def get_thread_state_at_checkpoint(
|
|
|
131
142
|
validate_uuid(thread_id, "Invalid thread ID: must be a UUID")
|
|
132
143
|
checkpoint_id = request.path_params["checkpoint_id"]
|
|
133
144
|
async with connect() as conn:
|
|
145
|
+
config = {
|
|
146
|
+
"configurable": {
|
|
147
|
+
**get_configurable_headers(request.headers),
|
|
148
|
+
"thread_id": thread_id,
|
|
149
|
+
"checkpoint_id": checkpoint_id,
|
|
150
|
+
}
|
|
151
|
+
}
|
|
134
152
|
state = state_snapshot_to_thread_state(
|
|
135
153
|
await Threads.State.get(
|
|
136
154
|
conn,
|
|
137
|
-
|
|
138
|
-
"configurable": {
|
|
139
|
-
"thread_id": thread_id,
|
|
140
|
-
"checkpoint_id": checkpoint_id,
|
|
141
|
-
}
|
|
142
|
-
},
|
|
155
|
+
config=config,
|
|
143
156
|
subgraphs=request.query_params.get("subgraphs") in ("true", "True"),
|
|
144
157
|
)
|
|
145
158
|
)
|
|
@@ -155,10 +168,17 @@ async def get_thread_state_at_checkpoint_post(
|
|
|
155
168
|
validate_uuid(thread_id, "Invalid thread ID: must be a UUID")
|
|
156
169
|
payload = await request.json(ThreadStateCheckpointRequest)
|
|
157
170
|
async with connect() as conn:
|
|
171
|
+
config = {
|
|
172
|
+
"configurable": {
|
|
173
|
+
**payload["checkpoint"],
|
|
174
|
+
**get_configurable_headers(request.headers),
|
|
175
|
+
"thread_id": thread_id,
|
|
176
|
+
}
|
|
177
|
+
}
|
|
158
178
|
state = state_snapshot_to_thread_state(
|
|
159
179
|
await Threads.State.get(
|
|
160
180
|
conn,
|
|
161
|
-
|
|
181
|
+
config=config,
|
|
162
182
|
subgraphs=payload.get("subgraphs", False),
|
|
163
183
|
)
|
|
164
184
|
)
|
|
@@ -183,6 +203,7 @@ async def update_thread_state(
|
|
|
183
203
|
config["configurable"]["user_id"] = user_id
|
|
184
204
|
except AssertionError:
|
|
185
205
|
pass
|
|
206
|
+
config["configurable"].update(get_configurable_headers(request.headers))
|
|
186
207
|
async with connect() as conn:
|
|
187
208
|
inserted = await Threads.State.post(
|
|
188
209
|
conn,
|
|
@@ -206,7 +227,13 @@ async def get_thread_history(
|
|
|
206
227
|
except ValueError:
|
|
207
228
|
raise HTTPException(status_code=422, detail=f"Invalid limit {limit_}") from None
|
|
208
229
|
before = request.query_params.get("before")
|
|
209
|
-
config = {
|
|
230
|
+
config = {
|
|
231
|
+
"configurable": {
|
|
232
|
+
"thread_id": thread_id,
|
|
233
|
+
"checkpoint_ns": "",
|
|
234
|
+
**get_configurable_headers(request.headers),
|
|
235
|
+
}
|
|
236
|
+
}
|
|
210
237
|
async with connect() as conn:
|
|
211
238
|
states = [
|
|
212
239
|
state_snapshot_to_thread_state(c)
|
|
@@ -227,6 +254,7 @@ async def get_thread_history_post(
|
|
|
227
254
|
payload = await request.json(ThreadStateSearch)
|
|
228
255
|
config = {"configurable": {"thread_id": thread_id, "checkpoint_ns": ""}}
|
|
229
256
|
config["configurable"].update(payload.get("checkpoint", {}))
|
|
257
|
+
config["configurable"].update(get_configurable_headers(request.headers))
|
|
230
258
|
async with connect() as conn:
|
|
231
259
|
states = [
|
|
232
260
|
state_snapshot_to_thread_state(c)
|
|
@@ -262,7 +290,12 @@ async def patch_thread(
|
|
|
262
290
|
validate_uuid(thread_id, "Invalid thread ID: must be a UUID")
|
|
263
291
|
payload = await request.json(ThreadPatch)
|
|
264
292
|
async with connect() as conn:
|
|
265
|
-
thread = await Threads.patch(
|
|
293
|
+
thread = await Threads.patch(
|
|
294
|
+
conn,
|
|
295
|
+
thread_id,
|
|
296
|
+
metadata=payload.get("metadata", {}),
|
|
297
|
+
ttl=payload.get("ttl"),
|
|
298
|
+
)
|
|
266
299
|
return ApiResponse(await fetchone(thread))
|
|
267
300
|
|
|
268
301
|
|
langgraph_api/command.py
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from typing import cast
|
|
2
|
+
|
|
1
3
|
from langgraph.types import Command, Send
|
|
2
4
|
|
|
3
5
|
from langgraph_api.schema import RunCommand
|
|
@@ -11,9 +13,9 @@ def map_cmd(cmd: RunCommand) -> Command:
|
|
|
11
13
|
update = cmd.get("update")
|
|
12
14
|
if isinstance(update, tuple | list) and all(
|
|
13
15
|
isinstance(t, tuple | list) and len(t) == 2 and isinstance(t[0], str)
|
|
14
|
-
for t in update
|
|
16
|
+
for t in cast(list, update)
|
|
15
17
|
):
|
|
16
|
-
update = [tuple(t) for t in update]
|
|
18
|
+
update = [tuple(t) for t in cast(list, update)]
|
|
17
19
|
|
|
18
20
|
return Command(
|
|
19
21
|
update=update,
|
langgraph_api/graph.py
CHANGED
|
@@ -392,14 +392,14 @@ async def collect_graphs_from_env(register: bool = False) -> None:
|
|
|
392
392
|
|
|
393
393
|
if (
|
|
394
394
|
config.HTTP_CONFIG
|
|
395
|
-
and config.HTTP_CONFIG.get("app")
|
|
396
|
-
and is_js_path(
|
|
395
|
+
and (js_app := config.HTTP_CONFIG.get("app"))
|
|
396
|
+
and is_js_path(js_app.split(":")[0])
|
|
397
397
|
):
|
|
398
398
|
js_bg_tasks.add(
|
|
399
399
|
asyncio.create_task(
|
|
400
400
|
run_js_http_process(
|
|
401
401
|
paths_str,
|
|
402
|
-
config.HTTP_CONFIG
|
|
402
|
+
config.HTTP_CONFIG or {},
|
|
403
403
|
watch="--reload" in sys.argv[1:],
|
|
404
404
|
),
|
|
405
405
|
)
|
langgraph_api/js/remote.py
CHANGED
|
@@ -153,9 +153,9 @@ class RemotePregel(BaseRemotePregel):
|
|
|
153
153
|
|
|
154
154
|
async for event in _client_stream("streamEvents", data):
|
|
155
155
|
if event["event"] == "on_custom_event":
|
|
156
|
-
yield CustomStreamEvent(**event)
|
|
156
|
+
yield CustomStreamEvent(**event) # type: ignore[missing-typed-dict-key]
|
|
157
157
|
else:
|
|
158
|
-
yield StandardStreamEvent(**event)
|
|
158
|
+
yield StandardStreamEvent(**event) # type: ignore[missing-typed-dict-key]
|
|
159
159
|
|
|
160
160
|
async def fetch_state_schema(self):
|
|
161
161
|
return await _client_invoke("getSchema", {"graph_id": self.graph_id})
|
|
@@ -187,15 +187,17 @@ class RemotePregel(BaseRemotePregel):
|
|
|
187
187
|
)
|
|
188
188
|
for data in nodes
|
|
189
189
|
},
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
190
|
+
list(
|
|
191
|
+
{
|
|
192
|
+
Edge(
|
|
193
|
+
data["source"],
|
|
194
|
+
data["target"],
|
|
195
|
+
data.get("data"),
|
|
196
|
+
data.get("conditional", False),
|
|
197
|
+
)
|
|
198
|
+
for data in edges
|
|
199
|
+
}
|
|
200
|
+
),
|
|
199
201
|
)
|
|
200
202
|
|
|
201
203
|
async def fetch_subgraphs(
|
|
@@ -861,6 +863,8 @@ class CustomJsAuthBackend(AuthenticationBackend):
|
|
|
861
863
|
self.ls_auth = LangsmithAuthBackend()
|
|
862
864
|
self.ttl_cache: LRUCache | None = None
|
|
863
865
|
self.cache_keys: list[str] | None = None
|
|
866
|
+
if LANGGRAPH_AUTH is None:
|
|
867
|
+
raise ValueError("LANGGRAPH_AUTH is not set")
|
|
864
868
|
if cache := LANGGRAPH_AUTH.get("cache"):
|
|
865
869
|
keys = cache.get("cache_keys", [])
|
|
866
870
|
if not isinstance(keys, list):
|
langgraph_api/models/run.py
CHANGED
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import contextlib
|
|
3
3
|
import time
|
|
4
|
-
import urllib.parse
|
|
5
4
|
import uuid
|
|
6
5
|
from collections.abc import Mapping, Sequence
|
|
7
6
|
from typing import Any, NamedTuple, cast
|
|
8
7
|
from uuid import UUID
|
|
9
8
|
|
|
10
|
-
import orjson
|
|
11
9
|
import structlog
|
|
12
10
|
from starlette.authentication import BaseUser
|
|
13
11
|
from starlette.exceptions import HTTPException
|
|
@@ -27,7 +25,7 @@ from langgraph_api.schema import (
|
|
|
27
25
|
StreamMode,
|
|
28
26
|
)
|
|
29
27
|
from langgraph_api.utils import AsyncConnectionProto, get_auth_ctx
|
|
30
|
-
from langgraph_api.utils.headers import
|
|
28
|
+
from langgraph_api.utils.headers import get_configurable_headers
|
|
31
29
|
from langgraph_api.utils.uuids import uuid7
|
|
32
30
|
from langgraph_runtime.ops import Runs
|
|
33
31
|
|
|
@@ -180,69 +178,6 @@ def get_user_id(user: BaseUser | None) -> str | None:
|
|
|
180
178
|
pass
|
|
181
179
|
|
|
182
180
|
|
|
183
|
-
LANGSMITH_METADATA = "langsmith-metadata"
|
|
184
|
-
LANGSMITH_TAGS = "langsmith-tags"
|
|
185
|
-
LANGSMITH_PROJECT = "langsmith-project"
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
# Default headers to exclude from run configuration for security
|
|
189
|
-
DEFAULT_RUN_HEADERS_EXCLUDE = {"x-api-key", "x-tenant-id", "x-service-key"}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
def get_configurable_headers(headers: Mapping[str, str]) -> dict[str, str]:
|
|
193
|
-
"""Extract headers that should be added to run configuration.
|
|
194
|
-
|
|
195
|
-
This function handles special cases like langsmith-trace and baggage headers,
|
|
196
|
-
while respecting the configurable header patterns.
|
|
197
|
-
"""
|
|
198
|
-
configurable = {}
|
|
199
|
-
|
|
200
|
-
for key, value in headers.items():
|
|
201
|
-
# First handle tracing stuff - always included regardless of patterns
|
|
202
|
-
if key == "langsmith-trace":
|
|
203
|
-
configurable[key] = value
|
|
204
|
-
if baggage := headers.get("baggage"):
|
|
205
|
-
for item in baggage.split(","):
|
|
206
|
-
baggage_key, baggage_value = item.split("=")
|
|
207
|
-
if (
|
|
208
|
-
baggage_key == LANGSMITH_METADATA
|
|
209
|
-
and baggage_key not in configurable
|
|
210
|
-
):
|
|
211
|
-
configurable[baggage_key] = orjson.loads(
|
|
212
|
-
urllib.parse.unquote(baggage_value)
|
|
213
|
-
)
|
|
214
|
-
elif baggage_key == LANGSMITH_TAGS:
|
|
215
|
-
configurable[baggage_key] = urllib.parse.unquote(
|
|
216
|
-
baggage_value
|
|
217
|
-
).split(",")
|
|
218
|
-
elif baggage_key == LANGSMITH_PROJECT:
|
|
219
|
-
configurable[baggage_key] = urllib.parse.unquote(baggage_value)
|
|
220
|
-
continue
|
|
221
|
-
|
|
222
|
-
# Check if header should be included based on patterns
|
|
223
|
-
# For run configuration, we have specific default behavior for x-* headers
|
|
224
|
-
if key.startswith("x-"):
|
|
225
|
-
# Check against default excludes for x-* headers
|
|
226
|
-
if key in DEFAULT_RUN_HEADERS_EXCLUDE:
|
|
227
|
-
# Check if explicitly included via patterns
|
|
228
|
-
if should_include_header(key):
|
|
229
|
-
configurable[key] = value
|
|
230
|
-
continue
|
|
231
|
-
# Other x-* headers are included by default unless patterns exclude them
|
|
232
|
-
if should_include_header(key):
|
|
233
|
-
configurable[key] = value
|
|
234
|
-
elif key == "user-agent":
|
|
235
|
-
# user-agent is included by default unless excluded by patterns
|
|
236
|
-
if should_include_header(key):
|
|
237
|
-
configurable[key] = value
|
|
238
|
-
else:
|
|
239
|
-
# All other headers only included if patterns allow
|
|
240
|
-
if should_include_header(key):
|
|
241
|
-
configurable[key] = value
|
|
242
|
-
|
|
243
|
-
return configurable
|
|
244
|
-
|
|
245
|
-
|
|
246
181
|
async def create_valid_run(
|
|
247
182
|
conn: AsyncConnectionProto,
|
|
248
183
|
thread_id: str | None,
|
|
@@ -319,7 +254,7 @@ async def create_valid_run(
|
|
|
319
254
|
configurable["__langsmith_example_id__"] = ls_tracing.get("example_id")
|
|
320
255
|
if request_start_time:
|
|
321
256
|
configurable["__request_start_time_ms__"] = request_start_time
|
|
322
|
-
after_seconds = payload.get("after_seconds", 0)
|
|
257
|
+
after_seconds = cast(int, payload.get("after_seconds", 0))
|
|
323
258
|
configurable["__after_seconds__"] = after_seconds
|
|
324
259
|
put_time_start = time.time()
|
|
325
260
|
if_not_exists = payload.get("if_not_exists", "reject")
|
langgraph_api/state.py
CHANGED
|
@@ -27,7 +27,7 @@ def runnable_config_to_checkpoint(
|
|
|
27
27
|
return None
|
|
28
28
|
|
|
29
29
|
configurable = config["configurable"]
|
|
30
|
-
checkpoint: Checkpoint = {
|
|
30
|
+
checkpoint: Checkpoint = { # type: ignore[typed-dict-item]
|
|
31
31
|
"checkpoint_id": configurable["checkpoint_id"],
|
|
32
32
|
"thread_id": configurable["thread_id"],
|
|
33
33
|
}
|
langgraph_api/thread_ttl.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""Sweeping logic for cleaning up expired threads and checkpoints."""
|
|
2
2
|
|
|
3
3
|
import asyncio
|
|
4
|
+
from typing import cast
|
|
4
5
|
|
|
5
6
|
import structlog
|
|
6
7
|
|
|
@@ -23,7 +24,9 @@ async def thread_ttl_sweep_loop():
|
|
|
23
24
|
raise NotImplementedError(
|
|
24
25
|
f"Unrecognized thread deletion strategy: {strategy}. Expected 'delete'."
|
|
25
26
|
)
|
|
26
|
-
sweep_interval_minutes =
|
|
27
|
+
sweep_interval_minutes = cast(
|
|
28
|
+
int, thread_ttl_config.get("sweep_interval_minutes", 5)
|
|
29
|
+
)
|
|
27
30
|
await logger.ainfo(
|
|
28
31
|
f"Starting thread TTL sweeper with interval {sweep_interval_minutes} minutes",
|
|
29
32
|
strategy=strategy,
|
langgraph_api/utils/headers.py
CHANGED
|
@@ -2,6 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
import functools
|
|
4
4
|
import re
|
|
5
|
+
import urllib.parse
|
|
6
|
+
from collections.abc import Mapping
|
|
7
|
+
|
|
8
|
+
import orjson
|
|
9
|
+
|
|
10
|
+
LANGSMITH_METADATA = "langsmith-metadata"
|
|
11
|
+
LANGSMITH_TAGS = "langsmith-tags"
|
|
12
|
+
LANGSMITH_PROJECT = "langsmith-project"
|
|
13
|
+
# For security, don't include these in configuration
|
|
14
|
+
DEFAULT_RUN_HEADERS_EXCLUDE = {"x-api-key", "x-tenant-id", "x-service-key"}
|
|
5
15
|
|
|
6
16
|
|
|
7
17
|
def translate_pattern(pat: str) -> re.Pattern[str]:
|
|
@@ -23,6 +33,62 @@ def translate_pattern(pat: str) -> re.Pattern[str]:
|
|
|
23
33
|
return re.compile(rf"(?s:{pattern})\Z")
|
|
24
34
|
|
|
25
35
|
|
|
36
|
+
def get_configurable_headers(headers: Mapping[str, str]) -> dict[str, str]:
|
|
37
|
+
"""Extract headers that should be added to run configuration.
|
|
38
|
+
|
|
39
|
+
This function handles special cases like langsmith-trace and baggage headers,
|
|
40
|
+
while respecting the configurable header patterns.
|
|
41
|
+
"""
|
|
42
|
+
configurable = {}
|
|
43
|
+
if not headers:
|
|
44
|
+
return configurable
|
|
45
|
+
|
|
46
|
+
for key, value in headers.items():
|
|
47
|
+
# First handle tracing stuff - always included regardless of patterns
|
|
48
|
+
if key == "langsmith-trace":
|
|
49
|
+
configurable[key] = value
|
|
50
|
+
if baggage := headers.get("baggage"):
|
|
51
|
+
for item in baggage.split(","):
|
|
52
|
+
baggage_key, baggage_value = item.split("=")
|
|
53
|
+
if (
|
|
54
|
+
baggage_key == LANGSMITH_METADATA
|
|
55
|
+
and baggage_key not in configurable
|
|
56
|
+
):
|
|
57
|
+
configurable[baggage_key] = orjson.loads(
|
|
58
|
+
urllib.parse.unquote(baggage_value)
|
|
59
|
+
)
|
|
60
|
+
elif baggage_key == LANGSMITH_TAGS:
|
|
61
|
+
configurable[baggage_key] = urllib.parse.unquote(
|
|
62
|
+
baggage_value
|
|
63
|
+
).split(",")
|
|
64
|
+
elif baggage_key == LANGSMITH_PROJECT:
|
|
65
|
+
configurable[baggage_key] = urllib.parse.unquote(baggage_value)
|
|
66
|
+
continue
|
|
67
|
+
|
|
68
|
+
# Check if header should be included based on patterns
|
|
69
|
+
# For run configuration, we have specific default behavior for x-* headers
|
|
70
|
+
if key.startswith("x-"):
|
|
71
|
+
# Check against default excludes for x-* headers
|
|
72
|
+
if key in DEFAULT_RUN_HEADERS_EXCLUDE:
|
|
73
|
+
# Check if explicitly included via patterns
|
|
74
|
+
if should_include_header(key):
|
|
75
|
+
configurable[key] = value
|
|
76
|
+
continue
|
|
77
|
+
# Other x-* headers are included by default unless patterns exclude them
|
|
78
|
+
if should_include_header(key):
|
|
79
|
+
configurable[key] = value
|
|
80
|
+
elif key == "user-agent":
|
|
81
|
+
# user-agent is included by default unless excluded by patterns
|
|
82
|
+
if should_include_header(key):
|
|
83
|
+
configurable[key] = value
|
|
84
|
+
else:
|
|
85
|
+
# All other headers only included if patterns allow
|
|
86
|
+
if should_include_header(key):
|
|
87
|
+
configurable[key] = value
|
|
88
|
+
|
|
89
|
+
return configurable
|
|
90
|
+
|
|
91
|
+
|
|
26
92
|
@functools.lru_cache(maxsize=1)
|
|
27
93
|
def get_header_patterns(
|
|
28
94
|
key: str,
|
|
@@ -93,5 +159,5 @@ def pattern_matches(
|
|
|
93
159
|
# If include patterns are specified, only include headers matching them
|
|
94
160
|
return any(pattern.match(key) for pattern in include_patterns)
|
|
95
161
|
|
|
96
|
-
# Default behavior -
|
|
97
|
-
return
|
|
162
|
+
# Default behavior - exclude
|
|
163
|
+
return False
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: langgraph-api
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.11
|
|
4
4
|
Author-email: Nuno Campos <nuno@langchain.dev>, Will Fu-Hinthorn <will@langchain.dev>
|
|
5
5
|
License: Elastic-2.0
|
|
6
6
|
License-File: LICENSE
|
|
@@ -11,7 +11,7 @@ Requires-Dist: httpx>=0.25.0
|
|
|
11
11
|
Requires-Dist: jsonschema-rs<0.30,>=0.20.0
|
|
12
12
|
Requires-Dist: langchain-core>=0.3.64
|
|
13
13
|
Requires-Dist: langgraph-checkpoint>=2.0.23
|
|
14
|
-
Requires-Dist: langgraph-runtime-inmem<0.
|
|
14
|
+
Requires-Dist: langgraph-runtime-inmem<0.12.0,>=0.11.0
|
|
15
15
|
Requires-Dist: langgraph-sdk>=0.2.0
|
|
16
16
|
Requires-Dist: langgraph>=0.4.0
|
|
17
17
|
Requires-Dist: langsmith>=0.3.45
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
langgraph_api/__init__.py,sha256=
|
|
1
|
+
langgraph_api/__init__.py,sha256=xIphSmmFF5C8ZjsK5bpruTtbjrTL9bI6TjdjgsELGCw,23
|
|
2
2
|
langgraph_api/asgi_transport.py,sha256=XtiLOu4WWsd-xizagBLzT5xUkxc9ZG9YqwvETBPjBFE,5161
|
|
3
3
|
langgraph_api/asyncio.py,sha256=NjHFvZStKryAAfGOrl3-efHtCzibvpDx-dl8PnrE1Tk,9588
|
|
4
4
|
langgraph_api/cli.py,sha256=-ruIeKi1imvS6GriOfRDZY-waV4SbWiJ0BEFAciPVYI,16330
|
|
5
|
-
langgraph_api/command.py,sha256=
|
|
5
|
+
langgraph_api/command.py,sha256=Q9XDRhnkCX7jyqW52_Rf2PPYKxjr-Z9BUHazI1HcmB8,817
|
|
6
6
|
langgraph_api/config.py,sha256=r9mmbyZlhBuJLpnTkaOLcNH6ufFNqm_2eCiuOmhqRl0,12241
|
|
7
7
|
langgraph_api/cron_scheduler.py,sha256=25wYzEQrhPEivZrAPYOmzLPDOQa-aFogU37mTXc9TJk,2566
|
|
8
8
|
langgraph_api/errors.py,sha256=zlnl3xXIwVG0oGNKKpXf1an9Rn_SBDHSyhe53hU6aLw,1858
|
|
9
9
|
langgraph_api/executor_entrypoint.py,sha256=CaX813ygtf9CpOaBkfkQXJAHjFtmlScCkrOvTDmu4Aw,750
|
|
10
10
|
langgraph_api/feature_flags.py,sha256=x28NwFJXdfuGW2uUmon6lBSh0pGBo27bw_Se72TO4sM,409
|
|
11
|
-
langgraph_api/graph.py,sha256=
|
|
11
|
+
langgraph_api/graph.py,sha256=h1m6rsLiCocvMO283LLU03A5cBycxAIxixXu9mwzqsQ,25056
|
|
12
12
|
langgraph_api/http.py,sha256=fyK-H-0UfNy_BzuVW3aWWGvhRavmGAVMkDwDArryJ_4,5659
|
|
13
13
|
langgraph_api/http_metrics.py,sha256=MU9ccXt7aBb0AJ2SWEjwtbtbJEWmeqSdx7-CI51e32o,5594
|
|
14
14
|
langgraph_api/logging.py,sha256=qB6q_cUba31edE4_D6dBGhdiUTpW7sXAOepUjYb_R50,5216
|
|
@@ -20,23 +20,23 @@ langgraph_api/schema.py,sha256=AsgF0dIjBvDd_PDy20mGqB_IkBLgVvSj8qRKG_lPlec,8440
|
|
|
20
20
|
langgraph_api/serde.py,sha256=3GvelKhySjlXaNqpg2GyUxU6-NEkvif7WlMF9if_EgU,6029
|
|
21
21
|
langgraph_api/server.py,sha256=uCAqPgCLJ6ckslLs0i_dacSR8mzuR0Y6PkkJYk0O3bE,7196
|
|
22
22
|
langgraph_api/sse.py,sha256=SLdtZmTdh5D8fbWrQjuY9HYLd2dg8Rmi6ZMmFMVc2iE,4204
|
|
23
|
-
langgraph_api/state.py,sha256=
|
|
23
|
+
langgraph_api/state.py,sha256=AjkLbUQakIwK7oGzJ8oqubazRsXxG3vDMnRa0s0mzDM,4716
|
|
24
24
|
langgraph_api/store.py,sha256=NIoNZojs6NbtG3VLBPQEFNttvp7XPkHAfjbQ3gY7aLY,4701
|
|
25
25
|
langgraph_api/stream.py,sha256=V8jWwA3wBRenMk3WIFkt0OLXm_LhPwg_Yj_tP4Dc6iI,18970
|
|
26
|
-
langgraph_api/thread_ttl.py,sha256=
|
|
26
|
+
langgraph_api/thread_ttl.py,sha256=KyHnvD0e1p1cV4Z_ZvKNVzDztuI2RBCUsUO2V7GlOSw,1951
|
|
27
27
|
langgraph_api/traceblock.py,sha256=Qq5CUdefnMDaRDnyvBSWGBClEj-f3oO7NbH6fedxOSE,630
|
|
28
|
-
langgraph_api/utils.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
29
28
|
langgraph_api/validation.py,sha256=86jftgOsMa7tkeshBw6imYe7zyUXPoVuf5Voh6dFiR8,5285
|
|
30
29
|
langgraph_api/webhook.py,sha256=SvSM1rdnNtiH4q3JQYmAqJUk2Sable5xAcwOLuRhtlo,1723
|
|
31
30
|
langgraph_api/worker.py,sha256=M9WQdxEzVGDZqdjz3LHEHhM1g6isPcf3k1V4PEkcSY8,15343
|
|
32
|
-
langgraph_api/api/__init__.py,sha256=
|
|
33
|
-
langgraph_api/api/
|
|
31
|
+
langgraph_api/api/__init__.py,sha256=raFkYH50tsO-KjRmDbGVoHCuxuH58u1lrZbr-MlITIY,6262
|
|
32
|
+
langgraph_api/api/a2a.py,sha256=HralDXn_sbTnZIKfRfC-1Gl2SHX3Z74vYkTkBA-mlyw,35143
|
|
33
|
+
langgraph_api/api/assistants.py,sha256=JFaBYp9BAXGaJ0yfy1SG_Mr-3xjeWSkdCHtmXpiAqP4,17290
|
|
34
34
|
langgraph_api/api/mcp.py,sha256=qe10ZRMN3f-Hli-9TI8nbQyWvMeBb72YB1PZVbyqBQw,14418
|
|
35
35
|
langgraph_api/api/meta.py,sha256=dFD9ZgykbKARLdVSaJD9vO3CShvEyBmGpkjE8tqii0c,4448
|
|
36
36
|
langgraph_api/api/openapi.py,sha256=If-z1ckXt-Yu5bwQytK1LWyX_T7G46UtLfixgEP8hwc,11959
|
|
37
37
|
langgraph_api/api/runs.py,sha256=Dzqg3Klnp_7QVHl26J51DpSlMvBhgUdwcKeeMQdqa4Y,22127
|
|
38
38
|
langgraph_api/api/store.py,sha256=xGcPFx4v-VxlK6HRU9uCjzCQ0v66cvc3o_PB5_g7n0Q,5550
|
|
39
|
-
langgraph_api/api/threads.py,sha256=
|
|
39
|
+
langgraph_api/api/threads.py,sha256=5-ZEcs48bL3vot_yCt3ImuA9hzg93LxuAd_DXd2xj4Y,12915
|
|
40
40
|
langgraph_api/api/ui.py,sha256=_genglTUy5BMHlL0lkQypX524yFv6Z5fraIvnrxp7yE,2639
|
|
41
41
|
langgraph_api/auth/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
42
42
|
langgraph_api/auth/custom.py,sha256=psETw_GpLWClBbd_ESVPRLUz9GLQ0_XNsuUDSVbtZy0,22522
|
|
@@ -55,11 +55,8 @@ langgraph_api/js/client.http.mts,sha256=cvn8JV9go4pUMWkcug8FfSYWsp1wTaT8SgJaskqE
|
|
|
55
55
|
langgraph_api/js/client.mts,sha256=gDvYiW7Qfl4re2YhZ5oNqtuvffnW_Sf7DK5aUbKB3vw,32330
|
|
56
56
|
langgraph_api/js/errors.py,sha256=Cm1TKWlUCwZReDC5AQ6SgNIVGD27Qov2xcgHyf8-GXo,361
|
|
57
57
|
langgraph_api/js/global.d.ts,sha256=j4GhgtQSZ5_cHzjSPcHgMJ8tfBThxrH-pUOrrJGteOU,196
|
|
58
|
-
langgraph_api/js/isolate-0x130008000-46649-46649-v8.log,sha256=s6h4v7ZtOBK9tKJ0zzPojnob7MBV2j-nyLVPGbgotdg,2641826
|
|
59
|
-
langgraph_api/js/isolate-0x138008000-44681-44681-v8.log,sha256=knAbmQTmz2umDoqMXKuThsmgc4Q6b1HBwZ2ZFUcFu40,2644591
|
|
60
|
-
langgraph_api/js/package-lock.json,sha256=bGM4LYdWJxAb5JE6Tap1-WA_IqRE_CzBjxZTCcMt1_U,117589
|
|
61
58
|
langgraph_api/js/package.json,sha256=syy2fEcmTxGQVfz4P9MUTgoTxHr1MUcA1rDXemAig2U,1335
|
|
62
|
-
langgraph_api/js/remote.py,sha256=
|
|
59
|
+
langgraph_api/js/remote.py,sha256=iWs3SdmV7vFa28p04rYFSdFEGSYSlS3918AzWljKT9Q,38644
|
|
63
60
|
langgraph_api/js/schema.py,sha256=M4fLtr50O1jck8H1hm_0W4cZOGYGdkrB7riLyCes4oY,438
|
|
64
61
|
langgraph_api/js/sse.py,sha256=hHkbncnYnXNIbHhAWneGWYkHp4UhhhGB7-MYtDrY264,4141
|
|
65
62
|
langgraph_api/js/traceblock.mts,sha256=QtGSN5VpzmGqDfbArrGXkMiONY94pMQ5CgzetT_bKYg,761
|
|
@@ -78,13 +75,13 @@ langgraph_api/middleware/http_logger.py,sha256=2LABfhzTAUtqT8nf1ACy8cYXteatkwraB
|
|
|
78
75
|
langgraph_api/middleware/private_network.py,sha256=eYgdyU8AzU2XJu362i1L8aSFoQRiV7_aLBPw7_EgeqI,2111
|
|
79
76
|
langgraph_api/middleware/request_id.py,sha256=SDj3Yi3WvTbFQ2ewrPQBjAV8sYReOJGeIiuoHeZpR9g,1242
|
|
80
77
|
langgraph_api/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
81
|
-
langgraph_api/models/run.py,sha256=
|
|
78
|
+
langgraph_api/models/run.py,sha256=HAMvpmIVnGcuOaKoDcpEfeRWo00-bmX_Gvp6lqo7VO0,13223
|
|
82
79
|
langgraph_api/tunneling/cloudflare.py,sha256=iKb6tj-VWPlDchHFjuQyep2Dpb-w2NGfJKt-WJG9LH0,3650
|
|
83
80
|
langgraph_api/utils/__init__.py,sha256=yCMq7pOMlmeNmi2Fh8U7KLiljBdOMcF0L2SfpobnKKE,5703
|
|
84
81
|
langgraph_api/utils/cache.py,sha256=SrtIWYibbrNeZzLXLUGBFhJPkMVNQnVxR5giiYGHEfI,1810
|
|
85
82
|
langgraph_api/utils/config.py,sha256=Tbp4tKDSLKXQJ44EKr885wAQupY-9VWNJ6rgUU2oLOY,4162
|
|
86
83
|
langgraph_api/utils/future.py,sha256=lXsRQPhJwY7JUbFFZrK-94JjgsToLu-EWU896hvbUxE,7289
|
|
87
|
-
langgraph_api/utils/headers.py,sha256=
|
|
84
|
+
langgraph_api/utils/headers.py,sha256=NDBmKSSVOOYeYN0HfK1a3xbYtAg35M_JO1G9yklpZsA,5682
|
|
88
85
|
langgraph_api/utils/uuids.py,sha256=AW_9-1iFqK2K5hljmi-jtaNzUIoBshk5QPt8LbpbD2g,2975
|
|
89
86
|
langgraph_license/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
90
87
|
langgraph_license/validation.py,sha256=CU38RUZ5xhP1S8F_y8TNeV6OmtO-tIGjCXbXTwJjJO4,612
|
|
@@ -99,9 +96,9 @@ langgraph_runtime/retry.py,sha256=V0duD01fO7GUQ_btQkp1aoXcEOFhXooGVP6q4yMfuyY,11
|
|
|
99
96
|
langgraph_runtime/store.py,sha256=7mowndlsIroGHv3NpTSOZDJR0lCuaYMBoTnTrewjslw,114
|
|
100
97
|
LICENSE,sha256=ZPwVR73Biwm3sK6vR54djCrhaRiM4cAD2zvOQZV8Xis,3859
|
|
101
98
|
logging.json,sha256=3RNjSADZmDq38eHePMm1CbP6qZ71AmpBtLwCmKU9Zgo,379
|
|
102
|
-
openapi.json,sha256=
|
|
103
|
-
langgraph_api-0.4.
|
|
104
|
-
langgraph_api-0.4.
|
|
105
|
-
langgraph_api-0.4.
|
|
106
|
-
langgraph_api-0.4.
|
|
107
|
-
langgraph_api-0.4.
|
|
99
|
+
openapi.json,sha256=21wu-NxdxyTQwZctNcEfRkLMnSBi0QhGAfwq5kg8XNU,172618
|
|
100
|
+
langgraph_api-0.4.11.dist-info/METADATA,sha256=hK2zyCzG6xb1reykMm0EeX_kXl_y2he20Bf2xHDWndM,3893
|
|
101
|
+
langgraph_api-0.4.11.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
102
|
+
langgraph_api-0.4.11.dist-info/entry_points.txt,sha256=hGedv8n7cgi41PypMfinwS_HfCwA7xJIfS0jAp8htV8,78
|
|
103
|
+
langgraph_api-0.4.11.dist-info/licenses/LICENSE,sha256=ZPwVR73Biwm3sK6vR54djCrhaRiM4cAD2zvOQZV8Xis,3859
|
|
104
|
+
langgraph_api-0.4.11.dist-info/RECORD,,
|