@langchain/langgraph-api 0.0.56 → 0.0.58

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # @langchain/langgraph-api
2
2
 
3
+ ## 0.0.58
4
+
5
+ ### Patch Changes
6
+
7
+ - f65f619: fix(api): send Content-Location header for stateless runs
8
+ - c857357: feat(api): harden embed server, implement missing endpoints needed for interrupts
9
+ - @langchain/langgraph-ui@0.0.58
10
+
11
+ ## 0.0.57
12
+
13
+ ### Patch Changes
14
+
15
+ - 31cc9f7: support description property for `langgraph.json`
16
+ - @langchain/langgraph-ui@0.0.57
17
+
3
18
  ## 0.0.56
4
19
 
5
20
  ### Patch Changes
package/dist/api/runs.mjs CHANGED
@@ -143,6 +143,7 @@ api.post("/runs/stream", zValidator("json", schemas.RunCreate), async (c) => {
143
143
  auth: c.var.auth,
144
144
  headers: c.req.raw.headers,
145
145
  });
146
+ c.header("Content-Location", `/runs/${run.run_id}`);
146
147
  return streamSSE(c, async (stream) => {
147
148
  const cancelOnDisconnect = payload.on_disconnect === "cancel"
148
149
  ? getDisconnectAbortSignal(c, stream)
@@ -166,6 +167,7 @@ api.get("/runs/:run_id/stream", zValidator("param", z.object({ run_id: z.string(
166
167
  const { run_id } = c.req.valid("param");
167
168
  const query = c.req.valid("query");
168
169
  const lastEventId = c.req.header("Last-Event-ID") || undefined;
170
+ c.header("Content-Location", `/runs/${run_id}`);
169
171
  return streamSSE(c, async (stream) => {
170
172
  const cancelOnDisconnect = query.cancel_on_disconnect
171
173
  ? getDisconnectAbortSignal(c, stream)
@@ -187,6 +189,7 @@ api.post("/runs/wait", zValidator("json", schemas.RunCreate), async (c) => {
187
189
  auth: c.var.auth,
188
190
  headers: c.req.raw.headers,
189
191
  });
192
+ c.header("Content-Location", `/runs/${run.run_id}`);
190
193
  return waitKeepAlive(c, Runs.wait(run.run_id, undefined, c.var.auth));
191
194
  });
192
195
  api.post("/runs", zValidator("json", schemas.RunCreate), async (c) => {
@@ -196,6 +199,7 @@ api.post("/runs", zValidator("json", schemas.RunCreate), async (c) => {
196
199
  auth: c.var.auth,
197
200
  headers: c.req.raw.headers,
198
201
  });
202
+ c.header("Content-Location", `/runs/${run.run_id}`);
199
203
  return jsonExtra(c, run);
200
204
  });
201
205
  api.post("/runs/batch", zValidator("json", schemas.RunBatchCreate), async (c) => {
@@ -51,17 +51,7 @@ api.get("/threads/:thread_id/state", zValidator("param", z.object({ thread_id: z
51
51
  const state = stateSnapshotToThreadState(await Threads.State.get({ configurable: { thread_id } }, { subgraphs }, c.var.auth));
52
52
  return jsonExtra(c, state);
53
53
  });
54
- api.post("/threads/:thread_id/state", zValidator("param", z.object({ thread_id: z.string().uuid() })), zValidator("json", z.object({
55
- values: z
56
- .union([
57
- z.record(z.string(), z.unknown()),
58
- z.array(z.record(z.string(), z.unknown())),
59
- ])
60
- .nullish(),
61
- as_node: z.string().optional(),
62
- checkpoint_id: z.string().optional(),
63
- checkpoint: schemas.CheckpointSchema.nullish(),
64
- })), async (c) => {
54
+ api.post("/threads/:thread_id/state", zValidator("param", z.object({ thread_id: z.string().uuid() })), zValidator("json", schemas.ThreadStateUpdate), async (c) => {
65
55
  // Update Thread State
66
56
  const { thread_id } = c.req.valid("param");
67
57
  const payload = c.req.valid("json");
@@ -4,7 +4,10 @@ export declare function spawnServer(args: {
4
4
  nJobsPerWorker: string;
5
5
  }, context: {
6
6
  config: {
7
- graphs: Record<string, string>;
7
+ graphs: Record<string, string | {
8
+ path: string;
9
+ description?: string;
10
+ }>;
8
11
  ui?: Record<string, string>;
9
12
  ui_config?: {
10
13
  shared?: string[];
@@ -6,15 +6,31 @@ interface Thread {
6
6
  thread_id: string;
7
7
  metadata: Metadata;
8
8
  }
9
- interface ThreadSaver {
9
+ /**
10
+ * Interface for storing and retrieving threads used by `createEmbedServer`.
11
+ * @experimental Does not follow semver.
12
+ */
13
+ export interface ThreadSaver {
10
14
  get: (id: string) => Promise<Thread>;
11
- put: (id: string, options: {
15
+ set: (id: string, options: {
16
+ kind: "put" | "patch";
12
17
  metadata?: Metadata;
13
- }) => Promise<void>;
18
+ }) => Promise<Thread>;
14
19
  delete: (id: string) => Promise<void>;
20
+ search?: (options: {
21
+ metadata?: Metadata;
22
+ limit: number;
23
+ offset: number;
24
+ sortBy: "created_at" | "updated_at";
25
+ sortOrder: "asc" | "desc";
26
+ }) => AsyncGenerator<{
27
+ thread: Thread;
28
+ total: number;
29
+ }>;
15
30
  }
16
31
  /**
17
- * Attach LangGraph Platform-esque routes to a given Hono instance.
32
+ * Create a Hono server with a subset of LangGraph Platform routes.
33
+ *
18
34
  * @experimental Does not follow semver.
19
35
  */
20
36
  export declare function createEmbedServer(options: {
@@ -5,18 +5,20 @@ import { v4 as uuidv4 } from "uuid";
5
5
  import * as schemas from "../schemas.mjs";
6
6
  import { z } from "zod";
7
7
  import { streamState } from "../stream.mjs";
8
- import { serialiseAsDict } from "../utils/serde.mjs";
8
+ 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
12
  function createStubRun(threadId, payload) {
13
13
  const now = new Date();
14
14
  const runId = uuidv4();
15
- const streamMode = Array.isArray(payload.stream_mode)
15
+ let streamMode = Array.isArray(payload.stream_mode)
16
16
  ? payload.stream_mode
17
17
  : payload.stream_mode
18
18
  ? [payload.stream_mode]
19
19
  : undefined;
20
+ if (streamMode == null || streamMode.length === 0)
21
+ streamMode = ["values"];
20
22
  const config = Object.assign({}, payload.config ?? {}, {
21
23
  configurable: {
22
24
  run_id: runId,
@@ -44,6 +46,7 @@ function createStubRun(threadId, payload) {
44
46
  input: payload.input,
45
47
  command: payload.command,
46
48
  config,
49
+ context: payload.context,
47
50
  stream_mode: streamMode,
48
51
  interrupt_before: payload.interrupt_before,
49
52
  interrupt_after: payload.interrupt_after,
@@ -57,7 +60,8 @@ function createStubRun(threadId, payload) {
57
60
  };
58
61
  }
59
62
  /**
60
- * Attach LangGraph Platform-esque routes to a given Hono instance.
63
+ * Create a Hono server with a subset of LangGraph Platform routes.
64
+ *
61
65
  * @experimental Does not follow semver.
62
66
  */
63
67
  export function createEmbedServer(options) {
@@ -70,23 +74,58 @@ export function createEmbedServer(options) {
70
74
  const api = new Hono();
71
75
  api.use(ensureContentType());
72
76
  api.post("/threads", zValidator("json", schemas.ThreadCreate), async (c) => {
73
- // create a new threaad
77
+ // create a new thread
74
78
  const payload = c.req.valid("json");
75
79
  const threadId = payload.thread_id || uuidv4();
76
- await options.threads.put(threadId, payload);
77
- return jsonExtra(c, { thread_id: threadId });
80
+ return jsonExtra(c, await options.threads.set(threadId, {
81
+ kind: "put",
82
+ metadata: payload.metadata,
83
+ }));
78
84
  });
79
85
  api.get("/threads/:thread_id", zValidator("param", z.object({ thread_id: z.string().uuid() })), async (c) => {
80
86
  // Get Thread
81
87
  const { thread_id } = c.req.valid("param");
82
- const thread = await options.threads.get(thread_id);
83
- return jsonExtra(c, thread);
88
+ return jsonExtra(c, await options.threads.get(thread_id));
89
+ });
90
+ api.patch("/threads/:thread_id", zValidator("param", z.object({ thread_id: z.string().uuid() })), zValidator("json", schemas.ThreadCreate), async (c) => {
91
+ // Update Thread
92
+ const { thread_id } = c.req.valid("param");
93
+ const payload = c.req.valid("json");
94
+ return jsonExtra(c, await options.threads.set(thread_id, {
95
+ kind: "patch",
96
+ metadata: payload.metadata,
97
+ }));
84
98
  });
85
99
  api.delete("/threads/:thread_id", zValidator("param", z.object({ thread_id: z.string().uuid() })), async (c) => {
100
+ // Delete Thread
86
101
  const { thread_id } = c.req.valid("param");
87
102
  await options.threads.delete(thread_id);
88
103
  return new Response(null, { status: 204 });
89
104
  });
105
+ api.post("/threads/search", zValidator("json", schemas.ThreadSearchRequest), async (c) => {
106
+ const payload = c.req.valid("json");
107
+ const result = [];
108
+ if (!options.threads.search)
109
+ return c.json({ error: "Threads search not implemented" }, 422);
110
+ const sortBy = payload.sort_by === "created_at" || payload.sort_by === "updated_at"
111
+ ? payload.sort_by
112
+ : "created_at";
113
+ let total = 0;
114
+ for await (const item of options.threads.search({
115
+ metadata: payload.metadata,
116
+ limit: payload.limit ?? 10,
117
+ offset: payload.offset ?? 0,
118
+ sortBy,
119
+ sortOrder: payload.sort_order ?? "desc",
120
+ })) {
121
+ result.push(item.thread);
122
+ // Only set total if it's the first item
123
+ if (total === 0)
124
+ total = item.total;
125
+ }
126
+ c.res.headers.set("X-Pagination-Total", total.toString());
127
+ return jsonExtra(c, result);
128
+ });
90
129
  api.get("/threads/:thread_id/state", zValidator("param", z.object({ thread_id: z.string().uuid() })), zValidator("query", z.object({ subgraphs: schemas.coercedBoolean.optional() })), async (c) => {
91
130
  // Get Latest Thread State
92
131
  const { thread_id } = c.req.valid("param");
@@ -109,6 +148,57 @@ export function createEmbedServer(options) {
109
148
  const result = await graph.getState(config, { subgraphs });
110
149
  return jsonExtra(c, stateSnapshotToThreadState(result));
111
150
  });
151
+ api.post("/threads/:thread_id/state", zValidator("param", z.object({ thread_id: z.string().uuid() })), zValidator("json", schemas.ThreadStateUpdate), async (c) => {
152
+ // Update Thread State
153
+ const { thread_id } = c.req.valid("param");
154
+ const payload = c.req.valid("json");
155
+ const config = { configurable: { thread_id } };
156
+ config.configurable ??= {};
157
+ if (payload.checkpoint_id) {
158
+ config.configurable.checkpoint_id = payload.checkpoint_id;
159
+ }
160
+ if (payload.checkpoint) {
161
+ Object.assign(config.configurable, payload.checkpoint);
162
+ }
163
+ const thread = await options.threads.get(thread_id);
164
+ const graphId = thread.metadata?.graph_id;
165
+ const graph = graphId ? await getGraph(graphId) : undefined;
166
+ if (graph == null)
167
+ return c.json({ error: "Graph not found" }, 404);
168
+ const result = await graph.updateState(config, payload.values, payload.as_node);
169
+ return jsonExtra(c, { checkpoint: result.configurable });
170
+ });
171
+ // get thread state at checkpoint
172
+ api.get("/threads/:thread_id/state/:checkpoint_id", zValidator("param", z.object({
173
+ thread_id: z.string().uuid(),
174
+ checkpoint_id: z.string().uuid(),
175
+ })), zValidator("query", z.object({ subgraphs: schemas.coercedBoolean.optional() })), async (c) => {
176
+ // Get Thread State At Checkpoint
177
+ const { thread_id, checkpoint_id } = c.req.valid("param");
178
+ const { subgraphs } = c.req.valid("query");
179
+ const thread = await options.threads.get(thread_id);
180
+ const graphId = thread.metadata?.graph_id;
181
+ const graph = graphId ? await getGraph(graphId) : undefined;
182
+ if (graph == null)
183
+ return c.json({ error: "Graph not found" }, 404);
184
+ const result = await graph.getState({ configurable: { thread_id, checkpoint_id } }, { subgraphs });
185
+ return jsonExtra(c, stateSnapshotToThreadState(result));
186
+ });
187
+ api.post("/threads/:thread_id/state/checkpoint", zValidator("param", z.object({ thread_id: z.string().uuid() })), zValidator("json", z.object({
188
+ subgraphs: schemas.coercedBoolean.optional(),
189
+ checkpoint: schemas.CheckpointSchema.nullish(),
190
+ })), async (c) => {
191
+ // Get Thread State At Checkpoint post
192
+ const { thread_id } = c.req.valid("param");
193
+ const { checkpoint, subgraphs } = c.req.valid("json");
194
+ const thread = await options.threads.get(thread_id);
195
+ const graphId = thread.metadata?.graph_id;
196
+ const graph = graphId ? await getGraph(graphId) : undefined;
197
+ if (graph == null)
198
+ return c.json({ error: "Graph not found" }, 404);
199
+ const result = await graph.getState({ configurable: { thread_id, ...checkpoint } }, { subgraphs });
200
+ return jsonExtra(c, stateSnapshotToThreadState(result));
201
+ });
112
202
  api.post("/threads/:thread_id/history", zValidator("param", z.object({ thread_id: z.string().uuid() })), zValidator("json", schemas.ThreadHistoryRequest), async (c) => {
113
203
  // Get Thread History Post
114
204
  const { thread_id } = c.req.valid("param");
@@ -134,26 +224,35 @@ export function createEmbedServer(options) {
134
224
  });
135
225
  api.post("/threads/:thread_id/runs/stream", zValidator("param", z.object({ thread_id: z.string().uuid() })), zValidator("json", schemas.RunCreate), async (c) => {
136
226
  // Stream Run
227
+ const { thread_id } = c.req.valid("param");
228
+ const payload = c.req.valid("json");
229
+ const thread = await options.threads.get(thread_id);
230
+ if (thread == null)
231
+ return c.json({ error: "Thread not found" }, 404);
137
232
  return streamSSE(c, async (stream) => {
138
- const { thread_id } = c.req.valid("param");
139
- const payload = c.req.valid("json");
140
233
  const signal = getDisconnectAbortSignal(c, stream);
141
234
  const run = createStubRun(thread_id, payload);
142
- // update thread with new graph_id
143
- const thread = await options.threads.get(thread_id);
144
- await options.threads.put(thread_id, {
235
+ await options.threads.set(thread_id, {
236
+ kind: "patch",
145
237
  metadata: {
146
- ...thread.metadata,
147
238
  graph_id: payload.assistant_id,
148
239
  assistant_id: payload.assistant_id,
149
240
  },
150
241
  });
151
- for await (const { event, data } of streamState(run, {
152
- attempt: 1,
153
- getGraph,
154
- signal,
155
- })) {
156
- await stream.writeSSE({ data: serialiseAsDict(data), event });
242
+ try {
243
+ for await (const { event, data } of streamState(run, {
244
+ attempt: 1,
245
+ getGraph,
246
+ signal,
247
+ })) {
248
+ await stream.writeSSE({ data: serialiseAsDict(data), event });
249
+ }
250
+ }
251
+ catch (error) {
252
+ await stream.writeSSE({
253
+ data: serialiseAsDict(serializeError(error)),
254
+ event: "error",
255
+ });
157
256
  }
158
257
  });
159
258
  });
@@ -163,7 +262,8 @@ export function createEmbedServer(options) {
163
262
  const payload = c.req.valid("json");
164
263
  const signal = getDisconnectAbortSignal(c, stream);
165
264
  const threadId = uuidv4();
166
- await options.threads.put(threadId, {
265
+ await options.threads.set(threadId, {
266
+ kind: "put",
167
267
  metadata: {
168
268
  graph_id: payload.assistant_id,
169
269
  assistant_id: payload.assistant_id,
@@ -171,12 +271,20 @@ export function createEmbedServer(options) {
171
271
  });
172
272
  try {
173
273
  const run = createStubRun(threadId, payload);
174
- for await (const { event, data } of streamState(run, {
175
- attempt: 1,
176
- getGraph,
177
- signal,
178
- })) {
179
- await stream.writeSSE({ data: serialiseAsDict(data), event });
274
+ try {
275
+ for await (const { event, data } of streamState(run, {
276
+ attempt: 1,
277
+ getGraph,
278
+ signal,
279
+ })) {
280
+ await stream.writeSSE({ data: serialiseAsDict(data), event });
281
+ }
282
+ }
283
+ catch (error) {
284
+ await stream.writeSSE({
285
+ data: serialiseAsDict(serializeError(error)),
286
+ event: "error",
287
+ });
180
288
  }
181
289
  }
182
290
  finally {
@@ -184,5 +292,8 @@ export function createEmbedServer(options) {
184
292
  }
185
293
  });
186
294
  });
295
+ api.notFound((c) => {
296
+ return c.json({ error: `${c.req.method} ${c.req.path} not implemented` }, 404);
297
+ });
187
298
  return api;
188
299
  }
@@ -8,9 +8,9 @@ export declare const NAMESPACE_GRAPH: Uint8Array<ArrayBufferLike>;
8
8
  export declare const getAssistantId: (graphId: string) => string;
9
9
  export declare function registerFromEnv(specs: Record<string, string>, options: {
10
10
  cwd: string;
11
- }): Promise<(CompiledGraph<string, any, any, Record<string, any>, any, any> | CompiledGraphFactory<string>)[]>;
11
+ }): Promise<(CompiledGraph<string, any, any, Record<string, any>, any, any, unknown> | CompiledGraphFactory<string>)[]>;
12
12
  export declare function getGraph(graphId: string, config: LangGraphRunnableConfig | undefined, options?: {
13
13
  checkpointer?: BaseCheckpointSaver | null;
14
14
  store?: BaseStore;
15
- }): Promise<CompiledGraph<string, any, any, Record<string, any>, any, any>>;
15
+ }): Promise<CompiledGraph<string, any, any, Record<string, any>, any, any, unknown>>;
16
16
  export declare function getCachedStaticGraphSchema(graphId: string): Promise<Record<string, GraphSchema>>;
package/dist/preload.mjs CHANGED
@@ -8,9 +8,18 @@ const options = JSON.parse(lastArg || "{}");
8
8
  // find the first file, as `parentURL` needs to be a valid file URL
9
9
  // if no graph found, just assume a dummy default file, which should
10
10
  // be working fine as well.
11
- const firstGraphFile = Object.values(options.graphs)
12
- .flatMap((i) => i.split(":").at(0))
13
- .at(0) || "index.mts";
11
+ const graphFiles = Object.values(options.graphs)
12
+ .map((i) => {
13
+ if (typeof i === "string") {
14
+ return i.split(":").at(0);
15
+ }
16
+ else if (i && typeof i === "object" && i.path) {
17
+ return i.path.split(":").at(0);
18
+ }
19
+ return null;
20
+ })
21
+ .filter(Boolean);
22
+ const firstGraphFile = graphFiles.at(0) || "index.mts";
14
23
  // enforce API @langchain/langgraph resolution
15
24
  register("./graph/load.hooks.mjs", import.meta.url, {
16
25
  parentURL: "data:",
@@ -1343,16 +1343,39 @@ export declare const ThreadStateSearch: z.ZodObject<{
1343
1343
  before?: string | undefined;
1344
1344
  }>;
1345
1345
  export declare const ThreadStateUpdate: z.ZodObject<{
1346
- values: z.ZodOptional<z.ZodUnion<[z.ZodArray<z.ZodObject<{}, "strip", z.ZodAny, z.objectOutputType<{}, z.ZodAny, "strip">, z.objectInputType<{}, z.ZodAny, "strip">>, "many">, z.ZodObject<{}, "strip", z.ZodAny, z.objectOutputType<{}, z.ZodAny, "strip">, z.objectInputType<{}, z.ZodAny, "strip">>, z.ZodNull]>>;
1347
- checkpoint_id: z.ZodOptional<z.ZodString>;
1346
+ values: z.ZodOptional<z.ZodNullable<z.ZodUnion<[z.ZodRecord<z.ZodString, z.ZodUnknown>, z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>, "many">]>>>;
1348
1347
  as_node: z.ZodOptional<z.ZodString>;
1348
+ checkpoint_id: z.ZodOptional<z.ZodString>;
1349
+ checkpoint: z.ZodOptional<z.ZodNullable<z.ZodObject<{
1350
+ checkpoint_id: z.ZodOptional<z.ZodString>;
1351
+ checkpoint_ns: z.ZodOptional<z.ZodNullable<z.ZodString>>;
1352
+ checkpoint_map: z.ZodOptional<z.ZodNullable<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
1353
+ }, "strip", z.ZodTypeAny, {
1354
+ checkpoint_ns?: string | null | undefined;
1355
+ checkpoint_id?: string | undefined;
1356
+ checkpoint_map?: Record<string, unknown> | null | undefined;
1357
+ }, {
1358
+ checkpoint_ns?: string | null | undefined;
1359
+ checkpoint_id?: string | undefined;
1360
+ checkpoint_map?: Record<string, unknown> | null | undefined;
1361
+ }>>>;
1349
1362
  }, "strip", z.ZodTypeAny, {
1350
- values?: z.objectOutputType<{}, z.ZodAny, "strip">[] | z.objectOutputType<{}, z.ZodAny, "strip"> | null | undefined;
1363
+ values?: Record<string, unknown> | Record<string, unknown>[] | null | undefined;
1351
1364
  checkpoint_id?: string | undefined;
1365
+ checkpoint?: {
1366
+ checkpoint_ns?: string | null | undefined;
1367
+ checkpoint_id?: string | undefined;
1368
+ checkpoint_map?: Record<string, unknown> | null | undefined;
1369
+ } | null | undefined;
1352
1370
  as_node?: string | undefined;
1353
1371
  }, {
1354
- values?: z.objectInputType<{}, z.ZodAny, "strip">[] | z.objectInputType<{}, z.ZodAny, "strip"> | null | undefined;
1372
+ values?: Record<string, unknown> | Record<string, unknown>[] | null | undefined;
1355
1373
  checkpoint_id?: string | undefined;
1374
+ checkpoint?: {
1375
+ checkpoint_ns?: string | null | undefined;
1376
+ checkpoint_id?: string | undefined;
1377
+ checkpoint_map?: Record<string, unknown> | null | undefined;
1378
+ } | null | undefined;
1356
1379
  as_node?: string | undefined;
1357
1380
  }>;
1358
1381
  export declare const ThreadHistoryRequest: z.ZodObject<{
package/dist/schemas.mjs CHANGED
@@ -387,13 +387,13 @@ export const ThreadStateUpdate = z
387
387
  .object({
388
388
  values: z
389
389
  .union([
390
- z.array(z.object({}).catchall(z.any())),
391
- z.object({}).catchall(z.any()),
392
- z.null(),
390
+ z.record(z.string(), z.unknown()),
391
+ z.array(z.record(z.string(), z.unknown())),
393
392
  ])
394
- .optional(),
395
- checkpoint_id: z.string().optional(),
393
+ .nullish(),
396
394
  as_node: z.string().optional(),
395
+ checkpoint_id: z.string().optional(),
396
+ checkpoint: CheckpointSchema.nullish(),
397
397
  })
398
398
  .describe("Payload for adding state to a thread.");
399
399
  export const ThreadHistoryRequest = z.object({
package/dist/server.d.mts CHANGED
@@ -4,7 +4,16 @@ export declare const StartServerSchema: z.ZodObject<{
4
4
  nWorkers: z.ZodNumber;
5
5
  host: z.ZodString;
6
6
  cwd: z.ZodString;
7
- graphs: z.ZodRecord<z.ZodString, z.ZodString>;
7
+ graphs: z.ZodRecord<z.ZodString, z.ZodUnion<[z.ZodString, z.ZodObject<{
8
+ path: z.ZodString;
9
+ description: z.ZodOptional<z.ZodString>;
10
+ }, "strip", z.ZodTypeAny, {
11
+ path: string;
12
+ description?: string | undefined;
13
+ }, {
14
+ path: string;
15
+ description?: string | undefined;
16
+ }>]>>;
8
17
  auth: z.ZodOptional<z.ZodObject<{
9
18
  path: z.ZodOptional<z.ZodString>;
10
19
  disable_studio_auth: z.ZodDefault<z.ZodBoolean>;
@@ -93,7 +102,10 @@ export declare const StartServerSchema: z.ZodObject<{
93
102
  host: string;
94
103
  port: number;
95
104
  nWorkers: number;
96
- graphs: Record<string, string>;
105
+ graphs: Record<string, string | {
106
+ path: string;
107
+ description?: string | undefined;
108
+ }>;
97
109
  auth?: {
98
110
  disable_studio_auth: boolean;
99
111
  path?: string | undefined;
@@ -124,7 +136,10 @@ export declare const StartServerSchema: z.ZodObject<{
124
136
  host: string;
125
137
  port: number;
126
138
  nWorkers: number;
127
- graphs: Record<string, string>;
139
+ graphs: Record<string, string | {
140
+ path: string;
141
+ description?: string | undefined;
142
+ }>;
128
143
  auth?: {
129
144
  path?: string | undefined;
130
145
  disable_studio_auth?: boolean | undefined;
package/dist/server.mjs CHANGED
@@ -25,7 +25,10 @@ export const StartServerSchema = z.object({
25
25
  nWorkers: z.number(),
26
26
  host: z.string(),
27
27
  cwd: z.string(),
28
- graphs: z.record(z.string()),
28
+ graphs: z.record(z.union([
29
+ z.string(),
30
+ z.object({ path: z.string(), description: z.string().optional() }),
31
+ ])),
29
32
  auth: z
30
33
  .object({
31
34
  path: z.string().optional(),
@@ -79,7 +82,20 @@ export async function startServer(options) {
79
82
  // We need to do this before we load the graphs in-case the logger is obtained at top-level.
80
83
  registerSdkLogger();
81
84
  logger.info(`Registering graphs from ${options.cwd}`);
82
- await registerFromEnv(options.graphs, { cwd: options.cwd });
85
+ let hasGraphDescriptions = false;
86
+ const graphPaths = Object.fromEntries(Object.entries(options.graphs).map(([graphId, rawSpec]) => {
87
+ if (typeof rawSpec === "string") {
88
+ return [graphId, rawSpec];
89
+ }
90
+ if (rawSpec.description) {
91
+ hasGraphDescriptions = true;
92
+ }
93
+ return [graphId, rawSpec.path];
94
+ }));
95
+ if (hasGraphDescriptions) {
96
+ logger.warn("A graph definition in `langgraph.json` has a `description` property. Local MCP features are not yet supported with the JS CLI and will be ignored.");
97
+ }
98
+ await registerFromEnv(graphPaths, { cwd: options.cwd });
83
99
  registerRuntimeLogFormatter((info) => {
84
100
  const config = getConfig();
85
101
  if (config == null)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@langchain/langgraph-api",
3
- "version": "0.0.56",
3
+ "version": "0.0.58",
4
4
  "type": "module",
5
5
  "engines": {
6
6
  "node": "^18.19.0 || >=20.16.0"
@@ -52,7 +52,7 @@
52
52
  "@babel/code-frame": "^7.26.2",
53
53
  "@hono/node-server": "^1.12.0",
54
54
  "@hono/zod-validator": "^0.2.2",
55
- "@langchain/langgraph-ui": "0.0.56",
55
+ "@langchain/langgraph-ui": "0.0.58",
56
56
  "@types/json-schema": "^7.0.15",
57
57
  "@typescript/vfs": "^1.6.0",
58
58
  "dedent": "^1.5.3",
@@ -84,9 +84,9 @@
84
84
  },
85
85
  "devDependencies": {
86
86
  "@langchain/core": "^0.3.59",
87
- "@langchain/langgraph": "0.4.2",
87
+ "@langchain/langgraph": "0.4.4",
88
88
  "@langchain/langgraph-checkpoint": "0.1.0",
89
- "@langchain/langgraph-sdk": "0.0.105",
89
+ "@langchain/langgraph-sdk": "0.0.107",
90
90
  "@types/babel__code-frame": "^7.0.6",
91
91
  "@types/node": "^18.15.11",
92
92
  "@types/react": "^19.0.8",