@langchain/langgraph-api 1.1.16 → 1.2.0

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.
Files changed (52) hide show
  1. package/dist/api/protocol.d.mts +7 -0
  2. package/dist/api/protocol.mjs +157 -0
  3. package/dist/api/runs.mjs +15 -4
  4. package/dist/command.mjs +1 -1
  5. package/dist/experimental/embed/constants.d.mts +3 -0
  6. package/dist/experimental/embed/constants.mjs +11 -0
  7. package/dist/experimental/embed/protocol.d.mts +9 -0
  8. package/dist/experimental/embed/protocol.mjs +453 -0
  9. package/dist/experimental/embed/runs.d.mts +8 -0
  10. package/dist/experimental/embed/runs.mjs +202 -0
  11. package/dist/experimental/embed/threads.d.mts +8 -0
  12. package/dist/experimental/embed/threads.mjs +151 -0
  13. package/dist/experimental/embed/types.d.mts +77 -0
  14. package/dist/experimental/embed/types.mjs +1 -0
  15. package/dist/experimental/embed/utils.d.mts +29 -0
  16. package/dist/experimental/embed/utils.mjs +62 -0
  17. package/dist/experimental/embed.d.mts +11 -31
  18. package/dist/experimental/embed.mjs +19 -399
  19. package/dist/graph/load.d.mts +2 -2
  20. package/dist/graph/load.utils.mjs +13 -3
  21. package/dist/protocol/constants.d.mts +7 -0
  22. package/dist/protocol/constants.mjs +7 -0
  23. package/dist/protocol/service.d.mts +101 -0
  24. package/dist/protocol/service.mjs +568 -0
  25. package/dist/protocol/session/event-normalizers.d.mts +52 -0
  26. package/dist/protocol/session/event-normalizers.mjs +162 -0
  27. package/dist/protocol/session/index.d.mts +261 -0
  28. package/dist/protocol/session/index.mjs +826 -0
  29. package/dist/protocol/session/internal-types.d.mts +67 -0
  30. package/dist/protocol/session/internal-types.mjs +28 -0
  31. package/dist/protocol/session/metadata.d.mts +24 -0
  32. package/dist/protocol/session/metadata.mjs +95 -0
  33. package/dist/protocol/session/namespace.d.mts +47 -0
  34. package/dist/protocol/session/namespace.mjs +62 -0
  35. package/dist/protocol/session/state-normalizers.d.mts +57 -0
  36. package/dist/protocol/session/state-normalizers.mjs +430 -0
  37. package/dist/protocol/session/tool-calls.d.mts +27 -0
  38. package/dist/protocol/session/tool-calls.mjs +59 -0
  39. package/dist/protocol/types.d.mts +121 -0
  40. package/dist/protocol/types.mjs +1 -0
  41. package/dist/queue.mjs +8 -2
  42. package/dist/schemas.d.mts +58 -58
  43. package/dist/semver/index.mjs +19 -1
  44. package/dist/server.mjs +22 -3
  45. package/dist/state.mjs +1 -1
  46. package/dist/storage/ops.mjs +17 -5
  47. package/dist/storage/persist.mjs +1 -1
  48. package/dist/storage/types.d.mts +10 -1
  49. package/dist/stream.d.mts +25 -4
  50. package/dist/stream.mjs +203 -11
  51. package/dist/utils/serde.mjs +1 -1
  52. package/package.json +16 -14
