@langchain/langgraph-api 0.0.40 → 0.0.42

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/dist/api/meta.mjs CHANGED
@@ -1,9 +1,10 @@
1
1
  import { Hono } from "hono";
2
2
  import * as fs from "node:fs/promises";
3
+ import * as path from "node:path";
3
4
  import * as url from "node:url";
4
5
  const api = new Hono();
5
6
  // Get the version using the same pattern as semver/index.mts
6
- const packageJsonPath = url.fileURLToPath(new URL("../../package.json", import.meta.url));
7
+ const packageJsonPath = path.resolve(url.fileURLToPath(import.meta.url), "../../../package.json");
7
8
  let version;
8
9
  try {
9
10
  const packageJson = JSON.parse(await fs.readFile(packageJsonPath, "utf-8"));
package/dist/api/runs.mjs CHANGED
@@ -13,7 +13,7 @@ import { serialiseAsDict } from "../utils/serde.mjs";
13
13
  const api = new Hono();
14
14
  const createValidRun = async (threadId, payload, kwargs) => {
15
15
  const { assistant_id: assistantId, ...run } = payload;
16
- const { auth, headers } = kwargs;
16
+ const { auth, headers } = kwargs ?? {};
17
17
  const runId = uuid4();
18
18
  const streamMode = Array.isArray(payload.stream_mode)
19
19
  ? payload.stream_mode
@@ -111,18 +111,7 @@ api.get("/threads/:thread_id/history", zValidator("param", z.object({ thread_id:
111
111
  const states = await Threads.State.list({ configurable: { thread_id, checkpoint_ns: "" } }, { limit, before }, c.var.auth);
112
112
  return jsonExtra(c, states.map(stateSnapshotToThreadState));
113
113
  });
114
- api.post("/threads/:thread_id/history", zValidator("param", z.object({ thread_id: z.string().uuid() })), zValidator("json", z.object({
115
- limit: z.number().optional().default(10),
116
- before: z.string().optional(),
117
- metadata: z.record(z.string(), z.unknown()).optional(),
118
- checkpoint: z
119
- .object({
120
- checkpoint_id: z.string().uuid().optional(),
121
- checkpoint_ns: z.string().optional(),
122
- checkpoint_map: z.record(z.string(), z.unknown()).optional(),
123
- })
124
- .optional(),
125
- })), async (c) => {
114
+ api.post("/threads/:thread_id/history", zValidator("param", z.object({ thread_id: z.string().uuid() })), zValidator("json", schemas.ThreadHistoryRequest), async (c) => {
126
115
  // Get Thread History Post
127
116
  const { thread_id } = c.req.valid("param");
128
117
  const { limit, before, metadata, checkpoint } = c.req.valid("json");
@@ -140,7 +129,7 @@ api.delete("/threads/:thread_id", zValidator("param", z.object({ thread_id: z.st
140
129
  await Threads.delete(thread_id, c.var.auth);
141
130
  return new Response(null, { status: 204 });
142
131
  });
143
- api.patch("/threads/:thread_id", zValidator("param", z.object({ thread_id: z.string().uuid() })), zValidator("json", z.object({ metadata: z.record(z.string(), z.unknown()) })), async (c) => {
132
+ api.patch("/threads/:thread_id", zValidator("param", z.object({ thread_id: z.string().uuid() })), zValidator("json", schemas.ThreadPatchRequest), async (c) => {
144
133
  // Patch Thread
145
134
  const { thread_id } = c.req.valid("param");
146
135
  const { metadata } = c.req.valid("json");
@@ -8,4 +8,4 @@ export interface RunCommand {
8
8
  update?: Record<string, unknown> | [string, unknown][];
9
9
  resume?: unknown;
10
10
  }
11
- export declare const getLangGraphCommand: (command: RunCommand) => Command<unknown>;
11
+ export declare const getLangGraphCommand: (command: RunCommand) => Command<unknown, Record<string, unknown>, string>;
@@ -0,0 +1,26 @@
1
+ import type { BaseCheckpointSaver, BaseStore, Pregel } from "@langchain/langgraph";
2
+ import { Hono } from "hono";
3
+ import type { Metadata } from "../storage/ops.mjs";
4
+ type AnyPregel = Pregel<any, any, any, any, any>;
5
+ interface Thread {
6
+ thread_id: string;
7
+ metadata: Metadata;
8
+ }
9
+ interface ThreadSaver {
10
+ get: (id: string) => Promise<Thread>;
11
+ put: (id: string, options: {
12
+ metadata?: Metadata;
13
+ }) => Promise<void>;
14
+ delete: (id: string) => Promise<void>;
15
+ }
16
+ /**
17
+ * Attach LangGraph Platform-esque routes to a given Hono instance.
18
+ * @experimental Does not follow semver.
19
+ */
20
+ export declare function createEmbedServer(options: {
21
+ graph: Record<string, AnyPregel>;
22
+ threads: ThreadSaver;
23
+ checkpointer: BaseCheckpointSaver;
24
+ store?: BaseStore;
25
+ }): Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
26
+ export {};
@@ -0,0 +1,167 @@
1
+ import { Hono } from "hono";
2
+ import { zValidator } from "@hono/zod-validator";
3
+ import { streamSSE } from "hono/streaming";
4
+ import { v4 as uuidv4 } from "uuid";
5
+ import * as schemas from "../schemas.mjs";
6
+ import { z } from "zod";
7
+ import { streamState } from "../stream.mjs";
8
+ import { serialiseAsDict } from "../utils/serde.mjs";
9
+ import { getDisconnectAbortSignal, jsonExtra } from "../utils/hono.mjs";
10
+ import { stateSnapshotToThreadState } from "../state.mjs";
11
+ import { ensureContentType } from "../http/middleware.mjs";
12
+ function createStubRun(threadId, payload) {
13
+ const now = new Date();
14
+ const runId = uuidv4();
15
+ const streamMode = Array.isArray(payload.stream_mode)
16
+ ? payload.stream_mode
17
+ : payload.stream_mode
18
+ ? [payload.stream_mode]
19
+ : undefined;
20
+ const config = Object.assign({}, payload.config ?? {}, {
21
+ configurable: {
22
+ run_id: runId,
23
+ thread_id: threadId,
24
+ graph_id: payload.assistant_id,
25
+ },
26
+ }, { metadata: payload.metadata ?? {} });
27
+ return {
28
+ run_id: runId,
29
+ thread_id: threadId,
30
+ assistant_id: payload.assistant_id,
31
+ metadata: payload.metadata ?? {},
32
+ status: "running",
33
+ kwargs: {
34
+ input: payload.input,
35
+ command: payload.command,
36
+ config,
37
+ stream_mode: streamMode,
38
+ interrupt_before: payload.interrupt_before,
39
+ interrupt_after: payload.interrupt_after,
40
+ feedback_keys: payload.feedback_keys,
41
+ subgraphs: payload.stream_subgraphs,
42
+ temporary: false,
43
+ },
44
+ multitask_strategy: "reject",
45
+ created_at: now,
46
+ updated_at: now,
47
+ };
48
+ }
49
+ /**
50
+ * Attach LangGraph Platform-esque routes to a given Hono instance.
51
+ * @experimental Does not follow semver.
52
+ */
53
+ export function createEmbedServer(options) {
54
+ async function getGraph(graphId) {
55
+ const targetGraph = options.graph[graphId];
56
+ targetGraph.store = options.store;
57
+ targetGraph.checkpointer = options.checkpointer;
58
+ return targetGraph;
59
+ }
60
+ const api = new Hono();
61
+ api.use(ensureContentType());
62
+ api.post("/threads", zValidator("json", schemas.ThreadCreate), async (c) => {
63
+ // create a new threaad
64
+ const payload = c.req.valid("json");
65
+ const threadId = payload.thread_id || uuidv4();
66
+ await options.threads.put(threadId, payload);
67
+ return jsonExtra(c, { thread_id: threadId });
68
+ });
69
+ 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) => {
70
+ // Get Latest Thread State
71
+ const { thread_id } = c.req.valid("param");
72
+ const { subgraphs } = c.req.valid("query");
73
+ const thread = await options.threads.get(thread_id);
74
+ const graphId = thread.metadata?.graph_id;
75
+ const graph = graphId ? options.graph[graphId] : undefined;
76
+ if (graph == null) {
77
+ return jsonExtra(c, stateSnapshotToThreadState({
78
+ values: {},
79
+ next: [],
80
+ config: {},
81
+ metadata: undefined,
82
+ createdAt: undefined,
83
+ parentConfig: undefined,
84
+ tasks: [],
85
+ }));
86
+ }
87
+ const config = { configurable: { thread_id } };
88
+ const result = await graph.getState(config, { subgraphs });
89
+ return jsonExtra(c, stateSnapshotToThreadState(result));
90
+ });
91
+ api.post("/threads/:thread_id/history", zValidator("param", z.object({ thread_id: z.string().uuid() })), zValidator("json", schemas.ThreadHistoryRequest), async (c) => {
92
+ // Get Thread History Post
93
+ const { thread_id } = c.req.valid("param");
94
+ const { limit, before, metadata, checkpoint } = c.req.valid("json");
95
+ const thread = await options.threads.get(thread_id);
96
+ const graphId = thread.metadata?.graph_id;
97
+ const graph = graphId ? options.graph[graphId] : undefined;
98
+ if (graph == null)
99
+ return jsonExtra(c, []);
100
+ const config = { configurable: { thread_id, ...checkpoint } };
101
+ const result = [];
102
+ const beforeConfig = typeof before === "string"
103
+ ? { configurable: { checkpoint_id: before } }
104
+ : before;
105
+ for await (const state of graph.getStateHistory(config, {
106
+ limit,
107
+ before: beforeConfig,
108
+ filter: metadata,
109
+ })) {
110
+ result.push(stateSnapshotToThreadState(state));
111
+ }
112
+ return jsonExtra(c, result);
113
+ });
114
+ api.post("/threads/:thread_id/runs/stream", zValidator("param", z.object({ thread_id: z.string().uuid() })), zValidator("json", schemas.RunCreate), async (c) => {
115
+ // Stream Run
116
+ return streamSSE(c, async (stream) => {
117
+ const { thread_id } = c.req.valid("param");
118
+ const payload = c.req.valid("json");
119
+ const signal = getDisconnectAbortSignal(c, stream);
120
+ const run = createStubRun(thread_id, payload);
121
+ // update thread with new graph_id
122
+ const thread = await options.threads.get(thread_id);
123
+ await options.threads.put(thread_id, {
124
+ metadata: {
125
+ ...thread.metadata,
126
+ graph_id: payload.assistant_id,
127
+ assistant_id: payload.assistant_id,
128
+ },
129
+ });
130
+ for await (const { event, data } of streamState(run, {
131
+ attempt: 1,
132
+ getGraph,
133
+ signal,
134
+ })) {
135
+ await stream.writeSSE({ data: serialiseAsDict(data), event });
136
+ }
137
+ });
138
+ });
139
+ api.post("/runs/stream", zValidator("json", schemas.RunCreate), async (c) => {
140
+ // Stream Stateless Run
141
+ return streamSSE(c, async (stream) => {
142
+ const payload = c.req.valid("json");
143
+ const signal = getDisconnectAbortSignal(c, stream);
144
+ const threadId = uuidv4();
145
+ await options.threads.put(threadId, {
146
+ metadata: {
147
+ graph_id: payload.assistant_id,
148
+ assistant_id: payload.assistant_id,
149
+ },
150
+ });
151
+ try {
152
+ const run = createStubRun(threadId, payload);
153
+ for await (const { event, data } of streamState(run, {
154
+ attempt: 1,
155
+ getGraph,
156
+ signal,
157
+ })) {
158
+ await stream.writeSSE({ data: serialiseAsDict(data), event });
159
+ }
160
+ }
161
+ finally {
162
+ await options.threads.delete(threadId);
163
+ }
164
+ });
165
+ });
166
+ return api;
167
+ }
@@ -20,12 +20,11 @@ type AnyGraph = {
20
20
 
21
21
  type Wrap<T> = (a: T) => void;
22
22
  type MatchBaseMessage<T> = T extends BaseMessage ? BaseMessage : never;
23
- type MatchBaseMessageArray<T> =
24
- T extends Array<infer C>
25
- ? Wrap<MatchBaseMessage<C>> extends Wrap<BaseMessage>
26
- ? BaseMessage[]
27
- : never
28
- : never;
23
+ type MatchBaseMessageArray<T> = T extends Array<infer C>
24
+ ? Wrap<MatchBaseMessage<C>> extends Wrap<BaseMessage>
25
+ ? BaseMessage[]
26
+ : never
27
+ : never;
29
28
 
30
29
  type Defactorify<T> = T extends (...args: any[]) => infer R
31
30
  ? Awaited<R>
@@ -35,32 +34,31 @@ type Defactorify<T> = T extends (...args: any[]) => infer R
35
34
  type Inspect<T, TDepth extends Array<0> = []> = TDepth extends [0, 0, 0]
36
35
  ? any
37
36
  : T extends unknown
38
- ? {
39
- [K in keyof T]: 0 extends 1 & T[K]
40
- ? T[K]
41
- : Wrap<MatchBaseMessageArray<T[K]>> extends Wrap<BaseMessage[]>
42
- ? BaseMessage[]
43
- : Wrap<MatchBaseMessage<T[K]>> extends Wrap<BaseMessage>
44
- ? BaseMessage
45
- : Inspect<T[K], [0, ...TDepth]>;
46
- }
47
- : never;
37
+ ? {
38
+ [K in keyof T]: 0 extends 1 & T[K]
39
+ ? T[K]
40
+ : Wrap<MatchBaseMessageArray<T[K]>> extends Wrap<BaseMessage[]>
41
+ ? BaseMessage[]
42
+ : Wrap<MatchBaseMessage<T[K]>> extends Wrap<BaseMessage>
43
+ ? BaseMessage
44
+ : Inspect<T[K], [0, ...TDepth]>;
45
+ }
46
+ : never;
48
47
 
49
48
  type ReflectCompiled<T> = T extends { RunInput: infer S; RunOutput: infer U }
50
49
  ? { state: S; update: U }
51
50
  : T extends { "~InputType": infer InputType; "~OutputType": infer OutputType }
52
- ? { state: OutputType; update: InputType }
53
- : never;
51
+ ? { state: OutputType; update: InputType }
52
+ : never;
54
53
 
55
54
  // @ts-expect-error
56
- type Reflect<T> =
57
- Defactorify<T> extends infer DT
58
- ? DT extends {
59
- compile(...args: any[]): infer Compiled;
60
- }
61
- ? ReflectCompiled<Compiled>
62
- : ReflectCompiled<DT>
63
- : never;
55
+ type Reflect<T> = Defactorify<T> extends infer DT
56
+ ? DT extends {
57
+ compile(...args: any[]): infer Compiled;
58
+ }
59
+ ? ReflectCompiled<Compiled>
60
+ : ReflectCompiled<DT>
61
+ : never;
64
62
 
65
63
  type BuilderReflectCompiled<T> = T extends {
66
64
  builder: {
@@ -77,14 +75,13 @@ type BuilderReflectCompiled<T> = T extends {
77
75
  : never;
78
76
 
79
77
  // @ts-expect-error
80
- type BuilderReflect<T> =
81
- Defactorify<T> extends infer DT
82
- ? DT extends {
83
- compile(...args: any[]): infer Compiled;
84
- }
85
- ? BuilderReflectCompiled<Compiled>
86
- : BuilderReflectCompiled<DT>
87
- : never;
78
+ type BuilderReflect<T> = Defactorify<T> extends infer DT
79
+ ? DT extends {
80
+ compile(...args: any[]): infer Compiled;
81
+ }
82
+ ? BuilderReflectCompiled<Compiled>
83
+ : BuilderReflectCompiled<DT>
84
+ : never;
88
85
 
89
86
  // @ts-expect-error
90
87
  type FilterAny<T> = 0 extends 1 & T ? never : T;
@@ -16,7 +16,7 @@ export const cors = (cors) => {
16
16
  return origin;
17
17
  return undefined;
18
18
  }
19
- : (cors.allow_origins ?? []);
19
+ : cors.allow_origins ?? [];
20
20
  if (cors.expose_headers?.length) {
21
21
  const headersSet = new Set(cors.expose_headers.map((h) => h.toLowerCase()));
22
22
  if (!headersSet.has("content-location")) {
package/dist/queue.mjs CHANGED
@@ -3,6 +3,7 @@ import { streamState, } from "./stream.mjs";
3
3
  import { logError, logger } from "./logging.mjs";
4
4
  import { serializeError } from "./utils/serde.mjs";
5
5
  import { callWebhook } from "./webhook.mjs";
6
+ import { getGraph } from "./graph/load.mjs";
6
7
  const MAX_RETRY_ATTEMPTS = 3;
7
8
  const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
8
9
  export const queue = async () => {
@@ -14,7 +15,7 @@ export const queue = async () => {
14
15
  await sleep(1000 * Math.random());
15
16
  }
16
17
  };
17
- const worker = async (run, attempt, abortSignal) => {
18
+ const worker = async (run, attempt, signal) => {
18
19
  const startedAt = new Date();
19
20
  let endedAt = undefined;
20
21
  let checkpoint = undefined;
@@ -48,8 +49,10 @@ const worker = async (run, attempt, abortSignal) => {
48
49
  const runId = run.run_id;
49
50
  const resumable = run.kwargs?.resumable ?? false;
50
51
  try {
51
- const stream = streamState(run, attempt, {
52
- signal: abortSignal,
52
+ const stream = streamState(run, {
53
+ getGraph,
54
+ attempt,
55
+ signal,
53
56
  ...(!temporary ? { onCheckpoint, onTaskResult } : undefined),
54
57
  });
55
58
  for await (const { event, data } of stream) {
@@ -1340,6 +1340,49 @@ export declare const ThreadStateUpdate: z.ZodObject<{
1340
1340
  checkpoint_id?: string | undefined;
1341
1341
  as_node?: string | undefined;
1342
1342
  }>;
1343
+ export declare const ThreadHistoryRequest: z.ZodObject<{
1344
+ limit: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
1345
+ before: z.ZodOptional<z.ZodString>;
1346
+ metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
1347
+ checkpoint: z.ZodOptional<z.ZodObject<{
1348
+ checkpoint_id: z.ZodOptional<z.ZodString>;
1349
+ checkpoint_ns: z.ZodOptional<z.ZodString>;
1350
+ checkpoint_map: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
1351
+ }, "strip", z.ZodTypeAny, {
1352
+ checkpoint_ns?: string | undefined;
1353
+ checkpoint_id?: string | undefined;
1354
+ checkpoint_map?: Record<string, unknown> | undefined;
1355
+ }, {
1356
+ checkpoint_ns?: string | undefined;
1357
+ checkpoint_id?: string | undefined;
1358
+ checkpoint_map?: Record<string, unknown> | undefined;
1359
+ }>>;
1360
+ }, "strip", z.ZodTypeAny, {
1361
+ limit: number;
1362
+ metadata?: Record<string, unknown> | undefined;
1363
+ checkpoint?: {
1364
+ checkpoint_ns?: string | undefined;
1365
+ checkpoint_id?: string | undefined;
1366
+ checkpoint_map?: Record<string, unknown> | undefined;
1367
+ } | undefined;
1368
+ before?: string | undefined;
1369
+ }, {
1370
+ metadata?: Record<string, unknown> | undefined;
1371
+ checkpoint?: {
1372
+ checkpoint_ns?: string | undefined;
1373
+ checkpoint_id?: string | undefined;
1374
+ checkpoint_map?: Record<string, unknown> | undefined;
1375
+ } | undefined;
1376
+ limit?: number | undefined;
1377
+ before?: string | undefined;
1378
+ }>;
1379
+ export declare const ThreadPatchRequest: z.ZodObject<{
1380
+ metadata: z.ZodRecord<z.ZodString, z.ZodUnknown>;
1381
+ }, "strip", z.ZodTypeAny, {
1382
+ metadata: Record<string, unknown>;
1383
+ }, {
1384
+ metadata: Record<string, unknown>;
1385
+ }>;
1343
1386
  export declare const AssistantLatestVersion: z.ZodObject<{
1344
1387
  version: z.ZodNumber;
1345
1388
  }, "strip", z.ZodTypeAny, {
package/dist/schemas.mjs CHANGED
@@ -388,6 +388,21 @@ export const ThreadStateUpdate = z
388
388
  as_node: z.string().optional(),
389
389
  })
390
390
  .describe("Payload for adding state to a thread.");
391
+ export const ThreadHistoryRequest = z.object({
392
+ limit: z.number().optional().default(10),
393
+ before: z.string().optional(),
394
+ metadata: z.record(z.string(), z.unknown()).optional(),
395
+ checkpoint: z
396
+ .object({
397
+ checkpoint_id: z.string().uuid().optional(),
398
+ checkpoint_ns: z.string().optional(),
399
+ checkpoint_map: z.record(z.string(), z.unknown()).optional(),
400
+ })
401
+ .optional(),
402
+ });
403
+ export const ThreadPatchRequest = z.object({
404
+ metadata: z.record(z.string(), z.unknown()),
405
+ });
391
406
  export const AssistantLatestVersion = z.object({
392
407
  version: z.number(),
393
408
  });
@@ -1,7 +1,8 @@
1
1
  import * as url from "node:url";
2
2
  import * as fs from "node:fs/promises";
3
+ import * as path from "node:path";
3
4
  import * as semver from "semver";
4
- const packageJsonPath = url.fileURLToPath(new URL("../../package.json", import.meta.url));
5
+ const packageJsonPath = path.resolve(url.fileURLToPath(import.meta.url), "../../../package.json");
5
6
  export async function checkSemver(packages) {
6
7
  const packageJson = JSON.parse(await fs.readFile(packageJsonPath, "utf-8"));
7
8
  const peerDependencies = packageJson.peerDependencies ?? {};
package/dist/state.mjs CHANGED
@@ -15,13 +15,11 @@ export const stateSnapshotToThreadState = (state) => {
15
15
  path: task.path,
16
16
  // TODO: too many type assertions, check if this is actually correct
17
17
  checkpoint: task.state != null && "configurable" in task.state
18
- ? (task.state.configurable ?? null)
18
+ ? task.state.configurable ?? null
19
19
  : null,
20
20
  state: task.state != null && isStateSnapshot(task.state)
21
21
  ? stateSnapshotToThreadState(task.state)
22
22
  : null,
23
- // TODO: add missing result to the library
24
- // @ts-expect-error
25
23
  result: task.result ?? null,
26
24
  })),
27
25
  metadata: state.metadata,
@@ -5,7 +5,7 @@ import { FileSystemPersistence } from "./persist.mjs";
5
5
  export type Metadata = Record<string, unknown>;
6
6
  export type ThreadStatus = "idle" | "busy" | "interrupted" | "error";
7
7
  export type RunStatus = "pending" | "running" | "error" | "success" | "timeout" | "interrupted";
8
- export type StreamMode = "values" | "messages" | "messages-tuple" | "custom" | "updates" | "events" | "debug";
8
+ export type StreamMode = "values" | "messages" | "messages-tuple" | "custom" | "updates" | "events" | "debug" | "tasks" | "checkpoints";
9
9
  export type MultitaskStrategy = "reject" | "rollback" | "interrupt" | "enqueue";
10
10
  export type OnConflictBehavior = "raise" | "do_nothing";
11
11
  export type IfNotExists = "create" | "reject";
@@ -183,7 +183,7 @@ interface ThreadTask {
183
183
  interrupts: Record<string, unknown>[];
184
184
  checkpoint: Checkpoint | null;
185
185
  state: ThreadState | null;
186
- result: Record<string, unknown> | null;
186
+ result: unknown | null;
187
187
  }
188
188
  export interface ThreadState {
189
189
  values: Record<string, unknown>;
@@ -744,27 +744,37 @@ export class Runs {
744
744
  static async *next() {
745
745
  yield* conn.withGenerator(async function* (STORE, options) {
746
746
  const now = new Date();
747
- const pendingRuns = Object.values(STORE.runs)
747
+ const pendingRunIds = Object.values(STORE.runs)
748
748
  .filter((run) => run.status === "pending" && run.created_at < now)
749
- .sort((a, b) => a.created_at.getTime() - b.created_at.getTime());
750
- if (!pendingRuns.length) {
749
+ .sort((a, b) => a.created_at.getTime() - b.created_at.getTime())
750
+ .map((run) => run.run_id);
751
+ if (!pendingRunIds.length) {
751
752
  return;
752
753
  }
753
- for (const run of pendingRuns) {
754
- const runId = run.run_id;
755
- const threadId = run.thread_id;
756
- const thread = STORE.threads[threadId];
757
- if (!thread) {
758
- await console.warn(`Unexpected missing thread in Runs.next: ${threadId}`);
759
- continue;
760
- }
754
+ for (const runId of pendingRunIds) {
761
755
  if (StreamManager.isLocked(runId))
762
756
  continue;
763
757
  try {
764
758
  const signal = StreamManager.lock(runId);
759
+ const run = STORE.runs[runId];
760
+ if (!run)
761
+ continue;
762
+ const threadId = run.thread_id;
763
+ const thread = STORE.threads[threadId];
764
+ if (!thread) {
765
+ logger.warn(`Unexpected missing thread in Runs.next: ${threadId}`);
766
+ continue;
767
+ }
768
+ // is the run still valid?
769
+ if (run.status !== "pending")
770
+ continue;
771
+ if (Object.values(STORE.runs).some((run) => run.thread_id === threadId && run.status === "running")) {
772
+ continue;
773
+ }
765
774
  options.schedulePersist();
766
775
  STORE.retry_counter[runId] ??= 0;
767
776
  STORE.retry_counter[runId] += 1;
777
+ STORE.runs[runId].status = "running";
768
778
  yield { run, attempt: STORE.retry_counter[runId], signal };
769
779
  }
770
780
  finally {
@@ -842,7 +852,8 @@ export class Runs {
842
852
  }
843
853
  // if multitask_mode = reject, check for inflight runs
844
854
  // and if there are any, return them to reject putting a new run
845
- const inflightRuns = Object.values(STORE.runs).filter((run) => run.thread_id === threadId && run.status === "pending");
855
+ const inflightRuns = Object.values(STORE.runs).filter((run) => run.thread_id === threadId &&
856
+ (run.status === "pending" || run.status === "running"));
846
857
  if (options?.preventInsertInInflight) {
847
858
  if (inflightRuns.length > 0)
848
859
  return inflightRuns;
@@ -1090,7 +1101,7 @@ export class Runs {
1090
1101
  yield { event: "error", data: "Run not found" };
1091
1102
  break;
1092
1103
  }
1093
- else if (run.status !== "pending") {
1104
+ else if (run.status !== "pending" && run.status !== "running") {
1094
1105
  break;
1095
1106
  }
1096
1107
  }
package/dist/stream.d.mts CHANGED
@@ -1,4 +1,5 @@
1
- import { type CheckpointMetadata, type Interrupt, type StateSnapshot } from "@langchain/langgraph";
1
+ import type { BaseCheckpointSaver, LangGraphRunnableConfig, CheckpointMetadata, Interrupt, StateSnapshot } from "@langchain/langgraph";
2
+ import type { Pregel } from "@langchain/langgraph/pregel";
2
3
  import type { Checkpoint, Run, RunnableConfig } from "./storage/ops.mjs";
3
4
  interface DebugTask {
4
5
  id: string;
@@ -27,7 +28,11 @@ export type StreamTaskResult = Prettify<Omit<DebugTask, "state"> & {
27
28
  state?: StateSnapshot;
28
29
  checkpoint?: Checkpoint;
29
30
  }>;
30
- export declare function streamState(run: Run, attempt?: number, options?: {
31
+ export declare function streamState(run: Run, options: {
32
+ attempt: number;
33
+ getGraph: (graphId: string, config: LangGraphRunnableConfig | undefined, options?: {
34
+ checkpointer?: BaseCheckpointSaver | null;
35
+ }) => Promise<Pregel<any, any, any, any, any>>;
31
36
  onCheckpoint?: (checkpoint: StreamCheckpoint) => void;
32
37
  onTaskResult?: (taskResult: StreamTaskResult) => void;
33
38
  signal?: AbortSignal;
package/dist/stream.mjs CHANGED
@@ -2,7 +2,6 @@ import { isBaseMessage } from "@langchain/core/messages";
2
2
  import { LangChainTracer } from "@langchain/core/tracers/tracer_langchain";
3
3
  import { Client as LangSmithClient, getDefaultProjectName } from "langsmith";
4
4
  import { getLangGraphCommand } from "./command.mjs";
5
- import { getGraph } from "./graph/load.mjs";
6
5
  import { checkLangGraphSemver } from "./semver/index.mjs";
7
6
  import { runnableConfigToCheckpoint, taskRunnableConfigToCheckpoint, } from "./utils/runnableConfig.mjs";
8
7
  const isRunnableConfig = (config) => {
@@ -54,13 +53,13 @@ function preprocessDebugCheckpoint(payload) {
54
53
  return result;
55
54
  }
56
55
  let LANGGRAPH_VERSION;
57
- export async function* streamState(run, attempt = 1, options) {
56
+ export async function* streamState(run, options) {
58
57
  const kwargs = run.kwargs;
59
58
  const graphId = kwargs.config?.configurable?.graph_id;
60
59
  if (!graphId || typeof graphId !== "string") {
61
60
  throw new Error("Invalid or missing graph_id");
62
61
  }
63
- const graph = await getGraph(graphId, kwargs.config, {
62
+ const graph = await options.getGraph(graphId, kwargs.config, {
64
63
  checkpointer: kwargs.temporary ? null : undefined,
65
64
  });
66
65
  const userStreamMode = kwargs.stream_mode ?? [];
@@ -75,7 +74,7 @@ export async function* streamState(run, attempt = 1, options) {
75
74
  libStreamMode.add("debug");
76
75
  yield {
77
76
  event: "metadata",
78
- data: { run_id: run.run_id, attempt },
77
+ data: { run_id: run.run_id, attempt: options.attempt },
79
78
  };
80
79
  if (!LANGGRAPH_VERSION) {
81
80
  const version = await checkLangGraphSemver();
@@ -83,7 +82,7 @@ export async function* streamState(run, attempt = 1, options) {
83
82
  }
84
83
  const metadata = {
85
84
  ...kwargs.config?.metadata,
86
- run_attempt: attempt,
85
+ run_attempt: options.attempt,
87
86
  langgraph_version: LANGGRAPH_VERSION?.version ?? "0.0.0",
88
87
  langgraph_plan: "developer",
89
88
  langgraph_host: "self-hosted",
@@ -104,7 +103,7 @@ export async function* streamState(run, attempt = 1, options) {
104
103
  : undefined;
105
104
  const events = graph.streamEvents(kwargs.command != null
106
105
  ? getLangGraphCommand(kwargs.command)
107
- : (kwargs.input ?? null), {
106
+ : kwargs.input ?? null, {
108
107
  version: "v2",
109
108
  interruptAfter: kwargs.interrupt_after,
110
109
  interruptBefore: kwargs.interrupt_before,
package/dist/ui/load.mjs CHANGED
@@ -26,9 +26,10 @@ api.post("/ui/:agent", zValidator("json", z.object({ name: z.string() })), async
26
26
  for (const css of files.filter((i) => path.extname(i.basename) === ".css")) {
27
27
  result.push(`<link rel="stylesheet" href="http://${host}/ui/${agent}/${css.basename}" />`);
28
28
  }
29
+ const stableName = agent.replace(/[^a-zA-Z0-9]/g, "_");
29
30
  const js = files.find((i) => path.extname(i.basename) === ".js");
30
31
  if (js) {
31
- result.push(`<script src="http://${host}/ui/${agent}/${js.basename}" onload='__LGUI_${agent}.render(${messageName}, "{{shadowRootId}}")'></script>`);
32
+ result.push(`<script src="http://${host}/ui/${agent}/${js.basename}" onload='__LGUI_${stableName}.render(${messageName}, "{{shadowRootId}}")'></script>`);
32
33
  }
33
34
  return c.text(result.join("\n"), {
34
35
  headers: { "Content-Type": "text/html" },
@@ -0,0 +1,2 @@
1
+ declare const _default: import("vitest/config").UserConfigFnObject;
2
+ export default _default;
@@ -0,0 +1,12 @@
1
+ import { configDefaults, defineConfig } from "vitest/config";
2
+ import { nodePolyfills } from "vite-plugin-node-polyfills";
3
+ export default defineConfig(() => {
4
+ /** @type {import("vitest/config").UserConfigExport} */
5
+ return {
6
+ test: {
7
+ hideSkippedTests: true,
8
+ testTimeout: 30_000,
9
+ fileParallelism: false,
10
+ },
11
+ };
12
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@langchain/langgraph-api",
3
- "version": "0.0.40",
3
+ "version": "0.0.42",
4
4
  "type": "module",
5
5
  "engines": {
6
6
  "node": "^18.19.0 || >=20.16.0"
@@ -27,6 +27,10 @@
27
27
  "types": "./dist/graph/parser/index.d.mts",
28
28
  "default": "./dist/graph/parser/index.mjs"
29
29
  },
30
+ "./experimental/embed": {
31
+ "types": "./dist/experimental/embed.d.mts",
32
+ "default": "./dist/experimental/embed.mjs"
33
+ },
30
34
  "./package.json": "./package.json"
31
35
  },
32
36
  "repository": {
@@ -34,14 +38,13 @@
34
38
  "url": "git@github.com:langchain-ai/langgraphjs-api.git"
35
39
  },
36
40
  "scripts": {
37
- "clean": "npx -y bun scripts/clean.mjs",
38
- "build": "npx -y bun scripts/build.mjs",
41
+ "clean": "rm -rf dist/ .turbo/ ./tests/graphs/.langgraph_api/",
42
+ "build": "yarn turbo:command build:internal --filter=@langchain/langgraph-api",
43
+ "build:internal": "yarn clean && node scripts/build.mjs",
39
44
  "dev": "tsx ./tests/utils.server.mts --dev",
40
45
  "prepack": "yarn run build",
41
46
  "typecheck": "tsc --noEmit",
42
- "test": "vitest",
43
- "test:parser": "vitest run ./tests/parser.test.mts --testTimeout 15000",
44
- "test:api": "npx -y bun scripts/test.mjs",
47
+ "test": "vitest run",
45
48
  "format": "prettier --write .",
46
49
  "format:check": "prettier --check ."
47
50
  },
@@ -49,7 +52,7 @@
49
52
  "@babel/code-frame": "^7.26.2",
50
53
  "@hono/node-server": "^1.12.0",
51
54
  "@hono/zod-validator": "^0.2.2",
52
- "@langchain/langgraph-ui": "0.0.40",
55
+ "@langchain/langgraph-ui": "workspace:*",
53
56
  "@types/json-schema": "^7.0.15",
54
57
  "@typescript/vfs": "^1.6.0",
55
58
  "dedent": "^1.5.3",
@@ -81,19 +84,20 @@
81
84
  },
82
85
  "devDependencies": {
83
86
  "@langchain/core": "^0.3.59",
84
- "@langchain/langgraph": "^0.2.57",
85
- "@langchain/langgraph-checkpoint": "~0.0.16",
86
- "@langchain/langgraph-sdk": "^0.0.77",
87
+ "@langchain/langgraph": "workspace:*",
88
+ "@langchain/langgraph-checkpoint": "workspace:*",
89
+ "@langchain/langgraph-sdk": "workspace:*",
87
90
  "@types/babel__code-frame": "^7.0.6",
88
- "@types/node": "^22.2.0",
91
+ "@types/node": "^18.15.11",
89
92
  "@types/react": "^19.0.8",
90
93
  "@types/react-dom": "^19.0.3",
91
94
  "@types/semver": "^7.7.0",
92
95
  "@types/uuid": "^10.0.0",
93
96
  "jose": "^6.0.10",
94
97
  "postgres": "^3.4.5",
95
- "prettier": "^3.3.3",
96
- "typescript": "^5.5.4",
97
- "vitest": "^3.0.5"
98
+ "prettier": "^2.8.3",
99
+ "typescript": "^4.9.5 || ^5.4.5",
100
+ "vitest": "^3.1.2",
101
+ "wait-port": "^1.1.0"
98
102
  }
99
- }
103
+ }