@langchain/langgraph-api 1.2.2-rc.0 → 1.2.2

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 (42) hide show
  1. package/dist/src/api/assistants.d.mts +3 -0
  2. package/dist/src/api/assistants.mjs +194 -0
  3. package/dist/src/api/meta.d.mts +3 -0
  4. package/dist/src/api/meta.mjs +65 -0
  5. package/dist/src/api/protocol.d.mts +7 -0
  6. package/dist/src/api/protocol.mjs +157 -0
  7. package/dist/src/api/runs.d.mts +3 -0
  8. package/dist/src/api/runs.mjs +335 -0
  9. package/dist/src/api/store.d.mts +3 -0
  10. package/dist/src/api/store.mjs +111 -0
  11. package/dist/src/api/threads.d.mts +3 -0
  12. package/dist/src/api/threads.mjs +143 -0
  13. package/dist/src/graph/load.utils.d.mts +22 -0
  14. package/dist/src/graph/load.utils.mjs +59 -0
  15. package/dist/src/protocol/service.d.mts +101 -0
  16. package/dist/src/protocol/service.mjs +568 -0
  17. package/dist/src/protocol/session/event-normalizers.d.mts +52 -0
  18. package/dist/src/protocol/session/index.d.mts +261 -0
  19. package/dist/src/protocol/session/index.mjs +826 -0
  20. package/dist/src/protocol/session/namespace.d.mts +47 -0
  21. package/dist/src/protocol/session/namespace.mjs +62 -0
  22. package/dist/src/protocol/types.d.mts +121 -0
  23. package/dist/src/protocol/types.mjs +1 -0
  24. package/dist/src/schemas.d.mts +1552 -0
  25. package/dist/src/semver/index.d.mts +15 -0
  26. package/dist/src/semver/index.mjs +46 -0
  27. package/dist/src/semver/satisfiesPeerRange.d.mts +1 -0
  28. package/dist/src/semver/satisfiesPeerRange.mjs +19 -0
  29. package/dist/src/state.d.mts +3 -0
  30. package/dist/src/state.mjs +30 -0
  31. package/dist/src/storage/context.d.mts +3 -0
  32. package/dist/src/storage/context.mjs +11 -0
  33. package/dist/src/storage/ops.mjs +1281 -0
  34. package/dist/src/stream.d.mts +64 -0
  35. package/dist/src/stream.mjs +427 -0
  36. package/dist/src/utils/hono.d.mts +5 -0
  37. package/dist/src/utils/hono.mjs +24 -0
  38. package/dist/src/utils/runnableConfig.d.mts +3 -0
  39. package/dist/src/utils/runnableConfig.mjs +45 -0
  40. package/dist/src/webhook.d.mts +11 -0
  41. package/dist/src/webhook.mjs +30 -0
  42. package/package.json +4 -4
