langgraph-api 0.0.46__py3-none-any.whl → 0.0.47__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/graph.py +79 -21
- langgraph_api/js/client.mts +35 -3
- langgraph_api/js/remote.py +9 -3
- langgraph_api/js/tests/api.test.mts +75 -2
- langgraph_api/js/tests/compose-postgres.yml +1 -1
- langgraph_api/js/tests/graphs/command.mts +48 -0
- langgraph_api/js/tests/graphs/langgraph.json +2 -1
- langgraph_api/patch.py +4 -0
- {langgraph_api-0.0.46.dist-info → langgraph_api-0.0.47.dist-info}/METADATA +1 -1
- {langgraph_api-0.0.46.dist-info → langgraph_api-0.0.47.dist-info}/RECORD +15 -14
- openapi.json +8 -1
- {langgraph_api-0.0.46.dist-info → langgraph_api-0.0.47.dist-info}/LICENSE +0 -0
- {langgraph_api-0.0.46.dist-info → langgraph_api-0.0.47.dist-info}/WHEEL +0 -0
- {langgraph_api-0.0.46.dist-info → langgraph_api-0.0.47.dist-info}/entry_points.txt +0 -0
langgraph_api/__init__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.0.
|
|
1
|
+
__version__ = "0.0.47"
|
langgraph_api/graph.py
CHANGED
|
@@ -42,7 +42,13 @@ NAMESPACE_GRAPH = UUID("6ba7b821-9dad-11d1-80b4-00c04fd430c8")
|
|
|
42
42
|
FACTORY_ACCEPTS_CONFIG: dict[str, bool] = {}
|
|
43
43
|
|
|
44
44
|
|
|
45
|
-
async def register_graph(
|
|
45
|
+
async def register_graph(
|
|
46
|
+
graph_id: str,
|
|
47
|
+
graph: GraphValue,
|
|
48
|
+
config: dict | None,
|
|
49
|
+
*,
|
|
50
|
+
description: str | None = None,
|
|
51
|
+
) -> None:
|
|
46
52
|
"""Register a graph."""
|
|
47
53
|
from langgraph_storage.database import connect
|
|
48
54
|
from langgraph_storage.ops import Assistants
|
|
@@ -60,6 +66,7 @@ async def register_graph(graph_id: str, graph: GraphValue, config: dict | None)
|
|
|
60
66
|
config=config or {},
|
|
61
67
|
if_exists="do_nothing",
|
|
62
68
|
name=graph_id,
|
|
69
|
+
description=description,
|
|
63
70
|
)
|
|
64
71
|
|
|
65
72
|
|
|
@@ -149,13 +156,26 @@ def get_assistant_id(assistant_id: str) -> str:
|
|
|
149
156
|
|
|
150
157
|
|
|
151
158
|
class GraphSpec(NamedTuple):
|
|
152
|
-
"""A graph specification.
|
|
159
|
+
"""A graph specification.
|
|
160
|
+
|
|
161
|
+
This is a definition of the graph that can be used to load the graph
|
|
162
|
+
from a file or module.
|
|
163
|
+
"""
|
|
153
164
|
|
|
154
165
|
id: str
|
|
166
|
+
"""The ID of the graph."""
|
|
155
167
|
path: str | None = None
|
|
156
168
|
module: str | None = None
|
|
157
169
|
variable: str | None = None
|
|
158
170
|
config: dict | None = None
|
|
171
|
+
"""The configuration for the graph.
|
|
172
|
+
|
|
173
|
+
Contains information such as: tags, recursion_limit and configurable.
|
|
174
|
+
|
|
175
|
+
Configurable is a dict containing user defined values for the graph.
|
|
176
|
+
"""
|
|
177
|
+
description: str | None = None
|
|
178
|
+
"""A description of the graph"""
|
|
159
179
|
|
|
160
180
|
|
|
161
181
|
js_bg_tasks: set[asyncio.Task] = set()
|
|
@@ -193,9 +213,33 @@ async def collect_graphs_from_env(register: bool = False) -> None:
|
|
|
193
213
|
|
|
194
214
|
if paths_str:
|
|
195
215
|
specs = []
|
|
196
|
-
|
|
216
|
+
# graphs-config can be either a mapping from graph id to path where the graph
|
|
217
|
+
# is defined or graph id to a dictionary containing information about the graph.
|
|
218
|
+
graphs_config = json.loads(paths_str)
|
|
219
|
+
|
|
220
|
+
for key, value in graphs_config.items():
|
|
221
|
+
if isinstance(value, dict) and "path" in value:
|
|
222
|
+
source = value["path"]
|
|
223
|
+
elif isinstance(value, str):
|
|
224
|
+
source = value
|
|
225
|
+
else:
|
|
226
|
+
msg = (
|
|
227
|
+
f"Invalid value '{value}' for graph '{key}'. "
|
|
228
|
+
"Expected a string or a dictionary. "
|
|
229
|
+
"If a string, it should be the path to the graph definition. "
|
|
230
|
+
"For example: '/path/to/graph.py:graph_variable' "
|
|
231
|
+
"or 'my.module:graph_variable'. "
|
|
232
|
+
"If a dictionary, then it needs to contains a `path` key with the "
|
|
233
|
+
"path to the graph definition."
|
|
234
|
+
"It can also contains additional configuration for the graph; "
|
|
235
|
+
"e.g., `description`."
|
|
236
|
+
"For example: {'path': '/path/to/graph.py:graph_variable', "
|
|
237
|
+
"'description': 'My graph'}"
|
|
238
|
+
)
|
|
239
|
+
raise TypeError(msg)
|
|
240
|
+
|
|
197
241
|
try:
|
|
198
|
-
path_or_module, variable =
|
|
242
|
+
path_or_module, variable = source.rsplit(":", maxsplit=1)
|
|
199
243
|
except ValueError as e:
|
|
200
244
|
raise ValueError(
|
|
201
245
|
f"Invalid path '{value}' for graph '{key}'."
|
|
@@ -203,22 +247,30 @@ async def collect_graphs_from_env(register: bool = False) -> None:
|
|
|
203
247
|
" Expected one of the following formats:"
|
|
204
248
|
" 'my.module:variable_name' or '/path/to/file.py:variable_name'"
|
|
205
249
|
) from e
|
|
250
|
+
|
|
251
|
+
graph_config = config_per_graph.get(key, {})
|
|
252
|
+
description = (
|
|
253
|
+
value.get("description", None) if isinstance(value, dict) else None
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
# Module syntax uses `.` instead of `/` to separate directories
|
|
257
|
+
if "/" in path_or_module:
|
|
258
|
+
path = path_or_module
|
|
259
|
+
module_ = None
|
|
260
|
+
else:
|
|
261
|
+
path = None
|
|
262
|
+
module_ = path_or_module
|
|
263
|
+
|
|
206
264
|
specs.append(
|
|
207
265
|
GraphSpec(
|
|
208
266
|
key,
|
|
209
|
-
module=
|
|
210
|
-
|
|
211
|
-
config=config_per_graph.get(key),
|
|
212
|
-
)
|
|
213
|
-
if "/" not in value
|
|
214
|
-
else GraphSpec(
|
|
215
|
-
key,
|
|
216
|
-
path=path_or_module,
|
|
267
|
+
module=module_,
|
|
268
|
+
path=path,
|
|
217
269
|
variable=variable,
|
|
218
|
-
config=
|
|
270
|
+
config=graph_config,
|
|
271
|
+
description=description,
|
|
219
272
|
)
|
|
220
273
|
)
|
|
221
|
-
|
|
222
274
|
else:
|
|
223
275
|
specs = [
|
|
224
276
|
GraphSpec(
|
|
@@ -270,12 +322,16 @@ async def collect_graphs_from_env(register: bool = False) -> None:
|
|
|
270
322
|
for spec in js_specs:
|
|
271
323
|
graph = RemotePregel(graph_id=spec.id)
|
|
272
324
|
if register:
|
|
273
|
-
await register_graph(
|
|
325
|
+
await register_graph(
|
|
326
|
+
spec.id, graph, spec.config, description=spec.description
|
|
327
|
+
)
|
|
274
328
|
|
|
275
329
|
for spec in py_specs:
|
|
276
330
|
graph = await run_in_executor(None, _graph_from_spec, spec)
|
|
277
331
|
if register:
|
|
278
|
-
await register_graph(
|
|
332
|
+
await register_graph(
|
|
333
|
+
spec.id, graph, spec.config, description=spec.description
|
|
334
|
+
)
|
|
279
335
|
|
|
280
336
|
|
|
281
337
|
def _handle_exception(task: asyncio.Task) -> None:
|
|
@@ -375,14 +431,16 @@ def _graph_from_spec(spec: GraphSpec) -> GraphValue:
|
|
|
375
431
|
# We don't want to fail real deployments, but this will help folks catch unnecessary custom components
|
|
376
432
|
# before they deploy
|
|
377
433
|
if config.API_VARIANT == "local_dev":
|
|
378
|
-
has_checkpointer = graph.checkpointer
|
|
379
|
-
has_store = graph.store
|
|
434
|
+
has_checkpointer = isinstance(graph.checkpointer, BaseCheckpointSaver)
|
|
435
|
+
has_store = isinstance(graph.store, BaseStore)
|
|
380
436
|
if has_checkpointer or has_store:
|
|
381
437
|
components = []
|
|
382
438
|
if has_checkpointer:
|
|
383
|
-
components.append(
|
|
439
|
+
components.append(
|
|
440
|
+
f"checkpointer (type {type(graph.checkpointer)})"
|
|
441
|
+
)
|
|
384
442
|
if has_store:
|
|
385
|
-
components.append("store")
|
|
443
|
+
components.append(f"store (type {type(graph.store)})")
|
|
386
444
|
component_list = " and ".join(components)
|
|
387
445
|
|
|
388
446
|
raise ValueError(
|
|
@@ -391,7 +449,7 @@ def _graph_from_spec(spec: GraphSpec) -> GraphValue:
|
|
|
391
449
|
f"so providing a custom {component_list} here isn't necessary and will be ignored when deployed.\n\n"
|
|
392
450
|
f"To simplify your setup and use the built-in persistence, please remove the custom {component_list} "
|
|
393
451
|
f"from your graph definition. If you are looking to customize which postgres database to connect to,"
|
|
394
|
-
" please set the `
|
|
452
|
+
" please set the `POSTGRES_URI` environment variable."
|
|
395
453
|
" See https://langchain-ai.github.io/langgraph/cloud/reference/env_var/#postgres_uri_custom for more details."
|
|
396
454
|
)
|
|
397
455
|
|
langgraph_api/js/client.mts
CHANGED
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
Item,
|
|
14
14
|
Operation,
|
|
15
15
|
Command,
|
|
16
|
+
Send,
|
|
16
17
|
OperationResults,
|
|
17
18
|
type Checkpoint,
|
|
18
19
|
type CheckpointMetadata,
|
|
@@ -539,7 +540,19 @@ const StreamEventsPayload = z.object({
|
|
|
539
540
|
graph_name: z.string().nullish(),
|
|
540
541
|
graph_config: RunnableConfigSchema.nullish(),
|
|
541
542
|
input: z.unknown(),
|
|
542
|
-
command: z
|
|
543
|
+
command: z
|
|
544
|
+
.object({
|
|
545
|
+
resume: z.unknown().nullish(),
|
|
546
|
+
goto: z.custom<Send | string | (Send | string)[]>().nullish(),
|
|
547
|
+
graph: z.string().nullish(),
|
|
548
|
+
update: z
|
|
549
|
+
.union([
|
|
550
|
+
z.record(z.unknown()),
|
|
551
|
+
z.array(z.tuple([z.string(), z.unknown()])),
|
|
552
|
+
])
|
|
553
|
+
.nullish(),
|
|
554
|
+
})
|
|
555
|
+
.nullish(),
|
|
543
556
|
stream_mode: z
|
|
544
557
|
.union([ExtraStreamModeSchema, z.array(ExtraStreamModeSchema)])
|
|
545
558
|
.optional(),
|
|
@@ -549,14 +562,33 @@ const StreamEventsPayload = z.object({
|
|
|
549
562
|
subgraphs: z.boolean().optional(),
|
|
550
563
|
});
|
|
551
564
|
|
|
565
|
+
function reviveCommand(
|
|
566
|
+
command: z.infer<typeof StreamEventsPayload>["command"]
|
|
567
|
+
): Command | undefined {
|
|
568
|
+
if (command == null) return undefined;
|
|
569
|
+
let { goto, update, resume, graph } = command;
|
|
570
|
+
|
|
571
|
+
goto ??= undefined;
|
|
572
|
+
update ??= undefined;
|
|
573
|
+
resume ??= undefined;
|
|
574
|
+
graph ??= undefined;
|
|
575
|
+
|
|
576
|
+
if (goto != null && !Array.isArray(goto)) goto = [goto];
|
|
577
|
+
goto = goto?.map((item) => {
|
|
578
|
+
if (typeof item === "string") return item;
|
|
579
|
+
return new Send(item.node, item.args);
|
|
580
|
+
});
|
|
581
|
+
|
|
582
|
+
return new Command({ goto, update, resume, graph });
|
|
583
|
+
}
|
|
584
|
+
|
|
552
585
|
async function* streamEventsRequest(
|
|
553
586
|
rawPayload: z.infer<typeof StreamEventsPayload>
|
|
554
587
|
) {
|
|
555
588
|
const { graph_id: graphId, ...payload } = rawPayload;
|
|
556
589
|
const config = getRunnableConfig(payload.config);
|
|
557
590
|
const graph = await getGraph(graphId, config, payload.graph_name);
|
|
558
|
-
|
|
559
|
-
const input = payload.command ? new Command(payload.command) : payload.input;
|
|
591
|
+
const input = reviveCommand(payload.command) ?? payload.input;
|
|
560
592
|
|
|
561
593
|
const userStreamMode =
|
|
562
594
|
payload.stream_mode == null
|
langgraph_api/js/remote.py
CHANGED
|
@@ -23,7 +23,7 @@ from langchain_core.runnables.schema import (
|
|
|
23
23
|
from langgraph.checkpoint.serde.base import SerializerProtocol
|
|
24
24
|
from langgraph.pregel.types import PregelTask, StateSnapshot
|
|
25
25
|
from langgraph.store.base import GetOp, Item, ListNamespacesOp, PutOp, SearchOp
|
|
26
|
-
from langgraph.types import Command, Interrupt
|
|
26
|
+
from langgraph.types import Command, Interrupt, Send
|
|
27
27
|
from pydantic import BaseModel
|
|
28
28
|
from starlette.applications import Starlette
|
|
29
29
|
from starlette.exceptions import HTTPException
|
|
@@ -58,6 +58,12 @@ _client = httpx.AsyncClient(
|
|
|
58
58
|
)
|
|
59
59
|
|
|
60
60
|
|
|
61
|
+
def default_command(obj):
|
|
62
|
+
if isinstance(obj, Send):
|
|
63
|
+
return {"node": obj.node, "args": obj.arg}
|
|
64
|
+
raise TypeError
|
|
65
|
+
|
|
66
|
+
|
|
61
67
|
async def _client_stream(method: str, data: dict[str, Any]):
|
|
62
68
|
graph_id = data.get("graph_id")
|
|
63
69
|
async with _client.stream(
|
|
@@ -68,7 +74,7 @@ async def _client_stream(method: str, data: dict[str, Any]):
|
|
|
68
74
|
"Cache-Control": "no-store",
|
|
69
75
|
"Content-Type": "application/json",
|
|
70
76
|
},
|
|
71
|
-
data=orjson.dumps(data),
|
|
77
|
+
data=orjson.dumps(data, default=default_command),
|
|
72
78
|
) as response:
|
|
73
79
|
decoder = SSEDecoder()
|
|
74
80
|
async for line in aiter_lines_raw(response):
|
|
@@ -84,7 +90,7 @@ async def _client_invoke(method: str, data: dict[str, Any]):
|
|
|
84
90
|
res = await _client.post(
|
|
85
91
|
f"/{graph_id}/{method}",
|
|
86
92
|
headers={"Content-Type": "application/json"},
|
|
87
|
-
data=orjson.dumps(data),
|
|
93
|
+
data=orjson.dumps(data, default=default_command),
|
|
88
94
|
)
|
|
89
95
|
return res.json()
|
|
90
96
|
|
|
@@ -1769,8 +1769,7 @@ it("unusual newline termination characters", async () => {
|
|
|
1769
1769
|
);
|
|
1770
1770
|
});
|
|
1771
1771
|
|
|
1772
|
-
|
|
1773
|
-
describe.skip("command update state", () => {
|
|
1772
|
+
describe("command update state", () => {
|
|
1774
1773
|
it("updates state via commands", async () => {
|
|
1775
1774
|
const assistant = await client.assistants.create({ graphId: "agent" });
|
|
1776
1775
|
const thread = await client.threads.create();
|
|
@@ -1829,6 +1828,80 @@ describe.skip("command update state", () => {
|
|
|
1829
1828
|
keyTwo: "value2",
|
|
1830
1829
|
});
|
|
1831
1830
|
});
|
|
1831
|
+
|
|
1832
|
+
it("goto skip start + map-reduce", async () => {
|
|
1833
|
+
const assistant = await client.assistants.create({ graphId: "command" });
|
|
1834
|
+
let thread = await client.threads.create();
|
|
1835
|
+
|
|
1836
|
+
await client.runs.wait(thread.thread_id, assistant.assistant_id, {
|
|
1837
|
+
command: { goto: "map" },
|
|
1838
|
+
config: globalConfig,
|
|
1839
|
+
});
|
|
1840
|
+
|
|
1841
|
+
let state = await client.threads.getState(thread.thread_id);
|
|
1842
|
+
expect(state.values.messages).toEqual([
|
|
1843
|
+
"map",
|
|
1844
|
+
"task: 1",
|
|
1845
|
+
"task: 2",
|
|
1846
|
+
"task: 3",
|
|
1847
|
+
]);
|
|
1848
|
+
|
|
1849
|
+
await client.runs.wait(thread.thread_id, assistant.assistant_id, {
|
|
1850
|
+
command: {
|
|
1851
|
+
goto: [
|
|
1852
|
+
{ node: "task", input: { value: 4 } },
|
|
1853
|
+
{ node: "task", input: { value: 5 } },
|
|
1854
|
+
{ node: "task", input: { value: 6 } },
|
|
1855
|
+
],
|
|
1856
|
+
},
|
|
1857
|
+
config: globalConfig,
|
|
1858
|
+
});
|
|
1859
|
+
|
|
1860
|
+
state = await client.threads.getState(thread.thread_id);
|
|
1861
|
+
expect(state.values.messages).toEqual([
|
|
1862
|
+
"map",
|
|
1863
|
+
"task: 1",
|
|
1864
|
+
"task: 2",
|
|
1865
|
+
"task: 3",
|
|
1866
|
+
"task: 4",
|
|
1867
|
+
"task: 5",
|
|
1868
|
+
"task: 6",
|
|
1869
|
+
]);
|
|
1870
|
+
});
|
|
1871
|
+
|
|
1872
|
+
it("goto interrupt", async () => {
|
|
1873
|
+
const assistant = await client.assistants.create({ graphId: "command" });
|
|
1874
|
+
let thread = await client.threads.create({ graphId: "command" });
|
|
1875
|
+
|
|
1876
|
+
let stream = await gatherIterator(
|
|
1877
|
+
client.runs.stream(thread.thread_id, assistant.assistant_id, {
|
|
1878
|
+
// TODO: figure out why we cannot go to the interrupt node directly
|
|
1879
|
+
command: { goto: "before_interrupt" },
|
|
1880
|
+
})
|
|
1881
|
+
);
|
|
1882
|
+
|
|
1883
|
+
let state = await client.threads.getState(thread.thread_id);
|
|
1884
|
+
expect(state.next).toEqual(["interrupt"]);
|
|
1885
|
+
expect(state.tasks).toMatchObject([
|
|
1886
|
+
{
|
|
1887
|
+
name: "interrupt",
|
|
1888
|
+
interrupts: [{ value: "interrupt", resumable: true, when: "during" }],
|
|
1889
|
+
},
|
|
1890
|
+
]);
|
|
1891
|
+
|
|
1892
|
+
stream = await gatherIterator(
|
|
1893
|
+
client.runs.stream(thread.thread_id, assistant.assistant_id, {
|
|
1894
|
+
command: { resume: "resume" },
|
|
1895
|
+
streamMode: ["updates"],
|
|
1896
|
+
})
|
|
1897
|
+
);
|
|
1898
|
+
|
|
1899
|
+
state = await client.threads.getState(thread.thread_id);
|
|
1900
|
+
expect(state.values.messages).toEqual([
|
|
1901
|
+
"before_interrupt",
|
|
1902
|
+
"interrupt: resume",
|
|
1903
|
+
]);
|
|
1904
|
+
});
|
|
1832
1905
|
});
|
|
1833
1906
|
|
|
1834
1907
|
it("dynamic graph", async () => {
|
|
@@ -34,7 +34,7 @@ services:
|
|
|
34
34
|
ADD . /deps/graphs
|
|
35
35
|
WORKDIR /deps/graphs
|
|
36
36
|
RUN yarn install --frozen-lockfile
|
|
37
|
-
ENV LANGSERVE_GRAPHS='{"agent":"./agent.mts:graph", "nested": "./nested.mts:graph", "weather": "./weather.mts:graph", "error": "./error.mts:graph", "delay": "./delay.mts:graph", "dynamic": "./dynamic.mts:graph"}'
|
|
37
|
+
ENV LANGSERVE_GRAPHS='{"agent":"./agent.mts:graph", "nested": "./nested.mts:graph", "weather": "./weather.mts:graph", "error": "./error.mts:graph", "delay": "./delay.mts:graph", "dynamic": "./dynamic.mts:graph", "command": "./command.mts:graph"}'
|
|
38
38
|
ENV LANGGRAPH_CONFIG='{"agent": {"configurable": {"model_name": "openai"}}}'
|
|
39
39
|
ENV LANGGRAPH_UI='{"agent": "./agent.ui.tsx"}'
|
|
40
40
|
RUN tsx /api/langgraph_api/js/build.mts
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Annotation,
|
|
3
|
+
Command,
|
|
4
|
+
END,
|
|
5
|
+
interrupt,
|
|
6
|
+
Send,
|
|
7
|
+
START,
|
|
8
|
+
StateGraph,
|
|
9
|
+
} from "@langchain/langgraph";
|
|
10
|
+
|
|
11
|
+
const StateSchema = Annotation.Root({
|
|
12
|
+
messages: Annotation<string[]>({
|
|
13
|
+
reducer: (a: string[], b: string | string[]) => [
|
|
14
|
+
...a,
|
|
15
|
+
...(Array.isArray(b) ? b : [b]),
|
|
16
|
+
],
|
|
17
|
+
default: () => [],
|
|
18
|
+
}),
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
export const graph = new StateGraph(StateSchema)
|
|
22
|
+
.addNode("router", () => new Command({ goto: END }), {
|
|
23
|
+
ends: ["before_interrupt", "map", END],
|
|
24
|
+
})
|
|
25
|
+
.addNode("before_interrupt", () => ({ messages: ["before_interrupt"] }))
|
|
26
|
+
.addNode("interrupt", () => {
|
|
27
|
+
const resolved = interrupt("interrupt");
|
|
28
|
+
return { messages: [`interrupt: ${resolved}`] };
|
|
29
|
+
})
|
|
30
|
+
.addNode(
|
|
31
|
+
"map",
|
|
32
|
+
() =>
|
|
33
|
+
new Command({
|
|
34
|
+
update: { messages: ["map"] },
|
|
35
|
+
goto: [
|
|
36
|
+
new Send("task", { value: 1 }),
|
|
37
|
+
new Send("task", { value: 2 }),
|
|
38
|
+
new Send("task", { value: 3 }),
|
|
39
|
+
],
|
|
40
|
+
}),
|
|
41
|
+
{ ends: ["task"] }
|
|
42
|
+
)
|
|
43
|
+
.addNode("task", (arg: { value: number }) => ({
|
|
44
|
+
messages: [`task: ${arg.value}`],
|
|
45
|
+
}))
|
|
46
|
+
.addEdge(START, "router")
|
|
47
|
+
.addEdge("before_interrupt", "interrupt")
|
|
48
|
+
.compile();
|
langgraph_api/patch.py
CHANGED
|
@@ -3,6 +3,8 @@ from typing import Any
|
|
|
3
3
|
from starlette.responses import Response, StreamingResponse
|
|
4
4
|
from starlette.types import Send
|
|
5
5
|
|
|
6
|
+
from langgraph_api.serde import Fragment
|
|
7
|
+
|
|
6
8
|
"""
|
|
7
9
|
Patch Response.render and StreamingResponse.stream_response
|
|
8
10
|
to recognize bytearrays and memoryviews as bytes-like objects.
|
|
@@ -28,6 +30,8 @@ async def StreamingResponse_stream_response(self, send: Send) -> None:
|
|
|
28
30
|
async for chunk in self.body_iterator:
|
|
29
31
|
if chunk is None:
|
|
30
32
|
continue
|
|
33
|
+
if isinstance(chunk, Fragment):
|
|
34
|
+
chunk = chunk.buf
|
|
31
35
|
if not isinstance(chunk, (bytes, bytearray, memoryview)): # noqa: UP038
|
|
32
36
|
chunk = chunk.encode(self.charset)
|
|
33
37
|
await send({"type": "http.response.body", "body": chunk, "more_body": True})
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
LICENSE,sha256=ZPwVR73Biwm3sK6vR54djCrhaRiM4cAD2zvOQZV8Xis,3859
|
|
2
|
-
langgraph_api/__init__.py,sha256=
|
|
2
|
+
langgraph_api/__init__.py,sha256=OkbXUm6WCcFd54358Y0HZk3Aq5hLc0sK6xgxJ6RmT5M,23
|
|
3
3
|
langgraph_api/api/__init__.py,sha256=qNcg8QJydef0gM-vYJlxITMRZw-9r1vw8zqm2raqqYE,5493
|
|
4
4
|
langgraph_api/api/assistants.py,sha256=WwaBtx1MpGn9gdJ8P9fkorJHMVrJKHt1nvtw0OCZcdw,14353
|
|
5
5
|
langgraph_api/api/mcp.py,sha256=KbR19dtFCpJEiKYj3IfepAuJij8YZVELuVp7JY_yu_o,13754
|
|
@@ -23,16 +23,16 @@ langgraph_api/command.py,sha256=3O9v3i0OPa96ARyJ_oJbLXkfO8rPgDhLCswgO9koTFA,768
|
|
|
23
23
|
langgraph_api/config.py,sha256=OJ6_9UCkPYNpUEB17EIU1nO0zQd3_T3G1Dr-ag0J8hE,10218
|
|
24
24
|
langgraph_api/cron_scheduler.py,sha256=9yzbbGxzNgJdIg4ZT7yu2oTwT_wRuPxD1c2sbbd52xs,2630
|
|
25
25
|
langgraph_api/errors.py,sha256=Bu_i5drgNTyJcLiyrwVE_6-XrSU50BHf9TDpttki9wQ,1690
|
|
26
|
-
langgraph_api/graph.py,sha256=
|
|
26
|
+
langgraph_api/graph.py,sha256=v7qj60MBQKfEx_0kmw9AsPygCQsOLmrJ1MkVkt4-GiM,21086
|
|
27
27
|
langgraph_api/http.py,sha256=gYbxxjY8aLnsXeJymcJ7G7Nj_yToOGpPYQqmZ1_ggfA,5240
|
|
28
28
|
langgraph_api/js/.gitignore,sha256=l5yI6G_V6F1600I1IjiUKn87f4uYIrBAYU1MOyBBhg4,59
|
|
29
29
|
langgraph_api/js/base.py,sha256=xkBp5bwRrbpMFaAMViEU-qIlnsJuu3X_G8sa1pqNZK0,227
|
|
30
30
|
langgraph_api/js/build.mts,sha256=oyP1GTnWAWl_dC4ny-h_9WxvWfZ8eJihGdrVdmMe81k,1834
|
|
31
|
-
langgraph_api/js/client.mts,sha256=
|
|
31
|
+
langgraph_api/js/client.mts,sha256=GJv846jWBRL0W_Yr6XtDu3UdYjdpndQ2v6dOfAhJXi0,26953
|
|
32
32
|
langgraph_api/js/errors.py,sha256=Cm1TKWlUCwZReDC5AQ6SgNIVGD27Qov2xcgHyf8-GXo,361
|
|
33
33
|
langgraph_api/js/global.d.ts,sha256=yDusqAyzVYhxfwqqcERUzucu2Pw9ma3-ug4DFyUvQfs,167
|
|
34
34
|
langgraph_api/js/package.json,sha256=oNcWe7UIoJtqzoIVr47px6-1n8KziqwEhyi4wBpzTQ0,1266
|
|
35
|
-
langgraph_api/js/remote.py,sha256=
|
|
35
|
+
langgraph_api/js/remote.py,sha256=dDrxJhe9Nm1OQREoa811GeXHygiBHTtHjvEVglOGfto,24808
|
|
36
36
|
langgraph_api/js/schema.py,sha256=7idnv7URlYUdSNMBXQcw7E4SxaPxCq_Oxwnlml8q5ik,408
|
|
37
37
|
langgraph_api/js/src/graph.mts,sha256=otgztTNzNJpeF2IOrpNuuwbSbpAy4eFE5dHtUd7eQwU,3742
|
|
38
38
|
langgraph_api/js/src/hooks.mjs,sha256=XtktgmIHlls_DsknAuwib-z7TqCm0haRoTXvnkgzMuo,601
|
|
@@ -44,16 +44,17 @@ langgraph_api/js/src/utils/importMap.mts,sha256=pX4TGOyUpuuWF82kXcxcv3-8mgusRezO
|
|
|
44
44
|
langgraph_api/js/src/utils/pythonSchemas.mts,sha256=98IW7Z_VP7L_CHNRMb3_MsiV3BgLE2JsWQY_PQcRR3o,685
|
|
45
45
|
langgraph_api/js/src/utils/serde.mts,sha256=OuyyO9btvwWd55rU_H4x91dFEJiaPxL-lL9O6Zgo908,742
|
|
46
46
|
langgraph_api/js/sse.py,sha256=lsfp4nyJyA1COmlKG9e2gJnTttf_HGCB5wyH8OZBER8,4105
|
|
47
|
-
langgraph_api/js/tests/api.test.mts,sha256=
|
|
48
|
-
langgraph_api/js/tests/compose-postgres.yml,sha256=
|
|
47
|
+
langgraph_api/js/tests/api.test.mts,sha256=cu2d5De-QsQUH9-e1vjxDOcUfPRV3bRKWuymAAOZeXk,62845
|
|
48
|
+
langgraph_api/js/tests/compose-postgres.yml,sha256=yz99nUy4Gm8vXuE5cnp2ekPzv7x5XWdwFPB9muAqeGE,1861
|
|
49
49
|
langgraph_api/js/tests/graphs/.gitignore,sha256=26J8MarZNXh7snXD5eTpV3CPFTht5Znv8dtHYCLNfkw,12
|
|
50
50
|
langgraph_api/js/tests/graphs/agent.css,sha256=QgcOC0W7IBsrg4pSqqpull-WTgtULZfx_lF_5ZxLdag,23
|
|
51
51
|
langgraph_api/js/tests/graphs/agent.mts,sha256=E9WMv0alMv0njUEECqEsqoRk9NXJUgXW7SyQJ3GOZ8k,5396
|
|
52
52
|
langgraph_api/js/tests/graphs/agent.ui.tsx,sha256=JDFJdpdIS6rglkXTaROSb1Is0j1kt5wN9ML8W4cuht8,175
|
|
53
|
+
langgraph_api/js/tests/graphs/command.mts,sha256=YO1_cEs_n9VsH_-IediLnI4Yc8KRlp4qrprUZXpAlwM,1163
|
|
53
54
|
langgraph_api/js/tests/graphs/delay.mts,sha256=CFneKxqI4bGGK0lYjSbe80QirowPQlsRSuhDUKfydhk,703
|
|
54
55
|
langgraph_api/js/tests/graphs/dynamic.mts,sha256=Wf_-keF7lkEfp_iyI45nlFGCeU8ARLQ8axc0LXh7TyE,659
|
|
55
56
|
langgraph_api/js/tests/graphs/error.mts,sha256=l4tk89449dj1BnEF_0ZcfPt0Ikk1gl8L1RaSnRfr3xo,487
|
|
56
|
-
langgraph_api/js/tests/graphs/langgraph.json,sha256=
|
|
57
|
+
langgraph_api/js/tests/graphs/langgraph.json,sha256=h6hV1wkNEUIpLBX9JOUKqtIBvbhvzyLEuWtBIHteseg,265
|
|
57
58
|
langgraph_api/js/tests/graphs/nested.mts,sha256=4G7jSOSaFVQAza-_ARbK-Iai1biLlF2DIPDZXf7PLIY,1245
|
|
58
59
|
langgraph_api/js/tests/graphs/package.json,sha256=SSYv9rN8XLeCKnVctKKwnksvy0RMh-Z9pC0j1lG17PM,174
|
|
59
60
|
langgraph_api/js/tests/graphs/weather.mts,sha256=A7mLK3xW8h5B-ZyJNAyX2M2fJJwzPJzXs4DYesJwreQ,1655
|
|
@@ -70,7 +71,7 @@ langgraph_api/middleware/http_logger.py,sha256=yuFPNFIWwn-4AE1CogBfWlo8KytzywLi_
|
|
|
70
71
|
langgraph_api/middleware/private_network.py,sha256=eYgdyU8AzU2XJu362i1L8aSFoQRiV7_aLBPw7_EgeqI,2111
|
|
71
72
|
langgraph_api/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
72
73
|
langgraph_api/models/run.py,sha256=85-pyyvosFAot8WrWl2QjCje-2c4lhattYvoAEMqztA,11000
|
|
73
|
-
langgraph_api/patch.py,sha256=
|
|
74
|
+
langgraph_api/patch.py,sha256=Dgs0PXHytekX4SUL6KsjjN0hHcOtGLvv1GRGbh6PswU,1408
|
|
74
75
|
langgraph_api/queue_entrypoint.py,sha256=4xICUxXarNV8DhnaqAMhVi3xCmyVKCL3J5NzHxPA9Xc,1835
|
|
75
76
|
langgraph_api/route.py,sha256=fM4qYCGbmH0a3_cV8uKocb1sLklehxO6HhdRXqLK6OM,4421
|
|
76
77
|
langgraph_api/schema.py,sha256=Frh_YOC3S1cDAMPUVanNi78ooSXK2WFpu9YkIVz5h14,5433
|
|
@@ -96,9 +97,9 @@ langgraph_storage/queue.py,sha256=IGjzCzYaGhbR9_Y37p1Hbd5uQkN9_IGzYkT_5lcPU54,75
|
|
|
96
97
|
langgraph_storage/retry.py,sha256=XmldOP4e_H5s264CagJRVnQMDFcEJR_dldVR1Hm5XvM,763
|
|
97
98
|
langgraph_storage/store.py,sha256=JB9jZ87GE19MVN9wgl3-esgR2eIkeipws9q6qsPWkgc,3399
|
|
98
99
|
logging.json,sha256=3RNjSADZmDq38eHePMm1CbP6qZ71AmpBtLwCmKU9Zgo,379
|
|
99
|
-
openapi.json,sha256=
|
|
100
|
-
langgraph_api-0.0.
|
|
101
|
-
langgraph_api-0.0.
|
|
102
|
-
langgraph_api-0.0.
|
|
103
|
-
langgraph_api-0.0.
|
|
104
|
-
langgraph_api-0.0.
|
|
100
|
+
openapi.json,sha256=YW4ND-N3adriEoNwxw7UD9endO2xUZoodCtwVIfa2dU,132261
|
|
101
|
+
langgraph_api-0.0.47.dist-info/LICENSE,sha256=ZPwVR73Biwm3sK6vR54djCrhaRiM4cAD2zvOQZV8Xis,3859
|
|
102
|
+
langgraph_api-0.0.47.dist-info/METADATA,sha256=IO4hwBirD3f4JqdFNa0B6V1TsB-QXhD0SlWYYRbce6Y,4165
|
|
103
|
+
langgraph_api-0.0.47.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
|
|
104
|
+
langgraph_api-0.0.47.dist-info/entry_points.txt,sha256=3EYLgj89DfzqJHHYGxPH4A_fEtClvlRbWRUHaXO7hj4,77
|
|
105
|
+
langgraph_api-0.0.47.dist-info/RECORD,,
|
openapi.json
CHANGED
|
@@ -3355,7 +3355,14 @@
|
|
|
3355
3355
|
"description": "The node to send the message to."
|
|
3356
3356
|
},
|
|
3357
3357
|
"input": {
|
|
3358
|
-
"type":
|
|
3358
|
+
"type": [
|
|
3359
|
+
"object",
|
|
3360
|
+
"array",
|
|
3361
|
+
"number",
|
|
3362
|
+
"string",
|
|
3363
|
+
"boolean",
|
|
3364
|
+
"null"
|
|
3365
|
+
],
|
|
3359
3366
|
"title": "Message",
|
|
3360
3367
|
"description": "The message to send."
|
|
3361
3368
|
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|