@@ -0,0 +1,7 @@
1
+ import { Hono } from "hono";
2
+ import type { UpgradeWebSocket } from "hono/ws";
3
+ import type { Ops } from "../storage/types.mjs";
4
+ /**
5
+ * Register thread-centric protocol transport routes for LangGraph API.
6
+ */
7
+ export default function createProtocolApi(upgradeWebSocket: UpgradeWebSocket, ops: Ops): Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
@@ -0,0 +1,157 @@
1
+ import { zValidator } from "@hono/zod-validator";
2
+ import { Hono } from "hono";
3
+ import { streamSSE } from "hono/streaming";
4
+ import { v7 as uuid7 } from "uuid";
5
+ import { z } from "zod/v3";
6
+ import { ProtocolService } from "../protocol/service.mjs";
7
+ import { jsonExtra } from "../utils/hono.mjs";
8
+ import { serialiseAsDict } from "../utils/serde.mjs";
9
+ const ThreadIdSchema = z.object({ thread_id: z.string() });
10
+ const EventsFilterSchema = z
11
+ .object({
12
+ channels: z.array(z.string()),
13
+ namespaces: z.array(z.array(z.string())).optional(),
14
+ depth: z.number().int().nonnegative().optional(),
15
+ since: z.number().int().nonnegative().optional(),
16
+ })
17
+ .strict();
18
+ const ProtocolCommandSchema = z.object({
19
+ id: z.number().int().nonnegative(),
20
+ method: z.string(),
21
+ params: z.record(z.unknown()).optional(),
22
+ });
23
+ /**
24
+ * Normalize browser/node websocket message payloads into UTF-8 text so the
25
+ * protocol layer only needs to handle JSON strings.
26
+ */
27
+ const parseSocketPayload = async (event) => {
28
+ if (typeof event.data === "string")
29
+ return event.data;
30
+ if (event.data instanceof ArrayBuffer) {
31
+ return new TextDecoder().decode(new Uint8Array(event.data));
32
+ }
33
+ if (event.data instanceof Blob) {
34
+ const buffer = await event.data.arrayBuffer();
35
+ return new TextDecoder().decode(new Uint8Array(buffer));
36
+ }
37
+ return String(event.data);
38
+ };
39
+ /**
40
+ * Register thread-centric protocol transport routes for LangGraph API.
41
+ */
42
+ export default function createProtocolApi(upgradeWebSocket, ops) {
43
+ const api = new Hono();
44
+ const protocolService = new ProtocolService({
45
+ runs: ops.runs,
46
+ threads: ops.threads,
47
+ });
48
+ api.get("/v2/threads/:thread_id/stream", zValidator("param", ThreadIdSchema), upgradeWebSocket((c) => {
49
+ const { thread_id } = c.req.valid("param");
50
+ const record = protocolService.ensureThread({
51
+ threadId: thread_id,
52
+ transport: "websocket",
53
+ auth: c.var.auth,
54
+ });
55
+ return {
56
+ async onOpen(_event, ws) {
57
+ await protocolService.attachEventSink(thread_id, (event) => {
58
+ ws.send(serialiseAsDict(event));
59
+ });
60
+ },
61
+ async onMessage(event, ws) {
62
+ let payload;
63
+ try {
64
+ payload = JSON.parse(await parseSocketPayload(event));
65
+ }
66
+ catch {
67
+ ws.send(serialiseAsDict({
68
+ type: "error",
69
+ id: null,
70
+ error: "invalid_argument",
71
+ message: "Protocol commands must be valid JSON.",
72
+ }));
73
+ return;
74
+ }
75
+ if (typeof payload !== "object" ||
76
+ payload == null ||
77
+ typeof payload.id !== "number" ||
78
+ typeof payload.method !== "string") {
79
+ ws.send(serialiseAsDict({
80
+ type: "error",
81
+ id: null,
82
+ error: "invalid_argument",
83
+ message: "Protocol commands must include an integer id and string method.",
84
+ }));
85
+ return;
86
+ }
87
+ const response = await protocolService.handleCommand(record.threadId, payload);
88
+ // `null` means the session already wrote the response through
89
+ // the shared transport queue (see
90
+ // `ProtocolSession.handleSubscribeForResponse`). Sending again
91
+ // here would double-deliver the success and break ordering.
92
+ if (response != null) {
93
+ ws.send(serialiseAsDict(response));
94
+ }
95
+ },
96
+ onClose() {
97
+ void protocolService.closeThread(record.threadId);
98
+ },
99
+ onError() {
100
+ void protocolService.closeThread(record.threadId);
101
+ },
102
+ };
103
+ }));
104
+ api.post("/v2/threads/:thread_id/commands", zValidator("param", ThreadIdSchema), zValidator("json", ProtocolCommandSchema), async (c) => {
105
+ const { thread_id } = c.req.valid("param");
106
+ protocolService.ensureThread({
107
+ threadId: thread_id,
108
+ transport: "sse-http",
109
+ auth: c.var.auth,
110
+ });
111
+ const payload = c.req.valid("json");
112
+ return jsonExtra(c, await protocolService.handleCommand(thread_id, payload));
113
+ });
114
+ api.post("/v2/threads/:thread_id/stream", zValidator("param", ThreadIdSchema), zValidator("json", EventsFilterSchema), async (c) => {
115
+ const { thread_id } = c.req.valid("param");
116
+ protocolService.ensureThread({
117
+ threadId: thread_id,
118
+ transport: "sse-http",
119
+ auth: c.var.auth,
120
+ });
121
+ const body = c.req.valid("json");
122
+ const sinkId = uuid7();
123
+ const filter = {
124
+ channels: new Set(body.channels),
125
+ namespaces: body.namespaces,
126
+ depth: body.depth,
127
+ since: body.since,
128
+ };
129
+ return streamSSE(c, async (stream) => {
130
+ const delivered = new Set();
131
+ const writeSse = async (event) => {
132
+ if (event.event_id == null)
133
+ return;
134
+ if (delivered.has(event.event_id))
135
+ return;
136
+ delivered.add(event.event_id);
137
+ await stream.writeSSE({
138
+ id: event.event_id,
139
+ event: event.method,
140
+ data: serialiseAsDict(event),
141
+ });
142
+ };
143
+ await protocolService.attachFilteredEventSink(thread_id, {
144
+ id: sinkId,
145
+ filter,
146
+ send: writeSse,
147
+ });
148
+ stream.onAbort(() => {
149
+ protocolService.detachEventSink(thread_id, sinkId);
150
+ });
151
+ await new Promise((resolve) => {
152
+ stream.onAbort(() => resolve());
153
+ });
154
+ });
155
+ });
156
+ return api;
157
+ }
package/dist/api/runs.mjs CHANGED
@@ -150,7 +150,8 @@ api.post("/runs/stream", zValidator("json", schemas.RunCreate), async (c) => {
150
150
  : undefined;
151
151
  try {
152
152
  for await (const { event, data } of runs().stream.join(run.run_id, undefined, {
153
- cancelOnDisconnect,
153
+ signal: cancelOnDisconnect,
154
+ cancelOnDisconnect: cancelOnDisconnect != null,
154
155
  lastEventId: run.kwargs.resumable ? "-1" : undefined,
155
156
  ignore404: true,
156
157
  }, c.var.auth)) {
@@ -173,7 +174,12 @@ api.get("/runs/:run_id/stream", zValidator("param", z.object({ run_id: z.string(
173
174
  ? getDisconnectAbortSignal(c, stream)
174
175
  : undefined;
175
176
  try {
176
- for await (const { id, event, data } of runs().stream.join(run_id, undefined, { cancelOnDisconnect, lastEventId, ignore404: true }, c.var.auth)) {
177
+ for await (const { id, event, data } of runs().stream.join(run_id, undefined, {
178
+ signal: cancelOnDisconnect,
179
+ cancelOnDisconnect: cancelOnDisconnect != null,
180
+ lastEventId,
181
+ ignore404: true,
182
+ }, c.var.auth)) {
177
183
  await stream.writeSSE({ id, data: serialiseAsDict(data), event });
178
184
  }
179
185
  }
@@ -252,7 +258,8 @@ api.post("/threads/:thread_id/runs/stream", zValidator("param", z.object({ threa
252
258
  : undefined;
253
259
  try {
254
260
  for await (const { id, event, data } of runs().stream.join(run.run_id, thread_id, {
255
- cancelOnDisconnect,
261
+ signal: cancelOnDisconnect,
262
+ cancelOnDisconnect: cancelOnDisconnect != null,
256
263
  lastEventId: run.kwargs.resumable ? "-1" : undefined,
257
264
  }, c.var.auth)) {
258
265
  await stream.writeSSE({ id, data: serialiseAsDict(data), event });
@@ -304,7 +311,11 @@ api.get("/threads/:thread_id/runs/:run_id/stream", zValidator("param", z.object(
304
311
  const signal = cancel_on_disconnect
305
312
  ? getDisconnectAbortSignal(c, stream)
306
313
  : undefined;
307
- for await (const { id, event, data } of runs().stream.join(run_id, thread_id, { cancelOnDisconnect: signal, lastEventId }, c.var.auth)) {
314
+ for await (const { id, event, data } of runs().stream.join(run_id, thread_id, {
315
+ signal,
316
+ cancelOnDisconnect: signal != null,
317
+ lastEventId,
318
+ }, c.var.auth)) {
308
319
  await stream.writeSSE({ id, data: serialiseAsDict(data), event });
309
320
  }
310
321
  });
package/dist/command.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Command, Send } from "@langchain/langgraph";
2
2
  export const getLangGraphCommand = (command) => {
3
- let goto = command.goto != null && !Array.isArray(command.goto)
3
+ const goto = command.goto != null && !Array.isArray(command.goto)
4
4
  ? [command.goto]
5
5
  : command.goto;
6
6
  return new Command({
@@ -0,0 +1,3 @@
1
+ import type { StreamMode } from "../../storage/types.mjs";
2
+ export declare const PROTOCOL_VERSION = "0.3.0";
3
+ export declare const DEFAULT_PROTOCOL_STREAM_MODES: StreamMode[];
@@ -0,0 +1,11 @@
1
+ export const PROTOCOL_VERSION = "0.3.0";
2
+ export const DEFAULT_PROTOCOL_STREAM_MODES = [
3
+ "values",
4
+ "updates",
5
+ "messages",
6
+ "tools",
7
+ "custom",
8
+ "debug",
9
+ "checkpoints",
10
+ "tasks",
11
+ ];
@@ -0,0 +1,9 @@
1
+ import type { Hono } from "hono";
2
+ import type { UpgradeWebSocket } from "hono/ws";
3
+ import type { EmbedRouteContext } from "./types.mjs";
4
+ /**
5
+ * Register thread-centric v2 protocol routes on an embed server Hono app.
6
+ *
7
+ * @experimental Does not follow semver.
8
+ */
9
+ export declare function registerProtocolRoutes(api: Hono, context: EmbedRouteContext, upgradeWebSocket?: UpgradeWebSocket): void;