langgraph-api 0.0.2__tar.gz → 0.0.4__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.0.2 → langgraph_api-0.0.4}/PKG-INFO +3 -3
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/langgraph_api/cli.py +36 -2
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/langgraph_api/graph.py +23 -13
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/langgraph_api/js/src/hooks.mjs +5 -1
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/langgraph_api/queue.py +9 -1
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/langgraph_api/schema.py +2 -0
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/langgraph_api/stream.py +18 -9
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/langgraph_license/validation.py +1 -1
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/langgraph_storage/checkpoint.py +3 -1
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/langgraph_storage/ops.py +9 -0
- langgraph_api-0.0.4/langgraph_storage/store.py +44 -0
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/pyproject.toml +3 -3
- langgraph_api-0.0.2/langgraph_storage/store.py +0 -28
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/LICENSE +0 -0
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/README.md +0 -0
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/langgraph_api/__init__.py +0 -0
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/langgraph_api/api/__init__.py +0 -0
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/langgraph_api/api/assistants.py +0 -0
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/langgraph_api/api/meta.py +0 -0
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/langgraph_api/api/openapi.py +0 -0
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/langgraph_api/api/runs.py +0 -0
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/langgraph_api/api/store.py +0 -0
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/langgraph_api/api/threads.py +0 -0
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/langgraph_api/asyncio.py +0 -0
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/langgraph_api/auth/__init__.py +0 -0
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/langgraph_api/auth/langsmith/__init__.py +0 -0
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/langgraph_api/auth/langsmith/backend.py +0 -0
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/langgraph_api/auth/langsmith/client.py +0 -0
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/langgraph_api/auth/middleware.py +0 -0
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/langgraph_api/auth/noop.py +0 -0
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/langgraph_api/config.py +0 -0
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/langgraph_api/cron_scheduler.py +0 -0
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/langgraph_api/errors.py +0 -0
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/langgraph_api/http.py +0 -0
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/langgraph_api/http_logger.py +0 -0
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/langgraph_api/js/.gitignore +0 -0
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/langgraph_api/js/build.mts +0 -0
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/langgraph_api/js/client.mts +0 -0
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/langgraph_api/js/global.d.ts +0 -0
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/langgraph_api/js/package.json +0 -0
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/langgraph_api/js/remote.py +0 -0
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/langgraph_api/js/server_sent_events.py +0 -0
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/langgraph_api/js/src/graph.mts +0 -0
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/langgraph_api/js/src/parser/parser.mts +0 -0
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/langgraph_api/js/src/parser/parser.worker.mjs +0 -0
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/langgraph_api/js/src/schema/types.mts +0 -0
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/langgraph_api/js/src/schema/types.template.mts +0 -0
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/langgraph_api/js/src/utils/importMap.mts +0 -0
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/langgraph_api/js/src/utils/pythonSchemas.mts +0 -0
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/langgraph_api/js/src/utils/serde.mts +0 -0
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/langgraph_api/js/tests/api.test.mts +0 -0
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/langgraph_api/js/tests/compose-postgres.yml +0 -0
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/langgraph_api/js/tests/graphs/.gitignore +0 -0
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/langgraph_api/js/tests/graphs/agent.mts +0 -0
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/langgraph_api/js/tests/graphs/error.mts +0 -0
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/langgraph_api/js/tests/graphs/langgraph.json +0 -0
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/langgraph_api/js/tests/graphs/nested.mts +0 -0
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/langgraph_api/js/tests/graphs/package.json +0 -0
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/langgraph_api/js/tests/graphs/weather.mts +0 -0
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/langgraph_api/js/tests/graphs/yarn.lock +0 -0
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/langgraph_api/js/tests/parser.test.mts +0 -0
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/langgraph_api/js/tests/utils.mts +0 -0
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/langgraph_api/js/yarn.lock +0 -0
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/langgraph_api/lifespan.py +0 -0
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/langgraph_api/logging.py +0 -0
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/langgraph_api/metadata.py +0 -0
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/langgraph_api/models/__init__.py +0 -0
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/langgraph_api/models/run.py +0 -0
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/langgraph_api/patch.py +0 -0
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/langgraph_api/route.py +0 -0
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/langgraph_api/serde.py +0 -0
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/langgraph_api/server.py +0 -0
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/langgraph_api/sse.py +0 -0
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/langgraph_api/state.py +0 -0
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/langgraph_api/utils.py +0 -0
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/langgraph_api/validation.py +0 -0
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/langgraph_license/__init__.py +0 -0
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/langgraph_license/middleware.py +0 -0
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/langgraph_storage/__init__.py +0 -0
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/langgraph_storage/database.py +0 -0
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/langgraph_storage/queue.py +0 -0
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/langgraph_storage/retry.py +0 -0
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/langgraph_storage/ttl_dict.py +0 -0
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/logging.json +0 -0
- {langgraph_api-0.0.2 → langgraph_api-0.0.4}/openapi.json +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: langgraph-api
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.4
|
|
4
4
|
Summary:
|
|
5
5
|
License: Elastic-2.0
|
|
6
6
|
Author: Nuno Campos
|
|
@@ -14,8 +14,8 @@ Requires-Dist: cryptography (>=43.0.3,<44.0.0)
|
|
|
14
14
|
Requires-Dist: httpx (>=0.27.0)
|
|
15
15
|
Requires-Dist: jsonschema-rs (>=0.25.0,<0.26.0)
|
|
16
16
|
Requires-Dist: langchain-core (>=0.2.38,<0.4.0)
|
|
17
|
-
Requires-Dist: langgraph (>=0.2.52)
|
|
18
|
-
Requires-Dist: langgraph-checkpoint (>=2.0.
|
|
17
|
+
Requires-Dist: langgraph (>=0.2.52,<0.3.0)
|
|
18
|
+
Requires-Dist: langgraph-checkpoint (>=2.0.6,<3.0)
|
|
19
19
|
Requires-Dist: langsmith (>=0.1.63,<0.2.0)
|
|
20
20
|
Requires-Dist: orjson (>=3.10.1)
|
|
21
21
|
Requires-Dist: pyjwt (>=2.9.0,<3.0.0)
|
|
@@ -4,12 +4,35 @@ import logging
|
|
|
4
4
|
import os
|
|
5
5
|
import pathlib
|
|
6
6
|
import threading
|
|
7
|
-
from collections.abc import Mapping
|
|
7
|
+
from collections.abc import Mapping, Sequence
|
|
8
8
|
|
|
9
9
|
logging.basicConfig(level=logging.INFO)
|
|
10
10
|
logger = logging.getLogger(__name__)
|
|
11
11
|
|
|
12
12
|
|
|
13
|
+
def _get_org_id() -> str | None:
|
|
14
|
+
from langsmith.client import Client
|
|
15
|
+
from langsmith.utils import tracing_is_enabled
|
|
16
|
+
|
|
17
|
+
# Yes, the organizationId is actually the workspace iD
|
|
18
|
+
# which is actually the tenantID which we actually get via
|
|
19
|
+
# the sessions endpoint
|
|
20
|
+
|
|
21
|
+
if not tracing_is_enabled():
|
|
22
|
+
return
|
|
23
|
+
client = Client()
|
|
24
|
+
try:
|
|
25
|
+
response = client.request_with_retries(
|
|
26
|
+
"GET", "/api/v1/sessions", params={"limit": 1}
|
|
27
|
+
)
|
|
28
|
+
result = response.json()
|
|
29
|
+
if result:
|
|
30
|
+
return result[0]["tenant_id"]
|
|
31
|
+
except Exception as e:
|
|
32
|
+
logger.debug("Failed to get organization ID: %s", str(e))
|
|
33
|
+
return None
|
|
34
|
+
|
|
35
|
+
|
|
13
36
|
@contextlib.contextmanager
|
|
14
37
|
def patch_environment(**kwargs):
|
|
15
38
|
"""Temporarily patch environment variables.
|
|
@@ -47,6 +70,8 @@ def run_server(
|
|
|
47
70
|
open_browser: bool = False,
|
|
48
71
|
debug_port: int | None = None,
|
|
49
72
|
env: str | pathlib.Path | Mapping[str, str] | None = None,
|
|
73
|
+
reload_includes: Sequence[str] | None = None,
|
|
74
|
+
reload_excludes: Sequence[str] | None = None,
|
|
50
75
|
):
|
|
51
76
|
"""Run the LangGraph API server."""
|
|
52
77
|
import uvicorn
|
|
@@ -90,14 +115,21 @@ def run_server(
|
|
|
90
115
|
studio_url = f"https://smith.langchain.com/studio/?baseUrl={local_url}"
|
|
91
116
|
|
|
92
117
|
def _open_browser():
|
|
118
|
+
nonlocal studio_url
|
|
93
119
|
import time
|
|
94
120
|
import urllib.request
|
|
95
121
|
import webbrowser
|
|
96
122
|
|
|
123
|
+
org_id = _get_org_id()
|
|
124
|
+
if org_id:
|
|
125
|
+
studio_url = f"https://smith.langchain.com/studio/?baseUrl={local_url}&organizationId={org_id}"
|
|
126
|
+
|
|
97
127
|
while True:
|
|
98
128
|
try:
|
|
99
129
|
with urllib.request.urlopen(f"{local_url}/ok") as response:
|
|
100
130
|
if response.status == 200:
|
|
131
|
+
logger.info("🎨 Opening Studio in your browser...")
|
|
132
|
+
logger.info("URL: " + studio_url)
|
|
101
133
|
webbrowser.open(studio_url)
|
|
102
134
|
return
|
|
103
135
|
except urllib.error.URLError:
|
|
@@ -127,7 +159,7 @@ For production use, please use LangGraph Cloud.
|
|
|
127
159
|
REDIS_URI="fake",
|
|
128
160
|
N_JOBS_PER_WORKER=str(n_jobs_per_worker if n_jobs_per_worker else 1),
|
|
129
161
|
LANGSERVE_GRAPHS=json.dumps(graphs) if graphs else None,
|
|
130
|
-
LANGSMITH_LANGGRAPH_API_VARIANT="
|
|
162
|
+
LANGSMITH_LANGGRAPH_API_VARIANT="local_dev",
|
|
131
163
|
**(env_vars or {}),
|
|
132
164
|
):
|
|
133
165
|
if open_browser:
|
|
@@ -140,6 +172,8 @@ For production use, please use LangGraph Cloud.
|
|
|
140
172
|
reload=reload,
|
|
141
173
|
env_file=env_file,
|
|
142
174
|
access_log=False,
|
|
175
|
+
reload_includes=reload_includes,
|
|
176
|
+
reload_excludes=reload_excludes,
|
|
143
177
|
log_config={
|
|
144
178
|
"version": 1,
|
|
145
179
|
"incremental": False,
|
|
@@ -157,18 +157,20 @@ async def collect_graphs_from_env(register: bool = False) -> None:
|
|
|
157
157
|
|
|
158
158
|
if paths_str:
|
|
159
159
|
specs = [
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
160
|
+
(
|
|
161
|
+
GraphSpec(
|
|
162
|
+
key,
|
|
163
|
+
module=value.split(":")[0],
|
|
164
|
+
variable=value.split(":")[1],
|
|
165
|
+
config=config_per_graph.get(key),
|
|
166
|
+
)
|
|
167
|
+
if "/" not in value
|
|
168
|
+
else GraphSpec(
|
|
169
|
+
key,
|
|
170
|
+
path=value.split(":")[0],
|
|
171
|
+
variable=value.split(":")[1],
|
|
172
|
+
config=config_per_graph.get(key),
|
|
173
|
+
)
|
|
172
174
|
)
|
|
173
175
|
for key, value in json.loads(paths_str).items()
|
|
174
176
|
]
|
|
@@ -268,7 +270,15 @@ def _graph_from_spec(spec: GraphSpec) -> GraphValue:
|
|
|
268
270
|
modspec.loader.exec_module(module)
|
|
269
271
|
except ImportError as e:
|
|
270
272
|
e.add_note(f"Could not import python module for graph: {spec}")
|
|
271
|
-
|
|
273
|
+
if os.environ.get("LANGSMITH_LANGGRAPH_API_VARIANT") == "local_dev":
|
|
274
|
+
e.add_note(
|
|
275
|
+
"This error likely means you haven't installed your project and its dependencies yet. Before running the server, install your project:\n\n"
|
|
276
|
+
"If you are using requirements.txt:\n"
|
|
277
|
+
"python -m pip install -r requirements.txt\n\n"
|
|
278
|
+
"If you are using pyproject.toml or setuptools:\n"
|
|
279
|
+
"python -m pip install -e .\n\n"
|
|
280
|
+
"Make sure to run this command from your project's root directory (where your setup.py or pyproject.toml is located)"
|
|
281
|
+
)
|
|
272
282
|
else:
|
|
273
283
|
raise ValueError("Graph specification must have a path or module")
|
|
274
284
|
|
|
@@ -5,7 +5,11 @@ export async function resolve(specifier, context, nextResolve) {
|
|
|
5
5
|
const parentURL = new URL("./graph.mts", import.meta.url).toString();
|
|
6
6
|
|
|
7
7
|
if (specifier.startsWith("@langchain/langgraph")) {
|
|
8
|
-
|
|
8
|
+
try {
|
|
9
|
+
return nextResolve(specifier, { ...context, parentURL });
|
|
10
|
+
} catch (error) {
|
|
11
|
+
return nextResolve(specifier, context);
|
|
12
|
+
}
|
|
9
13
|
}
|
|
10
14
|
|
|
11
15
|
return nextResolve(specifier, context);
|
|
@@ -5,7 +5,7 @@ from random import random
|
|
|
5
5
|
from typing import cast
|
|
6
6
|
|
|
7
7
|
import structlog
|
|
8
|
-
from langgraph.pregel.debug import CheckpointPayload
|
|
8
|
+
from langgraph.pregel.debug import CheckpointPayload, TaskResultPayload
|
|
9
9
|
|
|
10
10
|
from langgraph_api.config import BG_JOB_NO_DELAY, STATS_INTERVAL_SECS
|
|
11
11
|
from langgraph_api.errors import (
|
|
@@ -129,6 +129,13 @@ async def worker(
|
|
|
129
129
|
nonlocal checkpoint
|
|
130
130
|
checkpoint = checkpoint_arg
|
|
131
131
|
|
|
132
|
+
def on_task_result(task_result: TaskResultPayload):
|
|
133
|
+
if checkpoint is not None:
|
|
134
|
+
for task in checkpoint["tasks"]:
|
|
135
|
+
if task["id"] == task_result["id"]:
|
|
136
|
+
task.update(task_result)
|
|
137
|
+
break
|
|
138
|
+
|
|
132
139
|
try:
|
|
133
140
|
if attempt > MAX_RETRY_ATTEMPTS:
|
|
134
141
|
raise RuntimeError(f"Run {run['run_id']} exceeded max attempts")
|
|
@@ -144,6 +151,7 @@ async def worker(
|
|
|
144
151
|
attempt,
|
|
145
152
|
done,
|
|
146
153
|
on_checkpoint=on_checkpoint,
|
|
154
|
+
on_task_result=on_task_result,
|
|
147
155
|
)
|
|
148
156
|
await asyncio.wait_for(consume(stream, run_id), timeout)
|
|
149
157
|
await logger.ainfo(
|
|
@@ -98,6 +98,8 @@ class Thread(TypedDict):
|
|
|
98
98
|
"""The status of the thread. One of 'idle', 'busy', 'interrupted', "error"."""
|
|
99
99
|
values: Fragment
|
|
100
100
|
"""The current state of the thread."""
|
|
101
|
+
interrupts: Fragment
|
|
102
|
+
"""The current interrupts of the thread, a map of task_id to list of interrupts."""
|
|
101
103
|
|
|
102
104
|
|
|
103
105
|
class ThreadTask(TypedDict):
|
|
@@ -18,7 +18,7 @@ from langgraph.errors import (
|
|
|
18
18
|
GraphRecursionError,
|
|
19
19
|
InvalidUpdateError,
|
|
20
20
|
)
|
|
21
|
-
from langgraph.pregel.debug import CheckpointPayload
|
|
21
|
+
from langgraph.pregel.debug import CheckpointPayload, TaskResultPayload
|
|
22
22
|
from langgraph.types import Command, Send
|
|
23
23
|
from pydantic import ValidationError
|
|
24
24
|
from pydantic.v1 import ValidationError as ValidationErrorLegacy
|
|
@@ -90,10 +90,11 @@ async def astream_state(
|
|
|
90
90
|
done: ValueEvent,
|
|
91
91
|
*,
|
|
92
92
|
on_checkpoint: Callable[[CheckpointPayload], None] = lambda _: None,
|
|
93
|
+
on_task_result: Callable[[TaskResultPayload], None] = lambda _: None,
|
|
93
94
|
) -> AnyStream:
|
|
94
95
|
"""Stream messages from the runnable."""
|
|
95
96
|
run_id = str(run["run_id"])
|
|
96
|
-
await stack.enter_async_context(conn.pipeline())
|
|
97
|
+
pipe = await stack.enter_async_context(conn.pipeline())
|
|
97
98
|
# extract args from run
|
|
98
99
|
kwargs = run["kwargs"].copy()
|
|
99
100
|
subgraphs = kwargs.get("subgraphs", False)
|
|
@@ -102,7 +103,7 @@ async def astream_state(
|
|
|
102
103
|
graph = get_graph(
|
|
103
104
|
config["configurable"]["graph_id"],
|
|
104
105
|
config,
|
|
105
|
-
store=None if not conn else Store(conn),
|
|
106
|
+
store=None if not conn else Store(conn, pipe=pipe),
|
|
106
107
|
checkpointer=None if temporary else Checkpointer(conn),
|
|
107
108
|
)
|
|
108
109
|
input = kwargs.pop("input")
|
|
@@ -174,6 +175,8 @@ async def astream_state(
|
|
|
174
175
|
if chunk["type"] == "checkpoint":
|
|
175
176
|
checkpoint = _preprocess_debug_checkpoint(chunk["payload"])
|
|
176
177
|
on_checkpoint(checkpoint)
|
|
178
|
+
elif chunk["type"] == "task_result":
|
|
179
|
+
on_task_result(chunk["payload"])
|
|
177
180
|
if mode == "messages":
|
|
178
181
|
if "messages-tuple" in stream_mode:
|
|
179
182
|
yield "messages", chunk
|
|
@@ -185,9 +188,11 @@ async def astream_state(
|
|
|
185
188
|
messages[msg.id] = msg
|
|
186
189
|
yield "messages/metadata", {msg.id: {"metadata": meta}}
|
|
187
190
|
yield (
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
+
(
|
|
192
|
+
"messages/partial"
|
|
193
|
+
if isinstance(msg, BaseMessageChunk)
|
|
194
|
+
else "messages/complete"
|
|
195
|
+
),
|
|
191
196
|
[message_chunk_to_message(messages[msg.id])],
|
|
192
197
|
)
|
|
193
198
|
elif mode in stream_mode:
|
|
@@ -221,6 +226,8 @@ async def astream_state(
|
|
|
221
226
|
if chunk["type"] == "checkpoint":
|
|
222
227
|
checkpoint = _preprocess_debug_checkpoint(chunk["payload"])
|
|
223
228
|
on_checkpoint(checkpoint)
|
|
229
|
+
elif chunk["type"] == "task_result":
|
|
230
|
+
on_task_result(chunk["payload"])
|
|
224
231
|
if mode == "messages":
|
|
225
232
|
if "messages-tuple" in stream_mode:
|
|
226
233
|
yield "messages", chunk
|
|
@@ -232,9 +239,11 @@ async def astream_state(
|
|
|
232
239
|
messages[msg.id] = msg
|
|
233
240
|
yield "messages/metadata", {msg.id: {"metadata": meta}}
|
|
234
241
|
yield (
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
242
|
+
(
|
|
243
|
+
"messages/partial"
|
|
244
|
+
if isinstance(msg, BaseMessageChunk)
|
|
245
|
+
else "messages/complete"
|
|
246
|
+
),
|
|
238
247
|
[message_chunk_to_message(messages[msg.id])],
|
|
239
248
|
)
|
|
240
249
|
elif mode in stream_mode:
|
|
@@ -10,6 +10,8 @@ from langgraph.checkpoint.base import (
|
|
|
10
10
|
)
|
|
11
11
|
from langgraph.checkpoint.memory import MemorySaver, PersistentDict
|
|
12
12
|
|
|
13
|
+
from langgraph_api.serde import Serializer
|
|
14
|
+
|
|
13
15
|
_EXCLUDED_KEYS = {"checkpoint_ns", "checkpoint_id", "run_id", "thread_id"}
|
|
14
16
|
|
|
15
17
|
|
|
@@ -37,7 +39,7 @@ class InMemorySaver(MemorySaver):
|
|
|
37
39
|
return d
|
|
38
40
|
|
|
39
41
|
super().__init__(
|
|
40
|
-
serde=serde,
|
|
42
|
+
serde=serde if serde is not None else Serializer(),
|
|
41
43
|
factory=factory,
|
|
42
44
|
)
|
|
43
45
|
|
|
@@ -583,6 +583,15 @@ class Threads:
|
|
|
583
583
|
"updated_at": datetime.now(UTC),
|
|
584
584
|
"values": checkpoint["values"] if checkpoint else None,
|
|
585
585
|
"status": status,
|
|
586
|
+
"interrupts": (
|
|
587
|
+
{
|
|
588
|
+
t["id"]: t["interrupts"]
|
|
589
|
+
for t in checkpoint["tasks"]
|
|
590
|
+
if t["interrupts"]
|
|
591
|
+
}
|
|
592
|
+
if checkpoint
|
|
593
|
+
else {}
|
|
594
|
+
),
|
|
586
595
|
}
|
|
587
596
|
)
|
|
588
597
|
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
from langgraph.checkpoint.memory import PersistentDict
|
|
5
|
+
from langgraph.store.memory import InMemoryStore
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class DiskBackedInMemStore(InMemoryStore):
|
|
9
|
+
def __init__(self, *args: Any, filename: str | None = None, **kwargs: Any) -> None:
|
|
10
|
+
super().__init__(*args, **kwargs)
|
|
11
|
+
self.filename = filename
|
|
12
|
+
self._data = PersistentDict(dict, filename=self.filename)
|
|
13
|
+
self._load_data()
|
|
14
|
+
|
|
15
|
+
def _load_data(self) -> None:
|
|
16
|
+
if not self.filename:
|
|
17
|
+
return
|
|
18
|
+
try:
|
|
19
|
+
self._data.load()
|
|
20
|
+
except FileNotFoundError:
|
|
21
|
+
# It's okay if the file doesn't exist yet
|
|
22
|
+
pass
|
|
23
|
+
except (EOFError, ValueError) as e:
|
|
24
|
+
raise RuntimeError(
|
|
25
|
+
f"Failed to load store from {self.filename}. "
|
|
26
|
+
"This may be due to changes in the stored data structure. "
|
|
27
|
+
"Consider clearing the local store by running: rm -rf .langgraph_api"
|
|
28
|
+
) from e
|
|
29
|
+
except Exception as e:
|
|
30
|
+
raise RuntimeError(
|
|
31
|
+
f"Unexpected error loading store from {self.filename}: {str(e)}"
|
|
32
|
+
) from e
|
|
33
|
+
|
|
34
|
+
def close(self) -> None:
|
|
35
|
+
self._data.close()
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
_STORE_FILE = os.path.join(".langgraph_api", "store.pckl")
|
|
39
|
+
os.makedirs(".langgraph_api", exist_ok=True)
|
|
40
|
+
STORE = DiskBackedInMemStore(filename=_STORE_FILE)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def Store(*args: Any, **kwargs: Any) -> DiskBackedInMemStore:
|
|
44
|
+
return STORE
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "langgraph-api"
|
|
3
|
-
version = "0.0.
|
|
3
|
+
version = "0.0.4"
|
|
4
4
|
description = ""
|
|
5
5
|
authors = [
|
|
6
6
|
"Nuno Campos <nuno@langchain.dev>",
|
|
@@ -23,8 +23,8 @@ python = ">=3.11.0,<4.0"
|
|
|
23
23
|
sse-starlette = "^2.1.0"
|
|
24
24
|
starlette = ">=0.38.6"
|
|
25
25
|
watchfiles = ">=0.13"
|
|
26
|
-
langgraph = ">=0.2.52"
|
|
27
|
-
langgraph-checkpoint = ">=2.0.
|
|
26
|
+
langgraph = ">=0.2.52,<0.3.0"
|
|
27
|
+
langgraph-checkpoint = ">=2.0.6,<3.0"
|
|
28
28
|
orjson = ">=3.10.1"
|
|
29
29
|
uvicorn = ">=0.26.0"
|
|
30
30
|
langsmith = "^0.1.63"
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
|
|
3
|
-
from langgraph.checkpoint.memory import PersistentDict
|
|
4
|
-
from langgraph.store.memory import InMemoryStore
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
class DiskBackedInMemStore(InMemoryStore):
|
|
8
|
-
def __init__(self, *args, filename=None, **kwargs):
|
|
9
|
-
super().__init__(*args, **kwargs)
|
|
10
|
-
self.filename = filename
|
|
11
|
-
self._data = PersistentDict(dict, filename=self.filename)
|
|
12
|
-
try:
|
|
13
|
-
self._data.load()
|
|
14
|
-
except FileNotFoundError:
|
|
15
|
-
pass
|
|
16
|
-
|
|
17
|
-
def close(self):
|
|
18
|
-
self._data.close()
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
_STORE_FILE = os.path.join(".langgraph_api", "store.pckl")
|
|
22
|
-
if not os.path.exists(".langgraph_api"):
|
|
23
|
-
os.mkdir(".langgraph_api")
|
|
24
|
-
STORE = DiskBackedInMemStore(filename=_STORE_FILE)
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
def Store(*args, **kwargs):
|
|
28
|
-
return STORE
|
|
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
|
|
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
|