langgraph-api 0.0.46__py3-none-any.whl → 0.0.48__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.
langgraph_api/__init__.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.0.45"
1
+ __version__ = "0.0.46"
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(graph_id: str, graph: GraphValue, config: dict | None) -> None:
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
- for key, value in json.loads(paths_str).items():
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 = value.rsplit(":", maxsplit=1)
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=path_or_module,
210
- variable=variable,
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=config_per_graph.get(key),
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(spec.id, graph, spec.config)
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(spec.id, graph, spec.config)
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 is not None
379
- has_store = graph.store is not None
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("checkpointer")
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 `POSTGRES_URI_CUSTOM` environment variable."
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
 
@@ -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.object({ resume: z.unknown() }).nullish(),
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
@@ -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
- // Not implemented in JS yet
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();
@@ -5,6 +5,7 @@
5
5
  "subgraph": "./subgraph.mts:graph",
6
6
  "nested": "./nested.mts:graph",
7
7
  "dynamic": "./dynamic.mts:graph",
8
- "delay": "./delay.mts:graph"
8
+ "delay": "./delay.mts:graph",
9
+ "command": "./command.mts:graph"
9
10
  }
10
11
  }
@@ -5,7 +5,6 @@ import structlog
5
5
  from starlette.requests import ClientDisconnect
6
6
  from starlette.types import Message, Receive, Scope, Send
7
7
 
8
- from langgraph_api.asyncio import create_task
9
8
  from langgraph_api.logging import LOG_JSON
10
9
 
11
10
  asgi = structlog.stdlib.get_logger("asgi")
@@ -21,6 +20,12 @@ class AccessLoggerMiddleware:
21
20
  ) -> None:
22
21
  self.app = app
23
22
  self.logger = logger
23
+ if hasattr(logger, "isEnabledFor"):
24
+ self.debug_enabled = self.logger.isEnabledFor(logging.DEBUG)
25
+ elif hasattr(logger, "is_enabled_for"):
26
+ self.debug_enabled = self.logger.is_enabled_for(logging.DEBUG)
27
+ else:
28
+ self.debug_enabled = False
24
29
 
25
30
  async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
26
31
  if scope["type"] != "http" or (LOG_JSON and scope.get("path") in PATHS_IGNORE):
@@ -29,7 +34,7 @@ class AccessLoggerMiddleware:
29
34
  loop = asyncio.get_event_loop()
30
35
  info = {"response": {}}
31
36
 
32
- if self.logger.isEnabledFor(logging.DEBUG):
37
+ if self.debug_enabled:
33
38
 
34
39
  async def inner_receive() -> Message:
35
40
  message = await receive()
@@ -61,20 +66,18 @@ class AccessLoggerMiddleware:
61
66
  finally:
62
67
  info["end_time"] = loop.time()
63
68
  latency = int((info["end_time"] - info["start_time"]) * 1_000)
