@langchain/langgraph-api 1.1.14 → 1.1.16

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.
@@ -1,6 +1,6 @@
1
1
  import { zValidator } from "@hono/zod-validator";
2
2
  import { Hono } from "hono";
3
- import { v4 as uuid } from "uuid";
3
+ import { v7 as uuid } from "uuid";
4
4
  import { z } from "zod/v3";
5
5
  import { getAssistantId, getCachedStaticGraphSchema, getGraph, } from "../graph/load.mjs";
6
6
  import { getRuntimeGraphSchema } from "../graph/parser/index.mjs";
package/dist/api/runs.mjs CHANGED
@@ -2,7 +2,7 @@ import { zValidator } from "@hono/zod-validator";
2
2
  import { Hono } from "hono";
3
3
  import { HTTPException } from "hono/http-exception";
4
4
  import { streamSSE } from "hono/streaming";
5
- import { v4 as uuid4 } from "uuid";
5
+ import { v7 as uuid7 } from "uuid";
6
6
  import { z } from "zod/v3";
7
7
  import { getAssistantId } from "../graph/load.mjs";
8
8
  import { logError, logger } from "../logging.mjs";
@@ -14,7 +14,7 @@ const api = new Hono();
14
14
  const createValidRun = async (threadId, payload, kwargs) => {
15
15
  const { assistant_id: assistantId, ...run } = payload;
16
16
  const { auth, headers } = kwargs ?? {};
17
- const runId = uuid4();
17
+ const runId = uuid7();
18
18
  const streamMode = Array.isArray(payload.stream_mode)
19
19
  ? payload.stream_mode
20
20
  : payload.stream_mode != null
@@ -1,6 +1,6 @@
1
1
  import { zValidator } from "@hono/zod-validator";
2
2
  import { Hono } from "hono";
3
- import { v4 as uuid4 } from "uuid";
3
+ import { v7 as uuid7 } from "uuid";
4
4
  import { z } from "zod/v3";
5
5
  import * as schemas from "../schemas.mjs";
6
6
  import { stateSnapshotToThreadState } from "../state.mjs";
@@ -11,7 +11,7 @@ const api = new Hono();
11
11
  api.post("/threads", zValidator("json", schemas.ThreadCreate), async (c) => {
12
12
  // Create Thread
13
13
  const payload = c.req.valid("json");
14
- const thread = await threads().put(payload.thread_id || uuid4(), { metadata: payload.metadata, if_exists: payload.if_exists ?? "raise" }, c.var.auth);
14
+ const thread = await threads().put(payload.thread_id || uuid7(), { metadata: payload.metadata, if_exists: payload.if_exists ?? "raise" }, c.var.auth);
15
15
  if (payload.supersteps?.length) {
16
16
  await threads().state.bulk({ configurable: { thread_id: thread.thread_id } }, payload.supersteps, c.var.auth);
17
17
  }
@@ -1,7 +1,7 @@
1
1
  import { Hono } from "hono";
2
2
  import { zValidator } from "@hono/zod-validator";
3
3
  import { streamSSE } from "hono/streaming";
4
- import { v4 as uuidv4 } from "uuid";
4
+ import { v7 as uuidv7 } from "uuid";
5
5
  import * as schemas from "../schemas.mjs";
6
6
  import { z } from "zod/v3";
7
7
  import { streamState } from "../stream.mjs";
@@ -9,9 +9,9 @@ import { serialiseAsDict, serializeError } from "../utils/serde.mjs";
9
9
  import { getDisconnectAbortSignal, jsonExtra } from "../utils/hono.mjs";
10
10
  import { stateSnapshotToThreadState } from "../state.mjs";
11
11
  import { ensureContentType } from "../http/middleware.mjs";
12
- function createStubRun(threadId, payload) {
12
+ function createStubRun(threadId, payload, overrides) {
13
13
  const now = new Date();
14
- const runId = uuidv4();
14
+ const runId = uuidv7();
15
15
  let streamMode = Array.isArray(payload.stream_mode)
16
16
  ? payload.stream_mode
17
17
  : payload.stream_mode
@@ -41,7 +41,7 @@ function createStubRun(threadId, payload) {
41
41
  thread_id: threadId,
42
42
  assistant_id: payload.assistant_id,
43
43
  metadata: payload.metadata ?? {},
44
- status: "running",
44
+ status: overrides?.status ?? "running",
45
45
  kwargs: {
46
46
  input: payload.input,
47
47
  command: payload.command,
@@ -54,7 +54,9 @@ function createStubRun(threadId, payload) {
54
54
  subgraphs: payload.stream_subgraphs,
55
55
  temporary: false,
56
56
  },
57
- multitask_strategy: "reject",
57
+ multitask_strategy: (overrides?.multitask_strategy ??
58
+ payload.multitask_strategy ??
59
+ "reject"),
58
60
  created_at: now,
59
61
  updated_at: now,
60
62
  };
@@ -65,6 +67,32 @@ function createStubRun(threadId, payload) {
65
67
  * @experimental Does not follow semver.
66
68
  */
67
69
  export function createEmbedServer(options) {
70
+ const threadRunState = new Map();
71
+ function getThreadState(threadId) {
72
+ let state = threadRunState.get(threadId);
73
+ if (!state) {
74
+ state = { activeRunId: null, pendingRuns: [] };
75
+ threadRunState.set(threadId, state);
76
+ }
77
+ return state;
78
+ }
79
+ async function waitForRunReady(threadId, runId, signal) {
80
+ const state = getThreadState(threadId);
81
+ const run = state.pendingRuns.find((r) => r.run_id === runId);
82
+ if (!run)
83
+ return null;
84
+ while (true) {
85
+ if (signal?.aborted) {
86
+ throw new DOMException("Aborted", "AbortError");
87
+ }
88
+ const isHead = state.pendingRuns[0]?.run_id === runId;
89
+ const noActive = !state.activeRunId;
90
+ if (isHead && noActive)
91
+ break;
92
+ await new Promise((r) => setTimeout(r, 50));
93
+ }
94
+ return run;
95
+ }
68
96
  async function getGraph(graphId) {
69
97
  const targetGraph = options.graph[graphId];
70
98
  targetGraph.store = options.store;
@@ -76,7 +104,7 @@ export function createEmbedServer(options) {
76
104
  api.post("/threads", zValidator("json", schemas.ThreadCreate), async (c) => {
77
105
  // create a new thread
78
106
  const payload = c.req.valid("json");
79
- const threadId = payload.thread_id || uuidv4();
107
+ const threadId = payload.thread_id || uuidv7();
80
108
  return jsonExtra(c, await options.threads.set(threadId, {
81
109
  kind: "put",
82
110
  metadata: payload.metadata,
@@ -222,16 +250,102 @@ export function createEmbedServer(options) {
222
250
  }
223
251
  return jsonExtra(c, result);
224
252
  });
253
+ api.post("/threads/:thread_id/runs", zValidator("param", z.object({ thread_id: z.string().uuid() })), zValidator("json", schemas.RunCreate), async (c) => {
254
+ const { thread_id } = c.req.valid("param");
255
+ const payload = c.req.valid("json");
256
+ const thread = await options.threads.get(thread_id);
257
+ if (thread == null)
258
+ return c.json({ error: "Thread not found" }, 404);
259
+ const state = getThreadState(thread_id);
260
+ const multitaskStrategy = payload.multitask_strategy ?? "reject";
261
+ const shouldEnqueue = multitaskStrategy === "enqueue" && state.activeRunId != null;
262
+ const run = createStubRun(thread_id, payload, {
263
+ status: shouldEnqueue ? "pending" : "running",
264
+ multitask_strategy: multitaskStrategy,
265
+ });
266
+ state.pendingRuns.push(run);
267
+ c.header("Content-Location", `/threads/${thread_id}/runs/${run.run_id}`);
268
+ return jsonExtra(c, run);
269
+ });
270
+ api.get("/threads/:thread_id/runs/:run_id/stream", zValidator("param", z.object({
271
+ thread_id: z.string().uuid(),
272
+ run_id: z.string().uuid(),
273
+ })), async (c) => {
274
+ const { thread_id, run_id } = c.req.valid("param");
275
+ const thread = await options.threads.get(thread_id);
276
+ if (thread == null)
277
+ return c.json({ error: "Thread not found" }, 404);
278
+ return streamSSE(c, async (stream) => {
279
+ const signal = getDisconnectAbortSignal(c, stream);
280
+ const state = getThreadState(thread_id);
281
+ try {
282
+ const run = await waitForRunReady(thread_id, run_id, signal);
283
+ if (!run) {
284
+ await stream.writeSSE({
285
+ data: serialiseAsDict({ error: "Run not found" }),
286
+ event: "error",
287
+ });
288
+ return;
289
+ }
290
+ const idx = state.pendingRuns.findIndex((r) => r.run_id === run_id);
291
+ if (idx >= 0)
292
+ state.pendingRuns.splice(idx, 1);
293
+ state.activeRunId = run_id;
294
+ run.status = "running";
295
+ try {
296
+ for await (const { event, data } of streamState(run, {
297
+ attempt: 1,
298
+ getGraph,
299
+ signal,
300
+ })) {
301
+ await stream.writeSSE({ data: serialiseAsDict(data), event });
302
+ }
303
+ }
304
+ catch (error) {
305
+ await stream.writeSSE({
306
+ data: serialiseAsDict(serializeError(error)),
307
+ event: "error",
308
+ });
309
+ }
310
+ finally {
311
+ if (state.activeRunId === run_id) {
312
+ state.activeRunId = null;
313
+ }
314
+ }
315
+ }
316
+ catch (err) {
317
+ if (err instanceof DOMException && err.name === "AbortError") {
318
+ return;
319
+ }
320
+ throw err;
321
+ }
322
+ });
323
+ });
324
+ api.post("/threads/:thread_id/runs/:run_id/cancel", zValidator("param", z.object({
325
+ thread_id: z.string().uuid(),
326
+ run_id: z.string().uuid(),
327
+ })), async (c) => {
328
+ const { thread_id, run_id } = c.req.valid("param");
329
+ const state = getThreadState(thread_id);
330
+ const idx = state.pendingRuns.findIndex((r) => r.run_id === run_id);
331
+ if (idx >= 0) {
332
+ state.pendingRuns.splice(idx, 1);
333
+ }
334
+ return new Response(null, { status: 204 });
335
+ });
225
336
  api.post("/threads/:thread_id/runs/stream", zValidator("param", z.object({ thread_id: z.string().uuid() })), zValidator("json", schemas.RunCreate), async (c) => {
226
- // Stream Run
337
+ // Stream Run (create + stream in one request)
227
338
  const { thread_id } = c.req.valid("param");
228
339
  const payload = c.req.valid("json");
229
340
  const thread = await options.threads.get(thread_id);
230
341
  if (thread == null)
231
342
  return c.json({ error: "Thread not found" }, 404);
343
+ const state = getThreadState(thread_id);
344
+ const run = createStubRun(thread_id, payload);
345
+ c.header("Content-Location", `/threads/${thread_id}/runs/${run.run_id}`);
232
346
  return streamSSE(c, async (stream) => {
233
347
  const signal = getDisconnectAbortSignal(c, stream);
234
- const run = createStubRun(thread_id, payload);
348
+ state.activeRunId = run.run_id;
235
349
  await options.threads.set(thread_id, {
236
350
  kind: "patch",
237
351
  metadata: {
@@ -254,14 +368,21 @@ export function createEmbedServer(options) {
254
368
  event: "error",
255
369
  });
256
370
  }
371
+ finally {
372
+ if (state.activeRunId === run.run_id) {
373
+ state.activeRunId = null;
374
+ }
375
+ }
257
376
  });
258
377
  });
259
378
  api.post("/runs/stream", zValidator("json", schemas.RunCreate), async (c) => {
260
379
  // Stream Stateless Run
380
+ const payload = c.req.valid("json");
381
+ const threadId = uuidv7();
382
+ const run = createStubRun(threadId, payload);
383
+ c.header("Content-Location", `/threads/${threadId}/runs/${run.run_id}`);
261
384
  return streamSSE(c, async (stream) => {
262
- const payload = c.req.valid("json");
263
385
  const signal = getDisconnectAbortSignal(c, stream);
264
- const threadId = uuidv4();
265
386
  await options.threads.set(threadId, {
266
387
  kind: "put",
267
388
  metadata: {
@@ -270,7 +391,6 @@ export function createEmbedServer(options) {
270
391
  },
271
392
  });
272
393
  try {
273
- const run = createStubRun(threadId, payload);
274
394
  try {
275
395
  for await (const { event, data } of streamState(run, {
276
396
  attempt: 1,
@@ -1,5 +1,5 @@
1
1
  import { HTTPException } from "hono/http-exception";
2
- import { v4 as uuid4, v5 as uuid5 } from "uuid";
2
+ import { v7 as uuid7, v5 as uuid5 } from "uuid";
3
3
  import { handleAuthEvent, isAuthMatching } from "../auth/index.mjs";
4
4
  import { getLangGraphCommand } from "../command.mjs";
5
5
  import { getGraph, NAMESPACE_GRAPH } from "../graph/load.mjs";
@@ -650,7 +650,7 @@ export class FileSystemThreads {
650
650
  }
651
651
  return thread;
652
652
  });
653
- const newThreadId = uuid4();
653
+ const newThreadId = uuid7();
654
654
  const now = new Date();
655
655
  const newMetadata = { ...fromThread.metadata, thread_id: newThreadId };
656
656
  await handleAuthEvent(auth, "threads:create", {
@@ -948,7 +948,7 @@ export class FileSystemRuns {
948
948
  }
949
949
  const now = new Date();
950
950
  if (!existingThread && (threadId == null || ifNotExists === "create")) {
951
- threadId ??= uuid4();
951
+ threadId ??= uuid7();
952
952
  const thread = {
953
953
  thread_id: threadId,
954
954
  status: "busy",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@langchain/langgraph-api",
3
- "version": "1.1.14",
3
+ "version": "1.1.16",
4
4
  "type": "module",
5
5
  "engines": {
6
6
  "node": "^18.19.0 || >=20.16.0"
@@ -70,13 +70,13 @@
70
70
  "winston": "^3.17.0",
71
71
  "winston-console-format": "^1.0.8",
72
72
  "zod": "^3.25.76 || ^4",
73
- "@langchain/langgraph-ui": "1.1.14"
73
+ "@langchain/langgraph-ui": "1.1.16"
74
74
  },
75
75
  "peerDependencies": {
76
76
  "@langchain/core": "^0.3.59 || ^1.0.1",
77
77
  "@langchain/langgraph": "^0.2.57 || ^0.3.0 || ^0.4.0 || ^1.0.0-alpha || ^1.0.0",
78
78
  "@langchain/langgraph-checkpoint": "~0.0.16 || ^0.1.0 || ~1.0.0",
79
- "@langchain/langgraph-sdk": "~1.6.5",
79
+ "@langchain/langgraph-sdk": "^1.6.5",
80
80
  "typescript": "^5.5.4"
81
81
  },
82
82
  "peerDependenciesMeta": {
@@ -98,9 +98,9 @@
98
98
  "typescript": "^4.9.5 || ^5.4.5",
99
99
  "vitest": "^3.2.4",
100
100
  "wait-port": "^1.1.0",
101
- "@langchain/langgraph": "1.2.0",
102
- "@langchain/langgraph-checkpoint": "1.0.0",
103
- "@langchain/langgraph-sdk": "1.6.5"
101
+ "@langchain/langgraph": "1.2.3",
102
+ "@langchain/langgraph-checkpoint": "1.0.1",
103
+ "@langchain/langgraph-sdk": "1.7.3"
104
104
  },
105
105
  "scripts": {
106
106
  "clean": "rm -rf dist/ .turbo/ ./tests/graphs/.langgraph_api/",