langgraph-api 0.2.7__py3-none-any.whl → 0.2.9__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 CHANGED
@@ -1 +1 @@
1
- __version__ = "0.2.7"
1
+ __version__ = "0.2.9"
@@ -138,14 +138,19 @@ async def search_assistants(
138
138
  """List assistants."""
139
139
  payload = await request.json(AssistantSearchRequest)
140
140
  async with connect() as conn:
141
- assistants_iter = await Assistants.search(
141
+ assistants_iter, total = await Assistants.search(
142
142
  conn,
143
143
  graph_id=payload.get("graph_id"),
144
144
  metadata=payload.get("metadata"),
145
145
  limit=int(payload.get("limit") or 10),
146
146
  offset=int(payload.get("offset") or 0),
147
+ sort_by=payload.get("sort_by"),
148
+ sort_order=payload.get("sort_order"),
147
149
  )
148
- return ApiResponse([assistant async for assistant in assistants_iter])
150
+ return ApiResponse(
151
+ [assistant async for assistant in assistants_iter],
152
+ headers={"X-Pagination-Total": str(total)},
153
+ )
149
154
 
150
155
 
151
156
  @retry_db
@@ -33,7 +33,7 @@
33
33
  },
34
34
  "devDependencies": {
35
35
  "jose": "^6.0.10",
36
- "@langchain/langgraph-sdk": "^0.0.70",
36
+ "@langchain/langgraph-sdk": "^0.0.73",
37
37
  "@types/react": "^19.0.8",
38
38
  "@types/react-dom": "^19.0.3",
39
39
  "@types/node": "^22.2.0",
@@ -37,6 +37,8 @@ interface AgentState {
37
37
  sharedStateValue?: string | null;
38
38
  }
39
39
 
40
+ // the way this test is set up, it instantiates the client
41
+ // with 6 graphs.
40
42
  beforeAll(async () => {
41
43
  await sql`DELETE FROM thread`;
42
44
  await sql`DELETE FROM store`;
@@ -44,12 +46,24 @@ beforeAll(async () => {
44
46
  });
45
47
 
46
48
  describe("assistants", () => {
49
+ beforeEach(async () => {
50
+ await sql`DELETE FROM assistant WHERE metadata->>'created_by' is null OR metadata->>'created_by' != 'system'`;
51
+ });
52
+
47
53
  it("create read update delete", async () => {
48
54
  const graphId = "agent";
49
55
  const config = { configurable: { model_name: "gpt" } };
50
56
 
51
- let res = await client.assistants.create({ graphId, config });
52
- expect(res).toMatchObject({ graph_id: graphId, config });
57
+ let res = await client.assistants.create({
58
+ graphId,
59
+ config,
60
+ name: "assistant1",
61
+ });
62
+ expect(res).toMatchObject({
63
+ graph_id: graphId,
64
+ config,
65
+ name: "assistant1",
66
+ });
53
67
 
54
68
  const metadata = { name: "woof" };
55
69
  await client.assistants.update(res.assistant_id, { graphId, metadata });
@@ -57,6 +71,36 @@ describe("assistants", () => {
57
71
  res = await client.assistants.get(res.assistant_id);
58
72
  expect(res).toMatchObject({ graph_id: graphId, config, metadata });
59
73
 
74
+ const secondAssistant = await client.assistants.create({
75
+ graphId,
76
+ config,
77
+ name: "assistant2",
78
+ });
79
+ expect(secondAssistant).toMatchObject({
80
+ graph_id: graphId,
81
+ config,
82
+ name: "assistant2",
83
+ });
84
+
85
+ const search = await client.assistants.search();
86
+ const customAssistants = search.filter(
87
+ (a) => a.metadata?.created_by !== "system",
88
+ );
89
+ expect(customAssistants.length).toBe(2);
90
+ expect(customAssistants[0].name).toBe("assistant2");
91
+ expect(customAssistants[1].name).toBe("assistant1");
92
+
93
+ const search2 = await client.assistants.search({
94
+ sortBy: "name",
95
+ sortOrder: "desc",
96
+ });
97
+ const customAssistants2 = search2.filter(
98
+ (a) => a.metadata?.created_by !== "system",
99
+ );
100
+ expect(customAssistants2.length).toBe(2);
101
+ expect(customAssistants2[0].name).toBe("assistant2");
102
+ expect(customAssistants2[1].name).toBe("assistant1");
103
+
60
104
  await client.assistants.delete(res.assistant_id);
61
105
  await expect(() => client.assistants.get(res.assistant_id)).rejects.toThrow(
62
106
  "HTTP 404: Not Found",
@@ -134,23 +178,27 @@ describe("assistants", () => {
134
178
 
135
179
  it("list assistants", async () => {
136
180
  let search = await client.assistants.search();
137
- // Greater than or equal because the vitest retries can cause multiple assistants to be created
138
- expect(search.length).toBeGreaterThanOrEqual(1);
181
+ const numAssistants = search.length;
182
+
183
+ const customAssistants = search.filter(
184
+ (a) => a.metadata?.created_by !== "system",
185
+ );
186
+ expect(customAssistants.length).toBe(0);
139
187
 
140
188
  const graphid = "agent";
141
189
  const create = await client.assistants.create({ graphId: "agent" });
142
190
 
143
191
  search = await client.assistants.search();
144
- expect(search.length).toBeGreaterThanOrEqual(2);
192
+ expect(search.length).toBe(numAssistants + 1);
145
193
 
146
194
  search = await client.assistants.search({ graphId: graphid });
147
- expect(search.length).toBeGreaterThanOrEqual(2);
195
+ expect(search.length).toBe(2);
148
196
  expect(search.every((i) => i.graph_id === graphid)).toBe(true);
149
197
 
150
198
  search = await client.assistants.search({
151
199
  metadata: { created_by: "system" },
152
200
  });
153
- expect(search.length).toBeGreaterThanOrEqual(1);
201
+ expect(search.length).toBe(numAssistants);
154
202
  expect(search.every((i) => i.assistant_id !== create.assistant_id)).toBe(
155
203
  true,
156
204
  );
@@ -255,7 +303,7 @@ describe("threads crud", () => {
255
303
  });
256
304
 
257
305
  describe("threads copy", () => {
258
- it.concurrent("copy", async () => {
306
+ it.concurrent("copy", { retry: 3 }, async () => {
259
307
  const assistantId = "agent";
260
308
  const thread = await client.threads.create();
261
309
  const input = { messages: [{ type: "human", content: "foo" }] };
@@ -329,7 +377,7 @@ describe("threads copy", () => {
329
377
  }
330
378
  });
331
379
 
332
- it.concurrent("copy runs", async () => {
380
+ it.concurrent("copy runs", { retry: 3 }, async () => {
333
381
  const assistantId = "agent";
334
382
  const thread = await client.threads.create();
335
383
 
@@ -375,7 +423,7 @@ describe("threads copy", () => {
375
423
  expect(currentOriginalThreadState).toEqual(originalThreadState);
376
424
  });
377
425
 
378
- it.concurrent("get thread history", async () => {
426
+ it.concurrent("get thread history", { retry: 3 }, async () => {
379
427
  const assistant = await client.assistants.create({ graphId: "agent" });
380
428
  const thread = await client.threads.create();
381
429
  const input = { messages: [{ type: "human", content: "foo" }] };
@@ -419,7 +467,7 @@ describe("threads copy", () => {
419
467
  expect(filteredHistory.at(-1)?.values.messages.length).toBe(4);
420
468
  });
421
469
 
422
- it.concurrent("copy update", async () => {
470
+ it.concurrent("copy update", { retry: 3 }, async () => {
423
471
  const assistantId = "agent";
424
472
  const thread = await client.threads.create();
425
473
  const input = {
@@ -236,10 +236,10 @@
236
236
  dependencies:
237
237
  uuid "^10.0.0"
238
238
 
239
- "@langchain/langgraph-sdk@^0.0.70":
240
- version "0.0.70"
241
- resolved "https://registry.yarnpkg.com/@langchain/langgraph-sdk/-/langgraph-sdk-0.0.70.tgz#9589f984b47de5e4a669b6008cbf01427a3d41d0"
242
- integrity sha512-O8I12bfeMVz5fOrXnIcK4IdRf50IqyJTO458V56wAIHLNoi4H8/JHM+2M+Y4H2PtslXIGnvomWqlBd0eY5z/Og==
239
+ "@langchain/langgraph-sdk@^0.0.73":
240
+ version "0.0.73"
241
+ resolved "https://registry.yarnpkg.com/@langchain/langgraph-sdk/-/langgraph-sdk-0.0.73.tgz#6f5cb44fb306461182df55be4cf1d263c5ce3e16"
242
+ integrity sha512-V3E7Bd1KNcTpnEglZqKpnQtth62WHf+Dxq5V+0CqMxbzPHnW9I4eynCO4c1/HSY/rHgEtvgj7fMvCAb6I5R+lQ==
243
243
  dependencies:
244
244
  "@types/json-schema" "^7.0.15"
245
245
  p-queue "^6.6.2"
langgraph_api/worker.py CHANGED
@@ -275,6 +275,27 @@ async def worker(
275
275
  # let it bubble up and rollback db transaction, thus marking the run
276
276
  # as available to be picked up by another worker
277
277
 
278
+ # If a stateful run succeeded but no checkoint was returned, it's likely because
279
+ # there was a retriable exception that resumed right at the end
280
+ if checkpoint is None and (not temporary) and webhook and status == "success":
281
+ await logger.ainfo(
282
+ "Fetching missing checkpoint for webhook",
283
+ run_id=str(run_id),
284
+ run_attempt=attempt,
285
+ )
286
+ try:
287
+ state_snapshot = await Threads.State.get(
288
+ conn, run["kwargs"]["config"], subgraphs=True
289
+ )
290
+ checkpoint = {"values": state_snapshot.values}
291
+ except Exception:
292
+ await logger.aerror(
293
+ "Failed to fetch missing checkpoint for webhook. Continuing...",
294
+ exc_info=True,
295
+ run_id=str(run_id),
296
+ run_attempt=attempt,
297
+ )
298
+
278
299
  return WorkerResult(
279
300
  checkpoint=checkpoint,
280
301
  status=status,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: langgraph-api
3
- Version: 0.2.7
3
+ Version: 0.2.9
4
4
  Summary:
5
5
  License: Elastic-2.0
6
6
  Author: Nuno Campos
@@ -18,7 +18,7 @@ Requires-Dist: jsonschema-rs (>=0.20.0,<0.30)
18
18
  Requires-Dist: langchain-core (>=0.2.38) ; python_version < "4.0"
19
19
  Requires-Dist: langgraph (>=0.3.27) ; python_version < "4.0"
20
20
  Requires-Dist: langgraph-checkpoint (>=2.0.23) ; python_version < "4.0"
21
- Requires-Dist: langgraph-runtime-inmem (>=0.0.7)
21
+ Requires-Dist: langgraph-runtime-inmem (>=0.0.9,<0.1)
22
22
  Requires-Dist: langgraph-sdk (>=0.1.66,<0.2.0) ; python_version < "4.0"
23
23
  Requires-Dist: langsmith (>=0.1.63)
24
24
  Requires-Dist: orjson (>=3.9.7)
@@ -1,7 +1,7 @@
1
1
  LICENSE,sha256=ZPwVR73Biwm3sK6vR54djCrhaRiM4cAD2zvOQZV8Xis,3859
2
- langgraph_api/__init__.py,sha256=XHypfHSPdgXFKmOdoewn7czU670gt8InhHhzlP5j_aA,22
2
+ langgraph_api/__init__.py,sha256=F8OVhAhMXSkvvXYgZtbPn2SG1AQC3joK4yu-FrHt81Y,22
3
3
  langgraph_api/api/__init__.py,sha256=YVzpbn5IQotvuuLG9fhS9QMrxXfP4s4EpEMG0n4q3Nw,5625
4
- langgraph_api/api/assistants.py,sha256=6oYFRKlvqheJQGbWjFhQOUnnSbvsbrdMYLRJP7WtSRo,14481
4
+ langgraph_api/api/assistants.py,sha256=mcKaVeNG8hQAV4IQDhaukS7FgVqTIVQNTyti3GfK2KI,14649
5
5
  langgraph_api/api/mcp.py,sha256=RvRYgANqRzNQzSmgjNkq4RlKTtoEJYil04ot9lsmEtE,14352
6
6
  langgraph_api/api/meta.py,sha256=sTgkhE-DaFWpERG6F7KelZfDsmJAiVc4j5dg50tDkSo,2950
7
7
  langgraph_api/api/openapi.py,sha256=WYpkjwsVV056qBkBVbGjhh-TfU9-IgZMY2au1s1PhZ4,15953
@@ -34,7 +34,7 @@ langgraph_api/js/client.http.mts,sha256=AGA-p8J85IcNh2oXZjDxHQ4PnQdJmt-LPcpZp6j0
34
34
  langgraph_api/js/client.mts,sha256=RZgkJk43CMOwnHXMXBbWIEhHh0W8WBwQ8Rm8GkJwvhM,30341
35
35
  langgraph_api/js/errors.py,sha256=Cm1TKWlUCwZReDC5AQ6SgNIVGD27Qov2xcgHyf8-GXo,361
36
36
  langgraph_api/js/global.d.ts,sha256=j4GhgtQSZ5_cHzjSPcHgMJ8tfBThxrH-pUOrrJGteOU,196
37
- langgraph_api/js/package.json,sha256=5gtQpj0mXh069X5fWJFWp-jt7Sb3RQUPVpe4m8Q0KHE,1289
37
+ langgraph_api/js/package.json,sha256=J2sWtYb_gFMCb_g8661iGUR1C23lZQna3wmgl5da0Xc,1289
38
38
  langgraph_api/js/remote.py,sha256=ipAITSDyh-kNau37nfRg-PSB-6Lbtax3UJap8-lLZdw,35729
39
39
  langgraph_api/js/schema.py,sha256=7idnv7URlYUdSNMBXQcw7E4SxaPxCq_Oxwnlml8q5ik,408
40
40
  langgraph_api/js/src/graph.mts,sha256=_xKhdO2WAwsieoezFqTCcx1IDeHeN6GSMv0lyBsakKI,3485
@@ -49,7 +49,7 @@ langgraph_api/js/src/utils/importMap.mts,sha256=pX4TGOyUpuuWF82kXcxcv3-8mgusRezO
49
49
  langgraph_api/js/src/utils/pythonSchemas.mts,sha256=98IW7Z_VP7L_CHNRMb3_MsiV3BgLE2JsWQY_PQcRR3o,685
50
50
  langgraph_api/js/src/utils/serde.mts,sha256=D9o6MwTgwPezC_DEmsWS5NnLPnjPMVWIb1I1D4QPEPo,743
51
51
  langgraph_api/js/sse.py,sha256=lsfp4nyJyA1COmlKG9e2gJnTttf_HGCB5wyH8OZBER8,4105
52
- langgraph_api/js/tests/api.test.mts,sha256=qjHkSdgTDiBauhxRD4yNMufD5xfZh9BNZ7nD7hTgyzo,68125
52
+ langgraph_api/js/tests/api.test.mts,sha256=LSWDHTar-AtyazqR8EbnS17MrMr3IvKWysGDZ13p-GM,69432
53
53
  langgraph_api/js/tests/auth.test.mts,sha256=mMhKe9ggJw4BgUqzSVwqYY3HLMXXEBZ23iiKK8Yq1mM,21678
54
54
  langgraph_api/js/tests/compose-postgres.auth.yml,sha256=iPfJbCeYZdV6GiRLiDn_f7qgpG4TyyGaQ4lV-ZXr6Qk,1768
55
55
  langgraph_api/js/tests/compose-postgres.yml,sha256=w4B3YRS0QEnTcZH2-MY0DYvR_c5GcER0uDa1Ga_knf8,1960
@@ -73,7 +73,7 @@ langgraph_api/js/tests/parser.test.mts,sha256=BBKUTveZnf-RI6B9XfTBLqy6tp84ddyu1t
73
73
  langgraph_api/js/tests/utils.mts,sha256=Jk1ZZmllNgSS6FJlSs9VaQxHqCEUzkqB5rRQwTSAOP4,441
74
74
  langgraph_api/js/tsconfig.json,sha256=imCYqVnqFpaBoZPx8k1nO4slHIWBFsSlmCYhO73cpBs,341
75
75
  langgraph_api/js/ui.py,sha256=XNT8iBcyT8XmbIqSQUWd-j_00HsaWB2vRTVabwFBkik,2439
76
- langgraph_api/js/yarn.lock,sha256=OEj5JbffHe8opUAki0eH_0XJbVmgasv9zcHhGeI0g0w,84019
76
+ langgraph_api/js/yarn.lock,sha256=HJuG663b4R2sqQDTQOHtpfMiK615k3CiUsRIZfGDd7k,84019
77
77
  langgraph_api/logging.py,sha256=JJIzbNIgLCN6ClQ3tA-Mm5ffuBGvpRDSZsEvnIlsuu4,3693
78
78
  langgraph_api/metadata.py,sha256=ptaxwmzdx2bUBSc1KRhqgF-Xnm-Zh2gqwSiHpl8LD9c,4482
79
79
  langgraph_api/middleware/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -95,14 +95,14 @@ langgraph_api/tunneling/cloudflare.py,sha256=iKb6tj-VWPlDchHFjuQyep2Dpb-w2NGfJKt
95
95
  langgraph_api/utils.py,sha256=92mSti9GfGdMRRWyESKQW5yV-75Z9icGHnIrBYvdypU,3619
96
96
  langgraph_api/validation.py,sha256=zMuKmwUEBjBgFMwAaeLZmatwGVijKv2sOYtYg7gfRtc,4950
97
97
  langgraph_api/webhook.py,sha256=1ncwO0rIZcj-Df9sxSnFEzd1gP1bfS4okeZQS8NSRoE,1382
98
- langgraph_api/worker.py,sha256=e5RNsEeYDrLJx5Y13LZXgYdSR_7kP77riJqZ6Ppao_E,11281
98
+ langgraph_api/worker.py,sha256=lLrTBfUdUzhPU9W6YQ6sCO3HOaWTfGxfLjlaVPQuOVs,12136
99
99
  langgraph_license/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
100
100
  langgraph_license/validation.py,sha256=ZKraAVJArAABKqrmHN-EN18ncoNUmRm500Yt1Sc7tUA,537
101
101
  langgraph_runtime/__init__.py,sha256=O4GgSmu33c-Pr8Xzxj_brcK5vkm70iNTcyxEjICFZxA,1075
102
102
  logging.json,sha256=3RNjSADZmDq38eHePMm1CbP6qZ71AmpBtLwCmKU9Zgo,379
103
- openapi.json,sha256=yYqTGjcV2kMhYfY8S59EWNbzDrZ4kVXchYK5T9OHK88,133203
104
- langgraph_api-0.2.7.dist-info/LICENSE,sha256=ZPwVR73Biwm3sK6vR54djCrhaRiM4cAD2zvOQZV8Xis,3859
105
- langgraph_api-0.2.7.dist-info/METADATA,sha256=c2KdHYF2bdtQI4EHeWZieTb2KzQo_3_RTdq0dZ5ckg0,4235
106
- langgraph_api-0.2.7.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
107
- langgraph_api-0.2.7.dist-info/entry_points.txt,sha256=3EYLgj89DfzqJHHYGxPH4A_fEtClvlRbWRUHaXO7hj4,77
108
- langgraph_api-0.2.7.dist-info/RECORD,,
103
+ openapi.json,sha256=GefWJwBrbrN_jNDAEFlwNEXx1ottRtvjOmKBi965KLU,133756
104
+ langgraph_api-0.2.9.dist-info/LICENSE,sha256=ZPwVR73Biwm3sK6vR54djCrhaRiM4cAD2zvOQZV8Xis,3859
105
+ langgraph_api-0.2.9.dist-info/METADATA,sha256=-USWsNozeKkqmxeoMi3Aj_SkdoTp_s2p3AEwtnHq1F4,4240
106
+ langgraph_api-0.2.9.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
107
+ langgraph_api-0.2.9.dist-info/entry_points.txt,sha256=3EYLgj89DfzqJHHYGxPH4A_fEtClvlRbWRUHaXO7hj4,77
108
+ langgraph_api-0.2.9.dist-info/RECORD,,
openapi.json CHANGED
@@ -3904,6 +3904,27 @@
3904
3904
  "description": "The number of results to skip.",
3905
3905
  "default": 0,
3906
3906
  "minimum": 0
3907
+ },
3908
+ "sort_by": {
3909
+ "type": "string",
3910
+ "enum": [
3911
+ "assistant_id",
3912
+ "created_at",
3913
+ "updated_at",
3914
+ "name",
3915
+ "graph_id"
3916
+ ],
3917
+ "title": "Sort By",
3918
+ "description": "The field to sort by."
3919
+ },
3920
+ "sort_order": {
3921
+ "type": "string",
3922
+ "enum": [
3923
+ "asc",
3924
+ "desc"
3925
+ ],
3926
+ "title": "Sort Order",
3927
+ "description": "The order to sort by."
3907
3928
  }
3908
3929
  },
3909
3930
  "type": "object",