@@ -0,0 +1,3 @@
1
+ import { Hono } from "hono";
2
+ declare const api: Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
3
+ export default api;
@@ -0,0 +1,194 @@
1
+ import { zValidator } from "@hono/zod-validator";
2
+ import { Hono } from "hono";
3
+ import { v7 as uuid } from "uuid";
4
+ import { z } from "zod/v3";
5
+ import { getAssistantId, getCachedStaticGraphSchema, getGraph, } from "../graph/load.mjs";
6
+ import { getRuntimeGraphSchema } from "../graph/parser/index.mjs";
7
+ import { HTTPException } from "hono/http-exception";
8
+ import * as schemas from "../schemas.mjs";
9
+ import { assistants } from "../storage/context.mjs";
10
+ const api = new Hono();
11
+ const RunnableConfigSchema = z.object({
12
+ tags: z.array(z.string()).optional(),
13
+ metadata: z.record(z.unknown()).optional(),
14
+ run_name: z.string().optional(),
15
+ max_concurrency: z.number().optional(),
16
+ recursion_limit: z.number().optional(),
17
+ configurable: z.record(z.unknown()).optional(),
18
+ run_id: z.string().uuid().optional(),
19
+ });
20
+ const getRunnableConfig = (userConfig) => {
21
+ if (!userConfig)
22
+ return {};
23
+ return {
24
+ configurable: userConfig.configurable,
25
+ tags: userConfig.tags,
26
+ metadata: userConfig.metadata,
27
+ runName: userConfig.run_name,
28
+ maxConcurrency: userConfig.max_concurrency,
29
+ recursionLimit: userConfig.recursion_limit,
30
+ runId: userConfig.run_id,
31
+ };
32
+ };
33
+ api.post("/assistants", zValidator("json", schemas.AssistantCreate), async (c) => {
34
+ // Create Assistant
35
+ const payload = c.req.valid("json");
36
+ const assistant = await assistants().put(payload.assistant_id ?? uuid(), {
37
+ config: payload.config ?? {},
38
+ context: payload.context ?? {},
39
+ graph_id: payload.graph_id,
40
+ metadata: payload.metadata ?? {},
41
+ if_exists: payload.if_exists ?? "raise",
42
+ name: payload.name ?? "Untitled",
43
+ description: payload.description,
44
+ }, c.var.auth);
45
+ return c.json(assistant);
46
+ });
47
+ api.post("/assistants/search", zValidator("json", schemas.AssistantSearchRequest), async (c) => {
48
+ // Search Assistants
49
+ const payload = c.req.valid("json");
50
+ const result = [];
51
+ let total = 0;
52
+ for await (const item of assistants().search({
53
+ graph_id: payload.graph_id,
54
+ name: payload.name,
55
+ metadata: payload.metadata,
56
+ limit: payload.limit ?? 10,
57
+ offset: payload.offset ?? 0,
58
+ sort_by: payload.sort_by,
59
+ sort_order: payload.sort_order,
60
+ select: payload.select,
61
+ }, c.var.auth)) {
62
+ result.push(Object.fromEntries(Object.entries(item.assistant).filter(([k]) => !payload.select || payload.select.includes(k))));
63
+ if (total === 0) {
64
+ total = item.total;
65
+ }
66
+ }
67
+ if (total === payload.limit) {
68
+ c.res.headers.set("X-Pagination-Next", ((payload.offset ?? 0) + total).toString());
69
+ c.res.headers.set("X-Pagination-Total", ((payload.offset ?? 0) + total + 1).toString());
70
+ }
71
+ else {
72
+ c.res.headers.set("X-Pagination-Total", ((payload.offset ?? 0) + total).toString());
73
+ }
74
+ return c.json(result);
75
+ });
76
+ api.post("/assistants/count", zValidator("json", schemas.AssistantCountRequest), async (c) => {
77
+ const payload = c.req.valid("json");
78
+ const total = await assistants().count(payload, c.var.auth);
79
+ return c.json(total);
80
+ });
81
+ api.get("/assistants/:assistant_id", async (c) => {
82
+ // Get Assistant
83
+ const assistantId = getAssistantId(c.req.param("assistant_id"));
84
+ return c.json(await assistants().get(assistantId, c.var.auth));
85
+ });
86
+ api.delete("/assistants/:assistant_id", async (c) => {
87
+ // Delete Assistant
88
+ const assistantId = getAssistantId(c.req.param("assistant_id"));
89
+ const deleteThreads = c.req.query("delete_threads") === "true";
90
+ return c.json(await assistants().delete(assistantId, deleteThreads, c.var.auth));
91
+ });
92
+ api.patch("/assistants/:assistant_id", zValidator("json", schemas.AssistantPatch), async (c) => {
93
+ // Patch Assistant
94
+ const assistantId = getAssistantId(c.req.param("assistant_id"));
95
+ const payload = c.req.valid("json");
96
+ return c.json(await assistants().patch(assistantId, payload, c.var.auth));
97
+ });
98
+ api.get("/assistants/:assistant_id/graph", zValidator("query", z.object({ xray: schemas.coercedBoolean.optional() })), async (c) => {
99
+ // Get Assistant Graph
100
+ const assistantId = getAssistantId(c.req.param("assistant_id"));
101
+ const assistant = await assistants().get(assistantId, c.var.auth);
102
+ const { xray } = c.req.valid("query");
103
+ const config = getRunnableConfig(assistant.config);
104
+ const graph = await getGraph(assistant.graph_id, config);
105
+ const drawable = await graph.getGraphAsync({
106
+ ...config,
107
+ xray: xray ?? undefined,
108
+ });
109
+ return c.json(drawable.toJSON());
110
+ });
111
+ api.get("/assistants/:assistant_id/schemas", zValidator("json", z.object({ config: RunnableConfigSchema.optional() })), async (c) => {
112
+ // Get Assistant Schemas
113
+ const json = c.req.valid("json");
114
+ const assistantId = getAssistantId(c.req.param("assistant_id"));
115
+ const assistant = await assistants().get(assistantId, c.var.auth);
116
+ const config = getRunnableConfig(json.config);
117
+ const graph = await getGraph(assistant.graph_id, config);
118
+ const schema = await (async () => {
119
+ const runtimeSchema = await getRuntimeGraphSchema(graph);
120
+ if (runtimeSchema)
121
+ return runtimeSchema;
122
+ const graphSchema = await getCachedStaticGraphSchema(assistant.graph_id);
123
+ const rootGraphId = Object.keys(graphSchema).find((i) => !i.includes("|"));
124
+ if (!rootGraphId)
125
+ throw new HTTPException(404, { message: "Failed to find root graph" });
126
+ return graphSchema[rootGraphId];
127
+ })();
128
+ return c.json({
129
+ graph_id: assistant.graph_id,
130
+ input_schema: schema.input,
131
+ output_schema: schema.output,
132
+ state_schema: schema.state,
133
+ config_schema: schema.config,
134
+ // From JS PoV `configSchema` and `contextSchema` are indistinguishable,
135
+ // thus we use config_schema for context_schema.
136
+ context_schema: schema.config,
137
+ });
138
+ });
139
+ api.get("/assistants/:assistant_id/subgraphs/:namespace?", zValidator("param", z.object({ assistant_id: z.string(), namespace: z.string().optional() })), zValidator("query", z.object({ recurse: schemas.coercedBoolean.optional() })), async (c) => {
140
+ // Get Assistant Subgraphs
141
+ const { assistant_id, namespace } = c.req.valid("param");
142
+ const { recurse } = c.req.valid("query");
143
+ const assistantId = getAssistantId(assistant_id);
144
+ const assistant = await assistants().get(assistantId, c.var.auth);
145
+ const config = getRunnableConfig(assistant.config);
146
+ const graph = await getGraph(assistant.graph_id, config);
147
+ const result = [];
148
+ const subgraphsGenerator = "getSubgraphsAsync" in graph
149
+ ? graph.getSubgraphsAsync.bind(graph)
150
+ : // @ts-expect-error older versions of langgraph don't have getSubgraphsAsync
151
+ graph.getSubgraphs.bind(graph);
152
+ let graphSchemaPromise;
153
+ for await (const [ns, subgraph] of subgraphsGenerator(namespace, recurse)) {
154
+ const schema = await (async () => {
155
+ const runtimeSchema = await getRuntimeGraphSchema(subgraph);
156
+ if (runtimeSchema)
157
+ return runtimeSchema;
158
+ graphSchemaPromise ??= getCachedStaticGraphSchema(assistant.graph_id);
159
+ const graphSchema = await graphSchemaPromise;
160
+ const rootGraphId = Object.keys(graphSchema).find((i) => !i.includes("|"));
161
+ if (!rootGraphId) {
162
+ throw new HTTPException(404, {
163
+ message: "Failed to find root graph",
164
+ });
165
+ }
166
+ return graphSchema[`${rootGraphId}|${ns}`] || graphSchema[rootGraphId];
167
+ })();
168
+ result.push([ns, schema]);
169
+ }
170
+ return c.json(Object.fromEntries(result));
171
+ });
172
+ api.post("/assistants/:assistant_id/latest", zValidator("json", schemas.AssistantLatestVersion), async (c) => {
173
+ // Set Latest Assistant Version
174
+ const assistantId = getAssistantId(c.req.param("assistant_id"));
175
+ const { version } = c.req.valid("json");
176
+ return c.json(await assistants().setLatest(assistantId, version, c.var.auth));
177
+ });
178
+ api.post("/assistants/:assistant_id/versions", zValidator("json", z.object({
179
+ limit: z.number().min(1).max(1000).optional().default(10),
180
+ offset: z.number().min(0).optional().default(0),
181
+ metadata: z.record(z.unknown()).optional(),
182
+ })), async (c) => {
183
+ // Get Assistant Versions
184
+ const assistantId = getAssistantId(c.req.param("assistant_id"));
185
+ const { limit, offset, metadata } = c.req.valid("json");
186
+ const versions = await assistants().getVersions(assistantId, { limit, offset, metadata }, c.var.auth);
187
+ if (!versions?.length) {
188
+ throw new HTTPException(404, {
189
+ message: `Assistant "${assistantId}" not found.`,
190
+ });
191
+ }
192
+ return c.json(versions);
193
+ });
194
+ export default api;
@@ -0,0 +1,3 @@
1
+ import { Hono } from "hono";
2
+ declare const api: Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
3
+ export default api;
@@ -0,0 +1,65 @@
1
+ import { Hono } from "hono";
2
+ import * as fs from "node:fs/promises";
3
+ import * as path from "node:path";
4
+ import * as url from "node:url";
5
+ const api = new Hono();
6
+ // Get the version using the same pattern as semver/index.mts
7
+ const packageJsonPath = path.resolve(url.fileURLToPath(import.meta.url), "../../../package.json");
8
+ let version;
9
+ let langgraph_js_version;
10
+ let versionInfoLoaded = false;
11
+ const loadVersionInfo = async () => {
12
+ try {
13
+ const packageJson = JSON.parse(await fs.readFile(packageJsonPath, "utf-8"));
14
+ version = packageJson.version;
15
+ }
16
+ catch {
17
+ console.warn("Could not determine version of langgraph-api");
18
+ }
19
+ // Get the installed version of @langchain/langgraph
20
+ try {
21
+ const langgraphPkg = await import("@langchain/langgraph/package.json");
22
+ if (langgraphPkg?.default?.version) {
23
+ langgraph_js_version = langgraphPkg.default.version;
24
+ }
25
+ }
26
+ catch {
27
+ console.warn("Could not determine version of @langchain/langgraph");
28
+ }
29
+ };
30
+ // read env variable
31
+ const env = process.env;
32
+ api.get("/info", async (c) => {
33
+ if (!versionInfoLoaded) {
34
+ await loadVersionInfo();
35
+ versionInfoLoaded = true;
36
+ }
37
+ const langsmithApiKey = env["LANGSMITH_API_KEY"] || env["LANGCHAIN_API_KEY"];
38
+ const langsmithTracing = (() => {
39
+ if (langsmithApiKey) {
40
+ // Check if any tracing variable is explicitly set to "false"
41
+ const tracingVars = [
42
+ env["LANGCHAIN_TRACING_V2"],
43
+ env["LANGCHAIN_TRACING"],
44
+ env["LANGSMITH_TRACING_V2"],
45
+ env["LANGSMITH_TRACING"],
46
+ ];
47
+ // Return true unless explicitly disabled
48
+ return !tracingVars.some((val) => val === "false" || val === "False");
49
+ }
50
+ return undefined;
51
+ })();
52
+ return c.json({
53
+ version,
54
+ langgraph_js_version,
55
+ context: "js",
56
+ flags: {
57
+ assistants: true,
58
+ crons: false,
59
+ langsmith: !!langsmithTracing,
60
+ langsmith_tracing_replicas: true,
61
+ },
62
+ });
63
+ });
64
+ api.get("/ok", (c) => c.json({ ok: true }));
65
+ export default api;
@@ -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("/threads/:thread_id/stream/events", 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("/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("/threads/:thread_id/stream/events", 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
+ }
@@ -0,0 +1,3 @@
1
+ import { Hono } from "hono";
2
+ declare const api: Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
3
+ export default api;