@langchain/langgraph-api 0.0.17 → 0.0.19

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.
@@ -76,9 +76,10 @@ api.get("/assistants/:assistant_id/graph", zValidator("query", z.object({ xray:
76
76
  const assistantId = getAssistantId(c.req.param("assistant_id"));
77
77
  const assistant = await Assistants.get(assistantId);
78
78
  const { xray } = c.req.valid("query");
79
- const graph = getGraph(assistant.graph_id);
79
+ const config = getRunnableConfig(assistant.config);
80
+ const graph = await getGraph(assistant.graph_id, config);
80
81
  const drawable = await graph.getGraphAsync({
81
- ...getRunnableConfig(assistant.config),
82
+ ...config,
82
83
  xray: xray ?? undefined,
83
84
  });
84
85
  return c.json(drawable.toJSON());
@@ -106,7 +107,8 @@ api.get("/assistants/:assistant_id/subgraphs/:namespace?", zValidator("param", z
106
107
  const { recurse } = c.req.valid("query");
107
108
  const assistantId = getAssistantId(assistant_id);
108
109
  const assistant = await Assistants.get(assistantId);
109
- const graph = getGraph(assistant.graph_id);
110
+ const config = getRunnableConfig(assistant.config);
111
+ const graph = await getGraph(assistant.graph_id, config);
110
112
  const graphSchema = await getGraphSchema(assistant.graph_id);
111
113
  const rootGraphId = Object.keys(graphSchema).find((i) => !i.includes("|"));
112
114
  if (!rootGraphId) {
@@ -15,6 +15,9 @@ api.post("/threads", zValidator("json", schemas.ThreadCreate), async (c) => {
15
15
  metadata: payload.metadata,
16
16
  if_exists: payload.if_exists ?? "raise",
17
17
  });
18
+ if (payload.supersteps?.length) {
19
+ await Threads.State.bulk({ configurable: { thread_id: thread.thread_id } }, payload.supersteps);
20
+ }
18
21
  return jsonExtra(c, thread);
19
22
  });
20
23
  api.post("/threads/search", zValidator("json", schemas.ThreadSearchRequest), async (c) => {
@@ -37,5 +37,5 @@ logger.info(`Server running at ${host}`);
37
37
  let queryParams = `?baseUrl=http://${options.host}:${options.port}`;
38
38
  if (organizationId)
39
39
  queryParams += `&organizationId=${organizationId}`;
40
- asyncExitHook(cleanup, { wait: 1000 });
40
+ asyncExitHook(cleanup, { wait: 3_000 });
41
41
  sendToParent?.({ queryParams });
@@ -6,6 +6,9 @@ export declare function spawnServer(args: {
6
6
  config: {
7
7
  graphs: Record<string, string>;
8
8
  ui?: Record<string, string>;
9
+ ui_config?: {
10
+ shared?: string[];
11
+ };
9
12
  };
10
13
  env: NodeJS.ProcessEnv;
11
14
  hostUrl: string;
@@ -31,6 +31,7 @@ For production use, please use LangGraph Cloud.
31
31
  host: args.host,
32
32
  graphs: context.config.graphs,
33
33
  ui: context.config.ui,
34
+ ui_config: context.config.ui_config,
34
35
  cwd: options.projectCwd,
35
36
  }),
36
37
  ], {
@@ -0,0 +1,15 @@
1
+ import { Command, Send } from "@langchain/langgraph";
2
+ export const getLangGraphCommand = (command) => {
3
+ let goto = command.goto != null && !Array.isArray(command.goto)
4
+ ? [command.goto]
5
+ : command.goto;
6
+ return new Command({
7
+ goto: goto?.map((item) => {
8
+ if (typeof item !== "string")
9
+ return new Send(item.node, item.input);
10
+ return item;
11
+ }),
12
+ update: command.update,
13
+ resume: command.resume,
14
+ });
15
+ };
@@ -41,11 +41,12 @@ export async function registerFromEnv(specs, options) {
41
41
  return resolved;
42
42
  }));
43
43
  }
44
- export function getGraph(graphId, options) {
44
+ export async function getGraph(graphId, config, options) {
45
45
  if (!GRAPHS[graphId])
46
46
  throw new HTTPException(404, { message: `Graph "${graphId}" not found` });
47
- // TODO: have a check for the type of graph
48
- const compiled = GRAPHS[graphId];
47
+ const compiled = typeof GRAPHS[graphId] === "function"
48
+ ? await GRAPHS[graphId](config ?? { configurable: {} })
49
+ : GRAPHS[graphId];
49
50
  if (typeof options?.checkpointer !== "undefined") {
50
51
  compiled.checkpointer = options?.checkpointer ?? undefined;
51
52
  }
@@ -23,10 +23,17 @@ export async function resolveGraph(spec, options) {
23
23
  const resolved = await (async () => {
24
24
  if (!graph)
25
25
  throw new Error("Failed to load graph: graph is nullush");
26
- const graphLike = typeof graph === "function" ? await graph() : await graph;
27
- if (isGraph(graphLike))
28
- return graphLike.compile();
29
- return graphLike;
26
+ const afterResolve = (graphLike) => {
27
+ const graph = isGraph(graphLike) ? graphLike.compile() : graphLike;
28
+ return graph;
29
+ };
30
+ if (typeof graph === "function") {
31
+ return async (config) => {
32
+ const graphLike = await graph(config);
33
+ return afterResolve(graphLike);
34
+ };
35
+ }
36
+ return afterResolve(await graph);
30
37
  })();
31
38
  return { sourceFile, exportSymbol, resolved };
32
39
  }
package/dist/schemas.mjs CHANGED
@@ -140,35 +140,31 @@ export const Run = z.object({
140
140
  kwargs: z.object({}).catchall(z.any()),
141
141
  multitask_strategy: z.enum(["reject", "rollback", "interrupt", "enqueue"]),
142
142
  });
143
+ export const CommandSchema = z.object({
144
+ goto: z
145
+ .union([
146
+ z.union([
147
+ z.string(),
148
+ z.object({ node: z.string(), input: z.unknown().optional() }),
149
+ ]),
150
+ z.array(z.union([
151
+ z.string(),
152
+ z.object({ node: z.string(), input: z.unknown().optional() }),
153
+ ])),
154
+ ])
155
+ .optional(),
156
+ update: z
157
+ .union([z.record(z.unknown()), z.array(z.tuple([z.string(), z.unknown()]))])
158
+ .optional(),
159
+ resume: z.unknown().optional(),
160
+ });
143
161
  export const RunCreate = z
144
162
  .object({
145
163
  assistant_id: z.union([z.string().uuid(), z.string()]),
146
164
  checkpoint_id: z.string().optional(),
147
165
  checkpoint: CheckpointSchema.optional(),
148
166
  input: z.union([z.unknown(), z.null()]).optional(),
149
- command: z
150
- .object({
151
- goto: z
152
- .union([
153
- z.union([
154
- z.string(),
155
- z.object({ node: z.string(), input: z.unknown().optional() }),
156
- ]),
157
- z.array(z.union([
158
- z.string(),
159
- z.object({ node: z.string(), input: z.unknown().optional() }),
160
- ])),
161
- ])
162
- .optional(),
163
- update: z
164
- .union([
165
- z.record(z.unknown()),
166
- z.array(z.tuple([z.string(), z.unknown()])),
167
- ])
168
- .optional(),
169
- resume: z.unknown().optional(),
170
- })
171
- .optional(),
167
+ command: CommandSchema.optional(),
172
168
  metadata: z
173
169
  .object({})
174
170
  .catchall(z.any())
@@ -301,6 +297,16 @@ export const Thread = z.object({
301
297
  });
302
298
  export const ThreadCreate = z
303
299
  .object({
300
+ supersteps: z
301
+ .array(z.object({
302
+ updates: z.array(z.object({
303
+ values: z.unknown().nullish(),
304
+ command: CommandSchema.nullish(),
305
+ as_node: z.string(),
306
+ })),
307
+ }))
308
+ .describe("The supersteps to apply to the thread.")
309
+ .optional(),
304
310
  thread_id: z
305
311
  .string()
306
312
  .uuid()
package/dist/server.mjs CHANGED
@@ -50,6 +50,7 @@ export const StartServerSchema = z.object({
50
50
  cwd: z.string(),
51
51
  graphs: z.record(z.string()),
52
52
  ui: z.record(z.string()).optional(),
53
+ ui_config: z.object({ shared: z.array(z.string()).optional() }).optional(),
53
54
  });
54
55
  export async function startServer(options) {
55
56
  logger.info(`Initializing storage...`);
@@ -69,7 +70,10 @@ export async function startServer(options) {
69
70
  const { api, registerGraphUi } = await import("./ui/load.mjs");
70
71
  app.route("/", api);
71
72
  logger.info(`Registering UI from ${options.cwd}`);
72
- await registerGraphUi(options.ui, { cwd: options.cwd });
73
+ await registerGraphUi(options.ui, {
74
+ cwd: options.cwd,
75
+ config: options.ui_config,
76
+ });
73
77
  }
74
78
  logger.info(`Starting ${options.nWorkers} workers`);
75
79
  for (let i = 0; i < options.nWorkers; i++)
@@ -6,6 +6,7 @@ import { store } from "./store.mjs";
6
6
  import { logger } from "../logging.mjs";
7
7
  import { serializeError } from "../utils/serde.mjs";
8
8
  import { FileSystemPersistence } from "./persist.mjs";
9
+ import { getLangGraphCommand } from "../command.mjs";
9
10
  export const conn = new FileSystemPersistence(".langgraphjs_ops.json", () => ({
10
11
  runs: {},
11
12
  threads: {},
@@ -446,7 +447,10 @@ export class Threads {
446
447
  tasks: [],
447
448
  };
448
449
  }
449
- const graph = await getGraph(graphId, { checkpointer, store });
450
+ const graph = await getGraph(graphId, thread.config, {
451
+ checkpointer,
452
+ store,
453
+ });
450
454
  const result = await graph.getState(config, { subgraphs });
451
455
  if (result.metadata != null &&
452
456
  "checkpoint_ns" in result.metadata &&
@@ -470,7 +474,10 @@ export class Threads {
470
474
  }
471
475
  config.configurable ??= {};
472
476
  config.configurable.graph_id ??= graphId;
473
- const graph = await getGraph(graphId, { checkpointer, store });
477
+ const graph = await getGraph(graphId, thread.config, {
478
+ checkpointer,
479
+ store,
480
+ });
474
481
  const updateConfig = structuredClone(config);
475
482
  updateConfig.configurable ??= {};
476
483
  updateConfig.configurable.checkpoint_ns ??= "";
@@ -487,6 +494,44 @@ export class Threads {
487
494
  });
488
495
  return { checkpoint: nextConfig.configurable };
489
496
  }
497
+ static async bulk(config, supersteps) {
498
+ const threadId = config.configurable?.thread_id;
499
+ if (!threadId)
500
+ return [];
501
+ const thread = await Threads.get(threadId);
502
+ const graphId = thread.metadata?.graph_id;
503
+ if (graphId == null) {
504
+ throw new HTTPException(400, {
505
+ message: `Thread ${threadId} has no graph ID`,
506
+ });
507
+ }
508
+ config.configurable ??= {};
509
+ config.configurable.graph_id ??= graphId;
510
+ const graph = await getGraph(graphId, thread.config, {
511
+ checkpointer,
512
+ store,
513
+ });
514
+ const updateConfig = structuredClone(config);
515
+ updateConfig.configurable ??= {};
516
+ updateConfig.configurable.checkpoint_ns ??= "";
517
+ const nextConfig = await graph.bulkUpdateState(updateConfig, supersteps.map((i) => ({
518
+ updates: i.updates.map((j) => ({
519
+ values: j.command != null ? getLangGraphCommand(j.command) : j.values,
520
+ asNode: j.as_node,
521
+ })),
522
+ })));
523
+ const state = await Threads.State.get(config, { subgraphs: false });
524
+ // update thread values
525
+ await conn.with(async (STORE) => {
526
+ for (const thread of Object.values(STORE.threads)) {
527
+ if (thread.thread_id === threadId) {
528
+ thread.values = state.values;
529
+ break;
530
+ }
531
+ }
532
+ });
533
+ return { checkpoint: nextConfig.configurable };
534
+ }
490
535
  static async list(config, options) {
491
536
  const threadId = config.configurable?.thread_id;
492
537
  if (!threadId)
@@ -495,7 +540,10 @@ export class Threads {
495
540
  const graphId = thread.metadata?.graph_id;
496
541
  if (graphId == null)
497
542
  return [];
498
- const graph = await getGraph(graphId, { checkpointer, store });
543
+ const graph = await getGraph(graphId, thread.config, {
544
+ checkpointer,
545
+ store,
546
+ });
499
547
  const before = typeof options?.before === "string"
500
548
  ? { configurable: { checkpoint_id: options.before } }
501
549
  : options?.before;
@@ -513,7 +561,7 @@ export class Threads {
513
561
  }
514
562
  export class Runs {
515
563
  static async *next() {
516
- yield* conn.withGenerator(async function* (STORE) {
564
+ yield* conn.withGenerator(async function* (STORE, options) {
517
565
  const now = new Date();
518
566
  const pendingRuns = Object.values(STORE.runs)
519
567
  .filter((run) => run.status === "pending" && run.created_at < now)
@@ -533,6 +581,7 @@ export class Runs {
533
581
  continue;
534
582
  try {
535
583
  const signal = StreamManager.lock(runId);
584
+ options.schedulePersist();
536
585
  STORE.retry_counter[runId] ??= 0;
537
586
  STORE.retry_counter[runId] += 1;
538
587
  yield { run, attempt: STORE.retry_counter[runId], signal };
@@ -67,12 +67,15 @@ export class FileSystemPersistence {
67
67
  if (this.filepath == null || this.data == null) {
68
68
  throw new Error(`${this.name} not initialized`);
69
69
  }
70
+ let shouldPersist = false;
71
+ let schedulePersist = () => void (shouldPersist = true);
70
72
  try {
71
- const gen = typeof fn === "function" ? fn(this.data) : fn;
73
+ const gen = typeof fn === "function" ? fn(this.data, { schedulePersist }) : fn;
72
74
  yield* gen;
73
75
  }
74
76
  finally {
75
- this.schedulePersist();
77
+ if (shouldPersist)
78
+ this.schedulePersist();
76
79
  }
77
80
  }
78
81
  }
package/dist/stream.mjs CHANGED
@@ -1,22 +1,8 @@
1
1
  import { getGraph } from "./graph/load.mjs";
2
2
  import { Client as LangSmithClient } from "langsmith";
3
- import { Command, Send, } from "@langchain/langgraph";
4
3
  import { runnableConfigToCheckpoint, taskRunnableConfigToCheckpoint, } from "./utils/runnableConfig.mjs";
5
4
  import { isBaseMessage } from "@langchain/core/messages";
6
- const getLangGraphCommand = (command) => {
7
- let goto = command.goto != null && !Array.isArray(command.goto)
8
- ? [command.goto]
9
- : command.goto;
10
- return new Command({
11
- goto: goto?.map((item) => {
12
- if (typeof item !== "string")
13
- return new Send(item.node, item.input);
14
- return item;
15
- }),
16
- update: command.update,
17
- resume: command.resume,
18
- });
19
- };
5
+ import { getLangGraphCommand } from "./command.mjs";
20
6
  const isRunnableConfig = (config) => {
21
7
  if (typeof config !== "object" || config == null)
22
8
  return false;
@@ -71,7 +57,7 @@ export async function* streamState(run, attempt = 1, options) {
71
57
  if (!graphId || typeof graphId !== "string") {
72
58
  throw new Error("Invalid or missing graph_id");
73
59
  }
74
- const graph = getGraph(graphId, {
60
+ const graph = await getGraph(graphId, kwargs.config, {
75
61
  checkpointer: kwargs.temporary ? null : undefined,
76
62
  });
77
63
  const userStreamMode = kwargs.stream_mode ?? [];
@@ -2,6 +2,9 @@ import { type BuildOptions } from "esbuild";
2
2
  export declare function build(agentName: string, args: {
3
3
  cwd: string;
4
4
  userPath: string;
5
+ config?: {
6
+ shared?: string[];
7
+ };
5
8
  }): Promise<{
6
9
  basename: string;
7
10
  contents: Uint8Array;
@@ -9,6 +12,9 @@ export declare function build(agentName: string, args: {
9
12
  export declare function watch(agentName: string, args: {
10
13
  cwd: string;
11
14
  userPath: string;
15
+ config?: {
16
+ shared?: string[];
17
+ };
12
18
  }, onResult: (result: {
13
19
  basename: string;
14
20
  contents: Uint8Array;
@@ -68,6 +68,7 @@ function setup(agentName, args, onResult) {
68
68
  "react-dom",
69
69
  "@langchain/langgraph-sdk",
70
70
  "@langchain/langgraph-sdk/react-ui",
71
+ ...(args.config?.shared ?? []),
71
72
  ],
72
73
  plugins: [tailwind(), entrypointPlugin(args), registerPlugin(onResult)],
73
74
  globalName: `__LGUI_${agentName}`,
package/dist/ui/load.mjs CHANGED
@@ -7,7 +7,7 @@ import { watch } from "./bundler.mjs";
7
7
  const GRAPH_UI = {};
8
8
  export async function registerGraphUi(defs, options) {
9
9
  const result = await Promise.all(Object.entries(defs).map(async ([agentName, userPath]) => {
10
- const ctx = await watch(agentName, { cwd: options.cwd, userPath }, (result) => {
10
+ const ctx = await watch(agentName, { cwd: options.cwd, userPath, config: options.config }, (result) => {
11
11
  GRAPH_UI[agentName] = result;
12
12
  });
13
13
  return [agentName, ctx];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@langchain/langgraph-api",
3
- "version": "0.0.17",
3
+ "version": "0.0.19",
4
4
  "type": "module",
5
5
  "engines": {
6
6
  "node": "^18.19.0 || >=20.16.0"
@@ -47,13 +47,13 @@
47
47
  "zod": "^3.23.8"
48
48
  },
49
49
  "peerDependencies": {
50
- "@langchain/core": "^0.3.40",
51
- "@langchain/langgraph": "^0.2.49",
52
- "@langchain/langgraph-checkpoint": "^0.0.15",
50
+ "@langchain/core": "^0.3.42",
51
+ "@langchain/langgraph": "^0.2.57",
52
+ "@langchain/langgraph-checkpoint": "^0.0.16",
53
53
  "typescript": "^5.5.4"
54
54
  },
55
55
  "devDependencies": {
56
- "@langchain/langgraph-sdk": "^0.0.33",
56
+ "@langchain/langgraph-sdk": "^0.0.60",
57
57
  "@types/babel__code-frame": "^7.0.6",
58
58
  "@types/react": "^19.0.8",
59
59
  "@types/react-dom": "^19.0.3",