langgraph-api 0.0.31__py3-none-any.whl → 0.0.33__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/api/__init__.py +6 -0
- langgraph_api/api/assistants.py +7 -1
- langgraph_api/api/mcp.py +467 -0
- langgraph_api/api/threads.py +10 -1
- langgraph_api/asyncio.py +21 -2
- langgraph_api/cli.py +9 -7
- langgraph_api/command.py +29 -0
- langgraph_api/config.py +5 -0
- langgraph_api/graph.py +24 -8
- langgraph_api/js/remote.py +72 -51
- langgraph_api/lifespan.py +10 -1
- langgraph_api/logging.py +11 -10
- langgraph_api/metadata.py +1 -0
- langgraph_api/models/run.py +11 -1
- langgraph_api/stream.py +5 -30
- langgraph_api/validation.py +13 -1
- langgraph_api/worker.py +2 -2
- {langgraph_api-0.0.31.dist-info → langgraph_api-0.0.33.dist-info}/METADATA +2 -2
- {langgraph_api-0.0.31.dist-info → langgraph_api-0.0.33.dist-info}/RECORD +24 -22
- langgraph_storage/ops.py +75 -1
- openapi.json +102 -0
- {langgraph_api-0.0.31.dist-info → langgraph_api-0.0.33.dist-info}/LICENSE +0 -0
- {langgraph_api-0.0.31.dist-info → langgraph_api-0.0.33.dist-info}/WHEEL +0 -0
- {langgraph_api-0.0.31.dist-info → langgraph_api-0.0.33.dist-info}/entry_points.txt +0 -0
langgraph_api/graph.py
CHANGED
|
@@ -21,6 +21,7 @@ from langgraph.pregel import Pregel
|
|
|
21
21
|
from langgraph.store.base import BaseStore
|
|
22
22
|
from starlette.exceptions import HTTPException
|
|
23
23
|
|
|
24
|
+
from langgraph_api import asyncio as lg_asyncio
|
|
24
25
|
from langgraph_api.js.base import BaseRemotePregel
|
|
25
26
|
from langgraph_api.schema import Config
|
|
26
27
|
|
|
@@ -60,6 +61,12 @@ async def register_graph(graph_id: str, graph: GraphValue, config: dict | None)
|
|
|
60
61
|
)
|
|
61
62
|
|
|
62
63
|
|
|
64
|
+
def register_graph_sync(
|
|
65
|
+
graph_id: str, graph: GraphValue, config: dict | None = None
|
|
66
|
+
) -> None:
|
|
67
|
+
lg_asyncio.run_coroutine_threadsafe(register_graph(graph_id, graph, config))
|
|
68
|
+
|
|
69
|
+
|
|
63
70
|
@asynccontextmanager
|
|
64
71
|
async def _generate_graph(value: Any) -> AsyncIterator[Any]:
|
|
65
72
|
"""Yield a graph object regardless of its type."""
|
|
@@ -187,24 +194,33 @@ async def collect_graphs_from_env(register: bool = False) -> None:
|
|
|
187
194
|
config_per_graph = _load_graph_config_from_env() or {}
|
|
188
195
|
|
|
189
196
|
if paths_str:
|
|
190
|
-
specs = [
|
|
191
|
-
|
|
197
|
+
specs = []
|
|
198
|
+
for key, value in json.loads(paths_str).items():
|
|
199
|
+
try:
|
|
200
|
+
path_or_module, variable = value.rsplit(":", maxsplit=1)
|
|
201
|
+
except ValueError as e:
|
|
202
|
+
raise ValueError(
|
|
203
|
+
f"Invalid path '{value}' for graph '{key}'."
|
|
204
|
+
" Did you miss a variable name?\n"
|
|
205
|
+
" Expected one of the following formats:"
|
|
206
|
+
" 'my.module:variable_name' or '/path/to/file.py:variable_name'"
|
|
207
|
+
) from e
|
|
208
|
+
specs.append(
|
|
192
209
|
GraphSpec(
|
|
193
210
|
key,
|
|
194
|
-
module=
|
|
195
|
-
variable=
|
|
211
|
+
module=path_or_module,
|
|
212
|
+
variable=variable,
|
|
196
213
|
config=config_per_graph.get(key),
|
|
197
214
|
)
|
|
198
215
|
if "/" not in value
|
|
199
216
|
else GraphSpec(
|
|
200
217
|
key,
|
|
201
|
-
path=
|
|
202
|
-
variable=
|
|
218
|
+
path=path_or_module,
|
|
219
|
+
variable=variable,
|
|
203
220
|
config=config_per_graph.get(key),
|
|
204
221
|
)
|
|
205
222
|
)
|
|
206
|
-
|
|
207
|
-
]
|
|
223
|
+
|
|
208
224
|
else:
|
|
209
225
|
specs = [
|
|
210
226
|
GraphSpec(
|
langgraph_api/js/remote.py
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
+
import logging
|
|
2
3
|
import os
|
|
3
4
|
import shutil
|
|
4
5
|
import ssl
|
|
5
6
|
from collections.abc import AsyncIterator
|
|
7
|
+
from contextlib import AbstractContextManager
|
|
6
8
|
from typing import Any, Literal
|
|
7
9
|
|
|
8
10
|
import certifi
|
|
@@ -605,60 +607,79 @@ async def run_remote_checkpointer():
|
|
|
605
607
|
await server.serve()
|
|
606
608
|
|
|
607
609
|
|
|
610
|
+
class DisableHttpxLoggingContextManager(AbstractContextManager):
|
|
611
|
+
"""
|
|
612
|
+
Disable HTTP/1.1 200 OK logs spamming stdout.
|
|
613
|
+
"""
|
|
614
|
+
|
|
615
|
+
filter: logging.Filter
|
|
616
|
+
|
|
617
|
+
def filter(self, record: logging.LogRecord) -> bool:
|
|
618
|
+
return "200 OK" not in record.getMessage()
|
|
619
|
+
|
|
620
|
+
def __enter__(self):
|
|
621
|
+
logging.getLogger("httpx").addFilter(self.filter)
|
|
622
|
+
|
|
623
|
+
def __exit__(self, exc_type, exc_value, traceback):
|
|
624
|
+
logging.getLogger("httpx").removeFilter(self.filter)
|
|
625
|
+
|
|
626
|
+
|
|
608
627
|
async def wait_until_js_ready():
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
628
|
+
with DisableHttpxLoggingContextManager():
|
|
629
|
+
async with (
|
|
630
|
+
httpx.AsyncClient(
|
|
631
|
+
base_url=f"http://localhost:{GRAPH_PORT}",
|
|
632
|
+
limits=httpx.Limits(max_connections=1),
|
|
633
|
+
transport=httpx.AsyncHTTPTransport(verify=SSL),
|
|
634
|
+
) as graph_client,
|
|
635
|
+
httpx.AsyncClient(
|
|
636
|
+
base_url=f"http://localhost:{REMOTE_PORT}",
|
|
637
|
+
limits=httpx.Limits(max_connections=1),
|
|
638
|
+
transport=httpx.AsyncHTTPTransport(verify=SSL),
|
|
639
|
+
) as checkpointer_client,
|
|
640
|
+
):
|
|
641
|
+
attempt = 0
|
|
642
|
+
while not asyncio.current_task().cancelled():
|
|
643
|
+
try:
|
|
644
|
+
res = await graph_client.get("/ok")
|
|
645
|
+
res.raise_for_status()
|
|
646
|
+
res = await checkpointer_client.get("/ok")
|
|
647
|
+
res.raise_for_status()
|
|
648
|
+
return
|
|
649
|
+
except httpx.HTTPError:
|
|
650
|
+
if attempt > 240:
|
|
651
|
+
raise
|
|
652
|
+
else:
|
|
653
|
+
attempt += 1
|
|
654
|
+
await asyncio.sleep(0.5)
|
|
655
|
+
|
|
656
|
+
|
|
657
|
+
async def js_healthcheck():
|
|
658
|
+
with DisableHttpxLoggingContextManager():
|
|
659
|
+
async with (
|
|
660
|
+
httpx.AsyncClient(
|
|
661
|
+
base_url=f"http://localhost:{GRAPH_PORT}",
|
|
662
|
+
limits=httpx.Limits(max_connections=1),
|
|
663
|
+
transport=httpx.AsyncHTTPTransport(verify=SSL),
|
|
664
|
+
) as graph_client,
|
|
665
|
+
httpx.AsyncClient(
|
|
666
|
+
base_url=f"http://localhost:{REMOTE_PORT}",
|
|
667
|
+
limits=httpx.Limits(max_connections=1),
|
|
668
|
+
transport=httpx.AsyncHTTPTransport(verify=SSL),
|
|
669
|
+
) as checkpointer_client,
|
|
670
|
+
):
|
|
623
671
|
try:
|
|
624
672
|
res = await graph_client.get("/ok")
|
|
625
673
|
res.raise_for_status()
|
|
626
674
|
res = await checkpointer_client.get("/ok")
|
|
627
675
|
res.raise_for_status()
|
|
628
|
-
return
|
|
629
|
-
except httpx.HTTPError:
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
async with (
|
|
639
|
-
httpx.AsyncClient(
|
|
640
|
-
base_url=f"http://localhost:{GRAPH_PORT}",
|
|
641
|
-
limits=httpx.Limits(max_connections=1),
|
|
642
|
-
transport=httpx.AsyncHTTPTransport(verify=SSL),
|
|
643
|
-
) as graph_client,
|
|
644
|
-
httpx.AsyncClient(
|
|
645
|
-
base_url=f"http://localhost:{REMOTE_PORT}",
|
|
646
|
-
limits=httpx.Limits(max_connections=1),
|
|
647
|
-
transport=httpx.AsyncHTTPTransport(verify=SSL),
|
|
648
|
-
) as checkpointer_client,
|
|
649
|
-
):
|
|
650
|
-
try:
|
|
651
|
-
res = await graph_client.get("/ok")
|
|
652
|
-
res.raise_for_status()
|
|
653
|
-
res = await checkpointer_client.get("/ok")
|
|
654
|
-
res.raise_for_status()
|
|
655
|
-
return True
|
|
656
|
-
except httpx.HTTPError as exc:
|
|
657
|
-
logger.warning(
|
|
658
|
-
"JS healthcheck failed. Either the JS server is not running or the event loop is blocked by a CPU-intensive task.",
|
|
659
|
-
error=exc,
|
|
660
|
-
)
|
|
661
|
-
raise HTTPException(
|
|
662
|
-
status_code=500,
|
|
663
|
-
detail="JS healthcheck failed. Either the JS server is not running or the event loop is blocked by a CPU-intensive task.",
|
|
664
|
-
) from exc
|
|
676
|
+
return True
|
|
677
|
+
except httpx.HTTPError as exc:
|
|
678
|
+
logger.warning(
|
|
679
|
+
"JS healthcheck failed. Either the JS server is not running or the event loop is blocked by a CPU-intensive task.",
|
|
680
|
+
error=exc,
|
|
681
|
+
)
|
|
682
|
+
raise HTTPException(
|
|
683
|
+
status_code=500,
|
|
684
|
+
detail="JS healthcheck failed. Either the JS server is not running or the event loop is blocked by a CPU-intensive task.",
|
|
685
|
+
) from exc
|
langgraph_api/lifespan.py
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
from contextlib import asynccontextmanager
|
|
3
3
|
|
|
4
|
+
import structlog
|
|
4
5
|
from starlette.applications import Starlette
|
|
5
6
|
|
|
6
7
|
import langgraph_api.config as config
|
|
7
|
-
from langgraph_api.asyncio import SimpleTaskGroup
|
|
8
|
+
from langgraph_api.asyncio import SimpleTaskGroup, set_event_loop
|
|
8
9
|
from langgraph_api.cron_scheduler import cron_scheduler
|
|
9
10
|
from langgraph_api.graph import collect_graphs_from_env, stop_remote_graphs
|
|
10
11
|
from langgraph_api.http import start_http_client, stop_http_client
|
|
@@ -14,6 +15,8 @@ from langgraph_storage.database import start_pool, stop_pool
|
|
|
14
15
|
from langgraph_storage.queue import queue
|
|
15
16
|
from langgraph_storage.store import Store
|
|
16
17
|
|
|
18
|
+
logger = structlog.stdlib.get_logger(__name__)
|
|
19
|
+
|
|
17
20
|
|
|
18
21
|
@asynccontextmanager
|
|
19
22
|
async def lifespan(
|
|
@@ -21,6 +24,12 @@ async def lifespan(
|
|
|
21
24
|
with_cron_scheduler: bool = True,
|
|
22
25
|
taskset: set[asyncio.Task] | None = None,
|
|
23
26
|
):
|
|
27
|
+
try:
|
|
28
|
+
current_loop = asyncio.get_running_loop()
|
|
29
|
+
set_event_loop(current_loop)
|
|
30
|
+
except RuntimeError:
|
|
31
|
+
await logger.aerror("Failed to set loop")
|
|
32
|
+
|
|
24
33
|
if not await get_license_status():
|
|
25
34
|
raise ValueError(
|
|
26
35
|
"License verification failed. Please ensure proper configuration:\n"
|
langgraph_api/logging.py
CHANGED
|
@@ -119,13 +119,14 @@ class Formatter(structlog.stdlib.ProcessorFormatter):
|
|
|
119
119
|
|
|
120
120
|
# configure structlog
|
|
121
121
|
|
|
122
|
-
structlog.
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
122
|
+
if not structlog.is_configured():
|
|
123
|
+
structlog.configure(
|
|
124
|
+
processors=[
|
|
125
|
+
structlog.stdlib.filter_by_level,
|
|
126
|
+
*shared_processors,
|
|
127
|
+
structlog.stdlib.ProcessorFormatter.wrap_for_formatter,
|
|
128
|
+
],
|
|
129
|
+
logger_factory=structlog.stdlib.LoggerFactory(),
|
|
130
|
+
wrapper_class=structlog.stdlib.BoundLogger,
|
|
131
|
+
cache_logger_on_first_use=True,
|
|
132
|
+
)
|
langgraph_api/metadata.py
CHANGED
langgraph_api/models/run.py
CHANGED
|
@@ -241,7 +241,17 @@ async def create_valid_run(
|
|
|
241
241
|
# handle multitask strategy
|
|
242
242
|
inflight_runs = [run async for run in run_]
|
|
243
243
|
if first["run_id"] == run_id:
|
|
244
|
-
logger.info(
|
|
244
|
+
logger.info(
|
|
245
|
+
"Created run",
|
|
246
|
+
run_id=str(run_id),
|
|
247
|
+
thread_id=str(thread_id),
|
|
248
|
+
assistant_id=str(assistant_id),
|
|
249
|
+
multitask_strategy=multitask_strategy,
|
|
250
|
+
stream_mode=stream_mode,
|
|
251
|
+
temporary=temporary,
|
|
252
|
+
after_seconds=payload.get("after_seconds", 0),
|
|
253
|
+
if_not_exists=payload.get("if_not_exists", "reject"),
|
|
254
|
+
)
|
|
245
255
|
# inserted, proceed
|
|
246
256
|
if multitask_strategy in ("interrupt", "rollback") and inflight_runs:
|
|
247
257
|
try:
|
langgraph_api/stream.py
CHANGED
|
@@ -19,15 +19,15 @@ from langgraph.errors import (
|
|
|
19
19
|
InvalidUpdateError,
|
|
20
20
|
)
|
|
21
21
|
from langgraph.pregel.debug import CheckpointPayload, TaskResultPayload
|
|
22
|
-
from langgraph.types import Command, Send
|
|
23
22
|
from pydantic import ValidationError
|
|
24
23
|
from pydantic.v1 import ValidationError as ValidationErrorLegacy
|
|
25
24
|
|
|
26
25
|
from langgraph_api.asyncio import ValueEvent, wait_if_not_done
|
|
26
|
+
from langgraph_api.command import map_cmd
|
|
27
27
|
from langgraph_api.graph import get_graph
|
|
28
28
|
from langgraph_api.js.base import BaseRemotePregel
|
|
29
|
-
from langgraph_api.metadata import HOST, PLAN, incr_nodes
|
|
30
|
-
from langgraph_api.schema import Run,
|
|
29
|
+
from langgraph_api.metadata import HOST, PLAN, USER_API_URL, incr_nodes
|
|
30
|
+
from langgraph_api.schema import Run, StreamMode
|
|
31
31
|
from langgraph_api.serde import json_dumpb
|
|
32
32
|
from langgraph_api.utils import AsyncConnectionProto
|
|
33
33
|
from langgraph_storage.checkpoint import Checkpointer
|
|
@@ -70,32 +70,6 @@ def _preprocess_debug_checkpoint(payload: CheckpointPayload | None) -> dict[str,
|
|
|
70
70
|
return payload
|
|
71
71
|
|
|
72
72
|
|
|
73
|
-
def _map_cmd(cmd: RunCommand) -> Command:
|
|
74
|
-
goto = cmd.get("goto")
|
|
75
|
-
if goto is not None and not isinstance(goto, list):
|
|
76
|
-
goto = [cmd.get("goto")]
|
|
77
|
-
|
|
78
|
-
update = cmd.get("update")
|
|
79
|
-
if isinstance(update, tuple | list) and all(
|
|
80
|
-
isinstance(t, tuple | list) and len(t) == 2 and isinstance(t[0], str)
|
|
81
|
-
for t in update
|
|
82
|
-
):
|
|
83
|
-
update = [tuple(t) for t in update]
|
|
84
|
-
|
|
85
|
-
return Command(
|
|
86
|
-
update=update,
|
|
87
|
-
goto=(
|
|
88
|
-
[
|
|
89
|
-
it if isinstance(it, str) else Send(it["node"], it["input"])
|
|
90
|
-
for it in goto
|
|
91
|
-
]
|
|
92
|
-
if goto
|
|
93
|
-
else None
|
|
94
|
-
),
|
|
95
|
-
resume=cmd.get("resume"),
|
|
96
|
-
)
|
|
97
|
-
|
|
98
|
-
|
|
99
73
|
async def astream_state(
|
|
100
74
|
stack: AsyncExitStack,
|
|
101
75
|
conn: AsyncConnectionProto,
|
|
@@ -125,7 +99,7 @@ async def astream_state(
|
|
|
125
99
|
)
|
|
126
100
|
input = kwargs.pop("input")
|
|
127
101
|
if cmd := kwargs.pop("command"):
|
|
128
|
-
input =
|
|
102
|
+
input = map_cmd(cmd)
|
|
129
103
|
stream_mode: list[StreamMode] = kwargs.pop("stream_mode")
|
|
130
104
|
feedback_keys = kwargs.pop("feedback_keys", None)
|
|
131
105
|
stream_modes_set: set[StreamMode] = set(stream_mode) - {"events"}
|
|
@@ -140,6 +114,7 @@ async def astream_state(
|
|
|
140
114
|
config["metadata"]["langgraph_version"] = langgraph.version.__version__
|
|
141
115
|
config["metadata"]["langgraph_plan"] = PLAN
|
|
142
116
|
config["metadata"]["langgraph_host"] = HOST
|
|
117
|
+
config["metadata"]["langgraph_api_url"] = USER_API_URL
|
|
143
118
|
# attach node counter
|
|
144
119
|
if not isinstance(graph, BaseRemotePregel):
|
|
145
120
|
config["configurable"]["__pregel_node_finished"] = incr_nodes
|
langgraph_api/validation.py
CHANGED
|
@@ -27,7 +27,18 @@ AssistantVersionChange = jsonschema_rs.validator_for(
|
|
|
27
27
|
openapi["components"]["schemas"]["AssistantVersionChange"]
|
|
28
28
|
)
|
|
29
29
|
ThreadCreate = jsonschema_rs.validator_for(
|
|
30
|
-
|
|
30
|
+
{
|
|
31
|
+
**openapi["components"]["schemas"]["ThreadCreate"],
|
|
32
|
+
"components": {
|
|
33
|
+
"schemas": {
|
|
34
|
+
"ThreadSuperstepUpdate": openapi["components"]["schemas"][
|
|
35
|
+
"ThreadSuperstepUpdate"
|
|
36
|
+
],
|
|
37
|
+
"Command": openapi["components"]["schemas"]["Command"],
|
|
38
|
+
"Send": openapi["components"]["schemas"]["Send"],
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
}
|
|
31
42
|
)
|
|
32
43
|
ThreadPatch = jsonschema_rs.validator_for(
|
|
33
44
|
openapi["components"]["schemas"]["ThreadPatch"]
|
|
@@ -42,6 +53,7 @@ ThreadStateUpdate = jsonschema_rs.validator_for(
|
|
|
42
53
|
},
|
|
43
54
|
}
|
|
44
55
|
)
|
|
56
|
+
|
|
45
57
|
ThreadStateCheckpointRequest = jsonschema_rs.validator_for(
|
|
46
58
|
{
|
|
47
59
|
**openapi["components"]["schemas"]["ThreadStateCheckpointRequest"],
|
langgraph_api/worker.py
CHANGED
|
@@ -210,7 +210,7 @@ async def worker(
|
|
|
210
210
|
status = "retry"
|
|
211
211
|
run_ended_at = datetime.now(UTC).isoformat()
|
|
212
212
|
await logger.awarning(
|
|
213
|
-
"Background run failed, will retry",
|
|
213
|
+
f"Background run failed, will retry. Exception: {e}",
|
|
214
214
|
exc_info=True,
|
|
215
215
|
run_id=str(run_id),
|
|
216
216
|
run_attempt=attempt,
|
|
@@ -226,7 +226,7 @@ async def worker(
|
|
|
226
226
|
status = "error"
|
|
227
227
|
run_ended_at = datetime.now(UTC).isoformat()
|
|
228
228
|
await logger.aexception(
|
|
229
|
-
"Background run failed",
|
|
229
|
+
f"Background run failed. Exception: {exc}",
|
|
230
230
|
exc_info=not isinstance(exc, RemoteException),
|
|
231
231
|
run_id=str(run_id),
|
|
232
232
|
run_attempt=attempt,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: langgraph-api
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.33
|
|
4
4
|
Summary:
|
|
5
5
|
License: Elastic-2.0
|
|
6
6
|
Author: Nuno Campos
|
|
@@ -16,7 +16,7 @@ Requires-Dist: jsonschema-rs (>=0.20.0,<0.30)
|
|
|
16
16
|
Requires-Dist: langchain-core (>=0.2.38,<0.4.0)
|
|
17
17
|
Requires-Dist: langgraph (>=0.2.56,<0.4.0)
|
|
18
18
|
Requires-Dist: langgraph-checkpoint (>=2.0.21,<3.0)
|
|
19
|
-
Requires-Dist: langgraph-sdk (>=0.1.
|
|
19
|
+
Requires-Dist: langgraph-sdk (>=0.1.58,<0.2.0)
|
|
20
20
|
Requires-Dist: langsmith (>=0.1.63,<0.4.0)
|
|
21
21
|
Requires-Dist: orjson (>=3.9.7)
|
|
22
22
|
Requires-Dist: pyjwt (>=2.9.0,<3.0.0)
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
LICENSE,sha256=ZPwVR73Biwm3sK6vR54djCrhaRiM4cAD2zvOQZV8Xis,3859
|
|
2
2
|
langgraph_api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
|
-
langgraph_api/api/__init__.py,sha256=
|
|
4
|
-
langgraph_api/api/assistants.py,sha256=
|
|
3
|
+
langgraph_api/api/__init__.py,sha256=iDxgg0arqzZ0-roCSuWo3f8zXmKcl4fuU3Jw0-lqtZs,5447
|
|
4
|
+
langgraph_api/api/assistants.py,sha256=nU6tnbgdr_6Utlq0A9nw2a6xxpUM_DNuCFI42_Kcs_o,14233
|
|
5
|
+
langgraph_api/api/mcp.py,sha256=dpKT9DgIoLERTmYZ4sSOPyHbfGbm7hCyb2MrMS_ol18,13593
|
|
5
6
|
langgraph_api/api/meta.py,sha256=ifJ_Ki0Qf2DYbmY6OKlqKhLGxbt55gm0lEqH1A0cJbw,2790
|
|
6
7
|
langgraph_api/api/openapi.py,sha256=f9gfmWN2AMKNUpLCpSgZuw_aeOF9jCXPdOtFT5PaTWM,10960
|
|
7
8
|
langgraph_api/api/runs.py,sha256=_RWKtmjD89ALnTk56dwo2rJwEi2oghk2Tqp0l1aCcZg,16677
|
|
8
9
|
langgraph_api/api/store.py,sha256=VzAJVOwO0IxosBB7km5TTf2rhlWGyPkVz_LpvbxetVY,5437
|
|
9
|
-
langgraph_api/api/threads.py,sha256=
|
|
10
|
+
langgraph_api/api/threads.py,sha256=meaDGF3R2bYkx0KRa_le4Ka5nOSeqlMDWtLdnEhVYSY,8930
|
|
10
11
|
langgraph_api/api/ui.py,sha256=LiOZVewKOPbKEykCm30hCEaOA7vuS_Ti5hB32EEy4vw,2082
|
|
11
|
-
langgraph_api/asyncio.py,sha256=
|
|
12
|
+
langgraph_api/asyncio.py,sha256=hVuAxWTHoUyNqTzcIEKTkAvh7HFrdGK6WIDowDxORAE,8397
|
|
12
13
|
langgraph_api/auth/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
13
14
|
langgraph_api/auth/custom.py,sha256=z69UvpG7wPSXtRmp_UFmA8fY7HeQD4AEIWu3qlpJOBc,21210
|
|
14
15
|
langgraph_api/auth/langsmith/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -17,11 +18,12 @@ langgraph_api/auth/langsmith/client.py,sha256=eKchvAom7hdkUXauD8vHNceBDDUijrFgdT
|
|
|
17
18
|
langgraph_api/auth/middleware.py,sha256=jU8aDSIZHdzCGdifejRF7ndHkSjBtqIHcBwFIuUdHEA,1875
|
|
18
19
|
langgraph_api/auth/noop.py,sha256=Bk6Nf3p8D_iMVy_OyfPlyiJp_aEwzL-sHrbxoXpCbac,586
|
|
19
20
|
langgraph_api/auth/studio_user.py,sha256=FzFQRROKDlA9JjtBuwyZvk6Mbwno5M9RVYjDO6FU3F8,186
|
|
20
|
-
langgraph_api/cli.py,sha256=
|
|
21
|
-
langgraph_api/
|
|
21
|
+
langgraph_api/cli.py,sha256=17mTzIhAEaNy9THdtsTqaEBsi5h0J0Tu-EAtv4Qbz5U,11787
|
|
22
|
+
langgraph_api/command.py,sha256=3O9v3i0OPa96ARyJ_oJbLXkfO8rPgDhLCswgO9koTFA,768
|
|
23
|
+
langgraph_api/config.py,sha256=52qcOo9dBfynCozLwzO-Dhew-WgXjPVR2-9nNp6Q-nY,8703
|
|
22
24
|
langgraph_api/cron_scheduler.py,sha256=9yzbbGxzNgJdIg4ZT7yu2oTwT_wRuPxD1c2sbbd52xs,2630
|
|
23
25
|
langgraph_api/errors.py,sha256=Bu_i5drgNTyJcLiyrwVE_6-XrSU50BHf9TDpttki9wQ,1690
|
|
24
|
-
langgraph_api/graph.py,sha256=
|
|
26
|
+
langgraph_api/graph.py,sha256=5Lo9FghptdPIFJoeTDkLjoIQLuNy49RWtAHJmzOCrZI,17209
|
|
25
27
|
langgraph_api/http.py,sha256=gYbxxjY8aLnsXeJymcJ7G7Nj_yToOGpPYQqmZ1_ggfA,5240
|
|
26
28
|
langgraph_api/js/.gitignore,sha256=l5yI6G_V6F1600I1IjiUKn87f4uYIrBAYU1MOyBBhg4,59
|
|
27
29
|
langgraph_api/js/base.py,sha256=BpE8-xkUp8HFPRjSKx1tfUQubvoV4jYl6OwZdre3veI,209
|
|
@@ -30,7 +32,7 @@ langgraph_api/js/client.mts,sha256=2dptAX8fMowV9OC4DU4khjpZUgALBLVBTu3jTQbeUJY,2
|
|
|
30
32
|
langgraph_api/js/errors.py,sha256=Cm1TKWlUCwZReDC5AQ6SgNIVGD27Qov2xcgHyf8-GXo,361
|
|
31
33
|
langgraph_api/js/global.d.ts,sha256=cLJRZfYVGmgQ6o_xFevVNNTIi918ZUdxVRnpLVSjiAY,133
|
|
32
34
|
langgraph_api/js/package.json,sha256=j6DMoVgwRqWqTwdd7R1f-kvmiTUAbO3HaUhM8K64lbE,1224
|
|
33
|
-
langgraph_api/js/remote.py,sha256=
|
|
35
|
+
langgraph_api/js/remote.py,sha256=g0H2x3W7kejxswhgZfFaljzl4Y7agMCnO5BdNVj1rDY,23474
|
|
34
36
|
langgraph_api/js/schema.py,sha256=7idnv7URlYUdSNMBXQcw7E4SxaPxCq_Oxwnlml8q5ik,408
|
|
35
37
|
langgraph_api/js/src/graph.mts,sha256=mRyMUp03Fwd5DlmNIFl3RiUCQuJ5XwmFp1AfAeKDfVc,3169
|
|
36
38
|
langgraph_api/js/src/hooks.mjs,sha256=XtktgmIHlls_DsknAuwib-z7TqCm0haRoTXvnkgzMuo,601
|
|
@@ -58,14 +60,14 @@ langgraph_api/js/tests/graphs/yarn.lock,sha256=i2AAIgXA3XBLM8-oU45wgUefCSG-Tne4g
|
|
|
58
60
|
langgraph_api/js/tests/parser.test.mts,sha256=dEC8KTqKygeb1u39ZvpPqCT4HtfPD947nLmITt2buxA,27883
|
|
59
61
|
langgraph_api/js/tests/utils.mts,sha256=2kTybJ3O7Yfe1q3ehDouqV54ibXkNzsPZ_wBZLJvY-4,421
|
|
60
62
|
langgraph_api/js/yarn.lock,sha256=W89dVYZMThcec08lJMcYnvEEnQK7VM5cPglvwpIdRv0,82773
|
|
61
|
-
langgraph_api/lifespan.py,sha256=
|
|
62
|
-
langgraph_api/logging.py,sha256=
|
|
63
|
-
langgraph_api/metadata.py,sha256=
|
|
63
|
+
langgraph_api/lifespan.py,sha256=sUQnwszj3VaHKUyOLL8E7Rn7D4S_Vde0U69l739CgOQ,2472
|
|
64
|
+
langgraph_api/logging.py,sha256=JJIzbNIgLCN6ClQ3tA-Mm5ffuBGvpRDSZsEvnIlsuu4,3693
|
|
65
|
+
langgraph_api/metadata.py,sha256=5Mu3MUtUc-iIocU3X2SZDoGIqnUmTdT3517MhP94npI,3495
|
|
64
66
|
langgraph_api/middleware/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
65
67
|
langgraph_api/middleware/http_logger.py,sha256=yuFPNFIWwn-4AE1CogBfWlo8KytzywLi_Bd4ccsyVQE,3150
|
|
66
68
|
langgraph_api/middleware/private_network.py,sha256=eYgdyU8AzU2XJu362i1L8aSFoQRiV7_aLBPw7_EgeqI,2111
|
|
67
69
|
langgraph_api/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
68
|
-
langgraph_api/models/run.py,sha256=
|
|
70
|
+
langgraph_api/models/run.py,sha256=J1DpfMwv7W3zX9WtCANyObaCuZWBVfz2JWgdIBa9VzU,10180
|
|
69
71
|
langgraph_api/patch.py,sha256=82xjuFqY7tgrUm-k1XWHI6k8S6QovSD0zhe--8_xW4o,1296
|
|
70
72
|
langgraph_api/queue_entrypoint.py,sha256=4xICUxXarNV8DhnaqAMhVi3xCmyVKCL3J5NzHxPA9Xc,1835
|
|
71
73
|
langgraph_api/route.py,sha256=fM4qYCGbmH0a3_cV8uKocb1sLklehxO6HhdRXqLK6OM,4421
|
|
@@ -74,11 +76,11 @@ langgraph_api/serde.py,sha256=VoJ7Z1IuqrQGXFzEP1qijAITtWCrmjtVqlCRuScjXJI,3533
|
|
|
74
76
|
langgraph_api/server.py,sha256=CiNK327zTsEpoVGeJK1JOtZHvOBYRoz0CnBTZUmsC7c,4567
|
|
75
77
|
langgraph_api/sse.py,sha256=2wNodCOP2eg7a9mpSu0S3FQ0CHk2BBV_vv0UtIgJIcc,4034
|
|
76
78
|
langgraph_api/state.py,sha256=8jx4IoTCOjTJuwzuXJKKFwo1VseHjNnw_CCq4x1SW14,2284
|
|
77
|
-
langgraph_api/stream.py,sha256=
|
|
79
|
+
langgraph_api/stream.py,sha256=lhjnom-T8GbquUZry-KSkajnqYjElaIERhPiXPtpw1E,11354
|
|
78
80
|
langgraph_api/utils.py,sha256=92mSti9GfGdMRRWyESKQW5yV-75Z9icGHnIrBYvdypU,3619
|
|
79
|
-
langgraph_api/validation.py,sha256=
|
|
81
|
+
langgraph_api/validation.py,sha256=LnEdfgID2Z0HP75R_hqWxCn5d5ULGcDJ3r1xaDVv6-w,4845
|
|
80
82
|
langgraph_api/webhook.py,sha256=1ncwO0rIZcj-Df9sxSnFEzd1gP1bfS4okeZQS8NSRoE,1382
|
|
81
|
-
langgraph_api/worker.py,sha256=
|
|
83
|
+
langgraph_api/worker.py,sha256=zlkk2yg1tOBQHYwN-WHjc9NJgd3CAvsl8ET0-7tqQIk,9838
|
|
82
84
|
langgraph_license/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
83
85
|
langgraph_license/middleware.py,sha256=_ODIYzQkymr6W9_Fp9wtf1kAQspnpsmr53xuzyF2GA0,612
|
|
84
86
|
langgraph_license/validation.py,sha256=Uu_G8UGO_WTlLsBEY0gTVWjRR4czYGfw5YAD3HLZoj0,203
|
|
@@ -86,15 +88,15 @@ langgraph_storage/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU
|
|
|
86
88
|
langgraph_storage/checkpoint.py,sha256=V4t2GwYEJdPCHbhq_4Udhlv0TWKDzlMu_rlNPdTDc50,3589
|
|
87
89
|
langgraph_storage/database.py,sha256=I0AgFeJ-NSTT34vxKxQBUf1z2syFP0S8QpKCqTixrzY,5652
|
|
88
90
|
langgraph_storage/inmem_stream.py,sha256=8bxkILIuFpr7P7RQ37SQAxrpRKvmbHdRB_nbfFiomlk,3263
|
|
89
|
-
langgraph_storage/ops.py,sha256=
|
|
91
|
+
langgraph_storage/ops.py,sha256=0DhtDQllBnV495DPrwhbR_bQUg7ZrGBe0FxSZcicz5g,72469
|
|
90
92
|
langgraph_storage/queue.py,sha256=UDgsUTtUMfBSRDrQ8Onis-FJO4n7KTsX6sdpbY8Hs0A,5055
|
|
91
93
|
langgraph_storage/retry.py,sha256=XmldOP4e_H5s264CagJRVnQMDFcEJR_dldVR1Hm5XvM,763
|
|
92
94
|
langgraph_storage/store.py,sha256=33-J5-Xvobb9ArSa-GezP5KtfXgzWkHUHPyjRYmdw-E,2985
|
|
93
95
|
langgraph_storage/ttl_dict.py,sha256=FlpEY8EANeXWKo_G5nmIotPquABZGyIJyk6HD9u6vqY,1533
|
|
94
96
|
logging.json,sha256=3RNjSADZmDq38eHePMm1CbP6qZ71AmpBtLwCmKU9Zgo,379
|
|
95
|
-
openapi.json,sha256=
|
|
96
|
-
langgraph_api-0.0.
|
|
97
|
-
langgraph_api-0.0.
|
|
98
|
-
langgraph_api-0.0.
|
|
99
|
-
langgraph_api-0.0.
|
|
100
|
-
langgraph_api-0.0.
|
|
97
|
+
openapi.json,sha256=6okfuwPAKB2Hi1tup-EFza49pmN_iD2Yt5JktMALcq4,127984
|
|
98
|
+
langgraph_api-0.0.33.dist-info/LICENSE,sha256=ZPwVR73Biwm3sK6vR54djCrhaRiM4cAD2zvOQZV8Xis,3859
|
|
99
|
+
langgraph_api-0.0.33.dist-info/METADATA,sha256=PCu4vrh8uCMFyww-JUdBxsrQFN8NrAX9hckbr3SPem0,4027
|
|
100
|
+
langgraph_api-0.0.33.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
101
|
+
langgraph_api-0.0.33.dist-info/entry_points.txt,sha256=3EYLgj89DfzqJHHYGxPH4A_fEtClvlRbWRUHaXO7hj4,77
|
|
102
|
+
langgraph_api-0.0.33.dist-info/RECORD,,
|
langgraph_storage/ops.py
CHANGED
|
@@ -22,6 +22,7 @@ from starlette.exceptions import HTTPException
|
|
|
22
22
|
|
|
23
23
|
from langgraph_api.asyncio import SimpleTaskGroup, ValueEvent, create_task
|
|
24
24
|
from langgraph_api.auth.custom import handle_event
|
|
25
|
+
from langgraph_api.command import map_cmd
|
|
25
26
|
from langgraph_api.errors import UserInterrupt, UserRollback
|
|
26
27
|
from langgraph_api.graph import get_graph
|
|
27
28
|
from langgraph_api.schema import (
|
|
@@ -1081,6 +1082,78 @@ class Threads(Authenticated):
|
|
|
1081
1082
|
else:
|
|
1082
1083
|
raise HTTPException(status_code=400, detail="Thread has no graph ID.")
|
|
1083
1084
|
|
|
1085
|
+
@staticmethod
|
|
1086
|
+
async def bulk(
|
|
1087
|
+
conn: InMemConnectionProto,
|
|
1088
|
+
*,
|
|
1089
|
+
config: Config,
|
|
1090
|
+
supersteps: Sequence[dict],
|
|
1091
|
+
ctx: Auth.types.BaseAuthContext | None = None,
|
|
1092
|
+
) -> ThreadUpdateResponse:
|
|
1093
|
+
"""Update a thread with a batch of state updates."""
|
|
1094
|
+
|
|
1095
|
+
from langgraph.pregel.types import StateUpdate
|
|
1096
|
+
|
|
1097
|
+
thread_id = _ensure_uuid(config["configurable"]["thread_id"])
|
|
1098
|
+
filters = await Threads.handle_event(
|
|
1099
|
+
ctx,
|
|
1100
|
+
"update",
|
|
1101
|
+
Auth.types.ThreadsUpdate(thread_id=thread_id),
|
|
1102
|
+
)
|
|
1103
|
+
|
|
1104
|
+
thread_iter = await Threads.get(conn, thread_id, ctx=ctx)
|
|
1105
|
+
thread = await fetchone(
|
|
1106
|
+
thread_iter, not_found_detail=f"Thread {thread_id} not found."
|
|
1107
|
+
)
|
|
1108
|
+
|
|
1109
|
+
thread_config = thread["config"]
|
|
1110
|
+
metadata = thread["metadata"]
|
|
1111
|
+
|
|
1112
|
+
if not thread:
|
|
1113
|
+
raise HTTPException(status_code=404, detail="Thread not found")
|
|
1114
|
+
|
|
1115
|
+
if not _check_filter_match(metadata, filters):
|
|
1116
|
+
raise HTTPException(status_code=403, detail="Forbidden")
|
|
1117
|
+
|
|
1118
|
+
if graph_id := metadata.get("graph_id"):
|
|
1119
|
+
config["configurable"].setdefault("graph_id", graph_id)
|
|
1120
|
+
config["configurable"].setdefault("checkpoint_ns", "")
|
|
1121
|
+
|
|
1122
|
+
async with get_graph(
|
|
1123
|
+
graph_id, thread_config, checkpointer=Checkpointer(conn)
|
|
1124
|
+
) as graph:
|
|
1125
|
+
next_config = await graph.abulk_update_state(
|
|
1126
|
+
config,
|
|
1127
|
+
[
|
|
1128
|
+
[
|
|
1129
|
+
StateUpdate(
|
|
1130
|
+
map_cmd(update.get("command"))
|
|
1131
|
+
if update.get("command")
|
|
1132
|
+
else update.get("values"),
|
|
1133
|
+
update.get("as_node"),
|
|
1134
|
+
)
|
|
1135
|
+
for update in superstep.get("updates", [])
|
|
1136
|
+
]
|
|
1137
|
+
for superstep in supersteps
|
|
1138
|
+
],
|
|
1139
|
+
)
|
|
1140
|
+
|
|
1141
|
+
state = await Threads.State.get(
|
|
1142
|
+
conn, config, subgraphs=False, ctx=ctx
|
|
1143
|
+
)
|
|
1144
|
+
|
|
1145
|
+
# update thread values
|
|
1146
|
+
for thread in conn.store["threads"]:
|
|
1147
|
+
if thread["thread_id"] == thread_id:
|
|
1148
|
+
thread["values"] = state.values
|
|
1149
|
+
break
|
|
1150
|
+
|
|
1151
|
+
return ThreadUpdateResponse(
|
|
1152
|
+
checkpoint=next_config["configurable"],
|
|
1153
|
+
)
|
|
1154
|
+
else:
|
|
1155
|
+
raise HTTPException(status_code=400, detail="Thread has no graph ID")
|
|
1156
|
+
|
|
1084
1157
|
@staticmethod
|
|
1085
1158
|
async def list(
|
|
1086
1159
|
conn: InMemConnectionProto,
|
|
@@ -1905,7 +1978,8 @@ class Crons:
|
|
|
1905
1978
|
conn: InMemConnectionProto,
|
|
1906
1979
|
ctx: Auth.types.BaseAuthContext | None = None,
|
|
1907
1980
|
) -> AsyncIterator[Cron]:
|
|
1908
|
-
|
|
1981
|
+
yield
|
|
1982
|
+
raise NotImplementedError("The in-mem server does not implement Crons.")
|
|
1909
1983
|
|
|
1910
1984
|
@staticmethod
|
|
1911
1985
|
async def set_next_run_date(
|