64
- create_task(
65
- self.logger.ainfo(
66
- f"{scope.get('method')} {scope.get('path')} {info['response'].get('status')} {latency}ms",
67
- method=scope.get("method"),
68
- path=scope.get("path"),
69
- status=info["response"].get("status"),
70
- latency_ms=latency,
71
- route=scope.get("route"),
72
- path_params=scope.get("path_params"),
73
- query_string=scope.get("query_string").decode(),
74
- proto=scope.get("http_version"),
75
- req_header=_headers_to_dict(scope.get("headers")),
76
- res_header=_headers_to_dict(info["response"].get("headers")),
77
- )
69
+ self.logger.info(
70
+ f"{scope.get('method')} {scope.get('path')} {info['response'].get('status')} {latency}ms",
71
+ method=scope.get("method"),
72
+ path=scope.get("path"),
73
+ status=info["response"].get("status"),
74
+ latency_ms=latency,
75
+ route=scope.get("route"),
76
+ path_params=scope.get("path_params"),
77
+ query_string=scope.get("query_string").decode(),
78
+ proto=scope.get("http_version"),
79
+ req_header=_headers_to_dict(scope.get("headers")),
80
+ res_header=_headers_to_dict(info["response"].get("headers")),
78
81
  )
79
82
 
80
83
 
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,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: langgraph-api
3
- Version: 0.0.46
3
+ Version: 0.0.48
4
4
  Summary:
5
5
  License: Elastic-2.0
6
6
  Author: Nuno Campos
@@ -1,5 +1,5 @@
1
1
  LICENSE,sha256=ZPwVR73Biwm3sK6vR54djCrhaRiM4cAD2zvOQZV8Xis,3859
2
- langgraph_api/__init__.py,sha256=TcBob6tFZGDIyRe6fMRATilhAILshMCOgcFispx6nM0,23
2
+ langgraph_api/__init__.py,sha256=WvtDrm5o4jAYjOF-kumI_Il7bjdj2Nr-QuEWD_sgdXQ,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=GnvG44jEGKpRMXZLp9QC__B_PGJteM7K3aJP-q22EvE,18827
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=n6ecWwJBP2wp1jCaam55GxffH-YesVrV-7ZTml-k4Oc,26137
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=zvjMINAYOFpniQ_cZ3MxIOjwKI83xsQzU7pPiKsoCmQ,24623
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=52DcdgJ7b_I53Zl5XyYnMbyA0iofgVoxcVcvQSN4BGo,60752
48
- langgraph_api/js/tests/compose-postgres.yml,sha256=uMjmqdZ-rOImuLrfNaE9gsX-F-xlU35OVx7nWknpbdM,1827
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=iZL7XpAy3-QnCUHCRSj__Fxp3A-JPuYBJ_XQIxeyQfU,227
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
@@ -66,11 +67,11 @@ langgraph_api/lifespan.py,sha256=SfVZj0SQdsIfYwvN5s4dfxGMb7MMlGe40DghCGDFaTQ,291
66
67
  langgraph_api/logging.py,sha256=JJIzbNIgLCN6ClQ3tA-Mm5ffuBGvpRDSZsEvnIlsuu4,3693
67
68
  langgraph_api/metadata.py,sha256=bAeN3NwibBuXUVPjOEbEUJMnhUXe_VdTGw508VNeav4,3655
68
69
  langgraph_api/middleware/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
69
- langgraph_api/middleware/http_logger.py,sha256=yuFPNFIWwn-4AE1CogBfWlo8KytzywLi_Bd4ccsyVQE,3150
70
+ langgraph_api/middleware/http_logger.py,sha256=aj4mdisRobFePkD3Iy6-w_Mujwx4TQRaEhPvSd6HgLk,3284
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=82xjuFqY7tgrUm-k1XWHI6k8S6QovSD0zhe--8_xW4o,1296
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
@@ -91,14 +92,14 @@ langgraph_storage/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU
91
92
  langgraph_storage/checkpoint.py,sha256=Qq0y6vdh27qdF3h5nOLT5CcX9Rj2bcFqkVOMeCaGoK4,4036
92
93
  langgraph_storage/database.py,sha256=sZjZvMcvbr_6dX0d1YrYEccVuQozIfkiWt8bdlXGVYU,5849
93
94
  langgraph_storage/inmem_stream.py,sha256=LjJSAxsh_E0ywqEMzdWJk8Hy_Jn9oQByzycss-fANng,3264
94
- langgraph_storage/ops.py,sha256=S2qXgfyuCr5qkKLqIV-kZNRK924bBSLZ2qdLtExfi-M,75939
95
+ langgraph_storage/ops.py,sha256=ksJPrjdFRXRGYffrdLvk06Ec-OT1E_Q6qB1_rbdF0w8,75800
95
96
  langgraph_storage/queue.py,sha256=IGjzCzYaGhbR9_Y37p1Hbd5uQkN9_IGzYkT_5lcPU54,7595
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=P4Gy9hD4vXpGEjaMT1zLpw4ISNJ08RYB5BsVya1Wp_8,132113
100
- langgraph_api-0.0.46.dist-info/LICENSE,sha256=ZPwVR73Biwm3sK6vR54djCrhaRiM4cAD2zvOQZV8Xis,3859
101
- langgraph_api-0.0.46.dist-info/METADATA,sha256=YyzsDHSIz-8lj9Np7lEguVJ-Poz_eBpgtQXm4MpvhDM,4165
102
- langgraph_api-0.0.46.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
103
- langgraph_api-0.0.46.dist-info/entry_points.txt,sha256=3EYLgj89DfzqJHHYGxPH4A_fEtClvlRbWRUHaXO7hj4,77
104
- langgraph_api-0.0.46.dist-info/RECORD,,
100
+ openapi.json,sha256=YW4ND-N3adriEoNwxw7UD9endO2xUZoodCtwVIfa2dU,132261
101
+ langgraph_api-0.0.48.dist-info/LICENSE,sha256=ZPwVR73Biwm3sK6vR54djCrhaRiM4cAD2zvOQZV8Xis,3859
102
+ langgraph_api-0.0.48.dist-info/METADATA,sha256=0Bi2FktyQZbXA3aE3Q1YDCmaGh0Vwiswtk_x1xSUtz0,4165
103
+ langgraph_api-0.0.48.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
104
+ langgraph_api-0.0.48.dist-info/entry_points.txt,sha256=3EYLgj89DfzqJHHYGxPH4A_fEtClvlRbWRUHaXO7hj4,77
105
+ langgraph_api-0.0.48.dist-info/RECORD,,
langgraph_storage/ops.py CHANGED
@@ -4,7 +4,6 @@ import asyncio
4
4
  import base64
5
5
  import copy
6
6
  import json
7
- import logging
8
7
  import uuid
9
8
  from collections import defaultdict
10
9
  from collections.abc import AsyncIterator, Sequence
@@ -1915,7 +1914,6 @@ class Runs(Authenticated):
1915
1914
  ctx: Auth.types.BaseAuthContext | None = None,
1916
1915
  ) -> AsyncIterator[tuple[bytes, bytes]]:
1917
1916
  """Stream the run output."""
1918
- log = logger.isEnabledFor(logging.DEBUG)
1919
1917
  queue = (
1920
1918
  stream_mode
1921
1919
  if isinstance(stream_mode, asyncio.Queue)
@@ -1954,13 +1952,12 @@ class Runs(Authenticated):
1954
1952
  else:
1955
1953
  # Extract mode from topic
1956
1954
  yield topic[len_prefix:], data
1957
- if log:
1958
- await logger.adebug(
1959
- "Streamed run event",
1960
- run_id=str(run_id),
1961
- stream_mode=topic[len_prefix:],
1962
- data=data,
1963
- )
1955
+ logger.debug(
1956
+ "Streamed run event",
1957
+ run_id=str(run_id),
1958
+ stream_mode=topic[len_prefix:],
1959
+ data=data,
1960
+ )
1964
1961
  except TimeoutError:
1965
1962
  # Check if the run is still pending
1966
1963
  run_iter = await Runs.get(
openapi.json CHANGED
@@ -3355,7 +3355,14 @@
3355
3355
  "description": "The node to send the message to."
3356
3356
  },
3357
3357
  "input": {
3358
- "type": "object",
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
  }