langgraph-api 0.0.26__py3-none-any.whl → 0.0.28__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.

Files changed (53) hide show
  1. langgraph_api/api/__init__.py +2 -0
  2. langgraph_api/api/assistants.py +43 -13
  3. langgraph_api/api/meta.py +2 -1
  4. langgraph_api/api/runs.py +14 -1
  5. langgraph_api/api/ui.py +68 -0
  6. langgraph_api/asyncio.py +43 -4
  7. langgraph_api/auth/middleware.py +2 -2
  8. langgraph_api/cli.py +72 -57
  9. langgraph_api/config.py +23 -1
  10. langgraph_api/cron_scheduler.py +1 -1
  11. langgraph_api/graph.py +5 -0
  12. langgraph_api/http.py +24 -7
  13. langgraph_api/js/.gitignore +2 -0
  14. langgraph_api/js/build.mts +49 -3
  15. langgraph_api/js/client.mts +84 -40
  16. langgraph_api/js/global.d.ts +1 -0
  17. langgraph_api/js/package.json +15 -7
  18. langgraph_api/js/remote.py +662 -16
  19. langgraph_api/js/src/graph.mts +5 -4
  20. langgraph_api/js/sse.py +138 -0
  21. langgraph_api/js/tests/api.test.mts +28 -0
  22. langgraph_api/js/tests/compose-postgres.yml +2 -2
  23. langgraph_api/js/tests/graphs/agent.css +1 -0
  24. langgraph_api/js/tests/graphs/agent.ui.tsx +10 -0
  25. langgraph_api/js/tests/graphs/package.json +2 -2
  26. langgraph_api/js/tests/graphs/yarn.lock +13 -13
  27. langgraph_api/js/yarn.lock +710 -1187
  28. langgraph_api/lifespan.py +15 -5
  29. langgraph_api/logging.py +9 -0
  30. langgraph_api/metadata.py +5 -1
  31. langgraph_api/middleware/http_logger.py +1 -1
  32. langgraph_api/patch.py +2 -0
  33. langgraph_api/queue_entrypoint.py +63 -0
  34. langgraph_api/schema.py +2 -0
  35. langgraph_api/stream.py +1 -0
  36. langgraph_api/webhook.py +42 -0
  37. langgraph_api/{queue.py → worker.py} +52 -166
  38. {langgraph_api-0.0.26.dist-info → langgraph_api-0.0.28.dist-info}/METADATA +8 -8
  39. {langgraph_api-0.0.26.dist-info → langgraph_api-0.0.28.dist-info}/RECORD +49 -46
  40. langgraph_storage/database.py +8 -22
  41. langgraph_storage/inmem_stream.py +108 -0
  42. langgraph_storage/ops.py +80 -57
  43. langgraph_storage/queue.py +126 -103
  44. langgraph_storage/retry.py +5 -1
  45. langgraph_storage/store.py +5 -1
  46. openapi.json +3 -3
  47. langgraph_api/js/client.new.mts +0 -861
  48. langgraph_api/js/remote_new.py +0 -694
  49. langgraph_api/js/remote_old.py +0 -667
  50. langgraph_api/js/server_sent_events.py +0 -126
  51. {langgraph_api-0.0.26.dist-info → langgraph_api-0.0.28.dist-info}/LICENSE +0 -0
  52. {langgraph_api-0.0.26.dist-info → langgraph_api-0.0.28.dist-info}/WHEEL +0 -0
  53. {langgraph_api-0.0.26.dist-info → langgraph_api-0.0.28.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,138 @@
1
+ """Adapted from httpx_sse to split lines on \n, \r, \r\n per the SSE spec."""
2
+
3
+ from collections.abc import AsyncIterator
4
+
5
+ import httpx
6
+ import orjson
7
+ from langgraph_sdk.schema import StreamPart
8
+
9
+ BytesLike = bytes | bytearray | memoryview
10
+
11
+
12
+ class BytesLineDecoder:
13
+ """
14
+ Handles incrementally reading lines from text.
15
+
16
+ Has the same behaviour as the stdllib bytes splitlines,
17
+ but handling the input iteratively.
18
+ """
19
+
20
+ def __init__(self) -> None:
21
+ self.buffer = bytearray()
22
+ self.trailing_cr: bool = False
23
+
24
+ def decode(self, text: bytes) -> list[BytesLike]:
25
+ # See https://docs.python.org/3/glossary.html#term-universal-newlines
26
+ NEWLINE_CHARS = b"\n\r"
27
+
28
+ # We always push a trailing `\r` into the next decode iteration.
29
+ if self.trailing_cr:
30
+ text = b"\r" + text
31
+ self.trailing_cr = False
32
+ if text.endswith(b"\r"):
33
+ self.trailing_cr = True
34
+ text = text[:-1]
35
+
36
+ if not text:
37
+ # NOTE: the edge case input of empty text doesn't occur in practice,
38
+ # because other httpx internals filter out this value
39
+ return [] # pragma: no cover
40
+
41
+ trailing_newline = text[-1] in NEWLINE_CHARS
42
+ lines = text.splitlines()
43
+
44
+ if len(lines) == 1 and not trailing_newline:
45
+ # No new lines, buffer the input and continue.
46
+ self.buffer.extend(lines[0])
47
+ return []
48
+
49
+ if self.buffer:
50
+ # Include any existing buffer in the first portion of the
51
+ # splitlines result.
52
+ self.buffer.extend(lines[0])
53
+ lines = [self.buffer] + lines[1:]
54
+ self.buffer = bytearray()
55
+
56
+ if not trailing_newline:
57
+ # If the last segment of splitlines is not newline terminated,
58
+ # then drop it from our output and start a new buffer.
59
+ self.buffer.extend(lines.pop())
60
+
61
+ return lines
62
+
63
+ def flush(self) -> list[BytesLike]:
64
+ if not self.buffer and not self.trailing_cr:
65
+ return []
66
+
67
+ lines = [self.buffer]
68
+ self.buffer = bytearray()
69
+ self.trailing_cr = False
70
+ return lines
71
+
72
+
73
+ class SSEDecoder:
74
+ def __init__(self) -> None:
75
+ self._event = ""
76
+ self._data = bytearray()
77
+ self._last_event_id = ""
78
+ self._retry: int | None = None
79
+
80
+ def decode(self, line: bytes) -> StreamPart | None:
81
+ # See: https://html.spec.whatwg.org/multipage/server-sent-events.html#event-stream-interpretation # noqa: E501
82
+
83
+ if not line:
84
+ if (
85
+ not self._event
86
+ and not self._data
87
+ and not self._last_event_id
88
+ and self._retry is None
89
+ ):
90
+ return None
91
+
92
+ sse = StreamPart(
93
+ event=self._event,
94
+ data=orjson.loads(self._data) if self._data else None,
95
+ )
96
+
97
+ # NOTE: as per the SSE spec, do not reset last_event_id.
98
+ self._event = ""
99
+ self._data = bytearray()
100
+ self._retry = None
101
+
102
+ return sse
103
+
104
+ if line.startswith(b":"):
105
+ return None
106
+
107
+ fieldname, _, value = line.partition(b":")
108
+
109
+ if value.startswith(b" "):
110
+ value = value[1:]
111
+
112
+ if fieldname == b"event":
113
+ self._event = value.decode()
114
+ elif fieldname == b"data":
115
+ self._data.extend(value)
116
+ elif fieldname == b"id":
117
+ if b"\0" in value:
118
+ pass
119
+ else:
120
+ self._last_event_id = value.decode()
121
+ elif fieldname == b"retry":
122
+ try:
123
+ self._retry = int(value)
124
+ except (TypeError, ValueError):
125
+ pass
126
+ else:
127
+ pass # Field is ignored.
128
+
129
+ return None
130
+
131
+
132
+ async def aiter_lines_raw(response: httpx.Response) -> AsyncIterator[BytesLike]:
133
+ decoder = BytesLineDecoder()
134
+ async for chunk in response.aiter_bytes():
135
+ for line in decoder.decode(chunk):
136
+ yield line
137
+ for line in decoder.flush():
138
+ yield line
@@ -461,6 +461,7 @@ describe("runs", () => {
461
461
  {
462
462
  input: { messages: [{ type: "human", content: "bar" }] },
463
463
  config: globalConfig,
464
+ afterSeconds: 10,
464
465
  }
465
466
  );
466
467
 
@@ -1740,6 +1741,33 @@ describe("long running tasks", () => {
1740
1741
  );
1741
1742
  });
1742
1743
 
1744
+ it("unusual newline termination characters", async () => {
1745
+ const thread = await client.threads.create({
1746
+ metadata: { graph_id: "agent" },
1747
+ });
1748
+
1749
+ await client.threads.updateState(thread.thread_id, {
1750
+ values: {
1751
+ messages: [
1752
+ {
1753
+ type: "human",
1754
+ content:
1755
+ "Page break characters: \n\r\x0b\x0c\x1c\x1d\x1e\x85\u2028\u2029",
1756
+ },
1757
+ ],
1758
+ },
1759
+ });
1760
+
1761
+ const history = await client.threads.getHistory<{
1762
+ messages: { type: string; content: string }[];
1763
+ }>(thread.thread_id);
1764
+ expect(history.length).toBe(1);
1765
+ expect(history[0].values.messages.length).toBe(1);
1766
+ expect(history[0].values.messages[0].content).toBe(
1767
+ "Page break characters: \n\r\x0b\x0c\x1c\x1d\x1e\x85\u2028\u2029"
1768
+ );
1769
+ });
1770
+
1743
1771
  // Not implemented in JS yet
1744
1772
  describe.skip("command update state", () => {
1745
1773
  it("updates state via commands", async () => {
@@ -30,7 +30,7 @@ services:
30
30
  build:
31
31
  context: graphs
32
32
  dockerfile_inline: |
33
- FROM langchain/langgraphjs-api:20
33
+ FROM langchain/langgraphjs-api:${NODE_VERSION:-20}
34
34
  ADD . /deps/graphs
35
35
  WORKDIR /deps/graphs
36
36
  RUN yarn install --frozen-lockfile
@@ -52,6 +52,6 @@ services:
52
52
  environment:
53
53
  REDIS_URI: redis://langgraph-redis:6379
54
54
  DATABASE_URI: postgres://postgres:postgres@langgraph-postgres:5432/postgres?sslmode=disable
55
- N_JOBS_PER_WORKER: "2"
55
+ N_JOBS_PER_WORKER: "5"
56
56
  LANGGRAPH_CLOUD_LICENSE_KEY: ${LANGGRAPH_CLOUD_LICENSE_KEY}
57
57
  FF_JS_ZEROMQ_ENABLED: ${FF_JS_ZEROMQ_ENABLED}
@@ -0,0 +1 @@
1
+ @import "tailwindcss";
@@ -0,0 +1,10 @@
1
+ import "./agent.css";
2
+ import React from "react";
3
+
4
+ export function StockPrice() {
5
+ return <div>Stock Price</div>;
6
+ }
7
+
8
+ export default {
9
+ "stock-price": StockPrice,
10
+ } as const;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "private": true,
3
3
  "dependencies": {
4
- "@langchain/core": "^0.3.37",
5
- "@langchain/langgraph": "^0.2.43"
4
+ "@langchain/core": "^0.3.40",
5
+ "@langchain/langgraph": "^0.2.49"
6
6
  }
7
7
  }
@@ -7,10 +7,10 @@
7
7
  resolved "https://registry.yarnpkg.com/@cfworker/json-schema/-/json-schema-4.1.0.tgz#cc114da98c23b12f4cd4673ce8a076be24e0233c"
8
8
  integrity sha512-/vYKi/qMxwNsuIJ9WGWwM2rflY40ZenK3Kh4uR5vB9/Nz12Y7IUN/Xf4wDA7vzPfw0VNh3b/jz4+MjcVgARKJg==
9
9
 
10
- "@langchain/core@^0.3.37":
11
- version "0.3.37"
12
- resolved "https://registry.yarnpkg.com/@langchain/core/-/core-0.3.37.tgz#9ea7985c6cdaa075b02241ba3e6ba76c47454daf"
13
- integrity sha512-LFk9GqHxcyCFx0oXvCBP7vDZIOUHYzzNU7JR+2ofIMnfkBLzcCKzBLySQDfPtd13PrpGHkaeOeLq8H1Tqi9lSw==
10
+ "@langchain/core@^0.3.40":
11
+ version "0.3.42"
12
+ resolved "https://registry.yarnpkg.com/@langchain/core/-/core-0.3.42.tgz#f1fa38425626d8efe9fe2ee51d36c91506632363"
13
+ integrity sha512-pT/jC5lqWK3YGDq8dQwgKoa6anqAhMtG1x5JbnrOj9NdaLeBbCKBDQ+/Ykzk3nZ8o+0UMsaXNZo7IVL83VVjHg==
14
14
  dependencies:
15
15
  "@cfworker/json-schema" "^4.0.2"
16
16
  ansi-styles "^5.0.0"
@@ -25,10 +25,10 @@
25
25
  zod "^3.22.4"
26
26
  zod-to-json-schema "^3.22.3"
27
27
 
28
- "@langchain/langgraph-checkpoint@~0.0.14":
29
- version "0.0.14"
30
- resolved "https://registry.yarnpkg.com/@langchain/langgraph-checkpoint/-/langgraph-checkpoint-0.0.14.tgz#7f66454438a906283d7f4e6179784f312a5a9cbe"
31
- integrity sha512-UoJc1IZqtnn+AiRhygo1yjNZiXTwdOY6NSb7yrXrN96CAW3LPu8cFe7VihKg5OBv9qaGz1GCvnrNdNAB48HuKQ==
28
+ "@langchain/langgraph-checkpoint@~0.0.16":
29
+ version "0.0.16"
30
+ resolved "https://registry.yarnpkg.com/@langchain/langgraph-checkpoint/-/langgraph-checkpoint-0.0.16.tgz#e996f31d5da8ce67b2a9bf3dc64c4c0e05f01d72"
31
+ integrity sha512-B50l7w9o9353drHsdsD01vhQrCJw0eqvYeXid7oKeoj1Yye+qY90r97xuhiflaYCZHM5VEo2oaizs8oknerZsQ==
32
32
  dependencies:
33
33
  uuid "^10.0.0"
34
34
 
@@ -42,12 +42,12 @@
42
42
  p-retry "4"
43
43
  uuid "^9.0.0"
44
44
 
45
- "@langchain/langgraph@^0.2.43":
46
- version "0.2.43"
47
- resolved "https://registry.yarnpkg.com/@langchain/langgraph/-/langgraph-0.2.43.tgz#bf24f8b26bc606cd3be7230364314a227382eb1e"
48
- integrity sha512-uhdbzm3psUIEqxQUQPXeafLC5dxTzALrVGRnnGZi9gt0qlDueRfopZoh7uWJy+Zol+yN/E2mM3M6ZztSsfUEuQ==
45
+ "@langchain/langgraph@^0.2.49":
46
+ version "0.2.54"
47
+ resolved "https://registry.yarnpkg.com/@langchain/langgraph/-/langgraph-0.2.54.tgz#f57a9b471808c122ee5ae4506ed05cc75f1578bd"
48
+ integrity sha512-+P2rU0Qz6bBCNPXOSV8WeUpLRTvhu8fQuzMYR2MqWsbbfmZrfmLxqtVWPHkmr5khx/txxFy1vOBAy+KwZ94mrg==
49
49
  dependencies:
50
- "@langchain/langgraph-checkpoint" "~0.0.14"
50
+ "@langchain/langgraph-checkpoint" "~0.0.16"
51
51
  "@langchain/langgraph-sdk" "~0.0.32"
52
52
  uuid "^10.0.0"
53
53
  zod "^3.23.8"