@langchain/langgraph-api 0.0.35 → 0.0.37
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/runs.mjs +36 -5
- package/dist/graph/parser/parser.mjs +7 -2
- package/dist/http/middleware.mjs +11 -2
- package/dist/queue.mjs +9 -2
- package/dist/schemas.d.mts +6 -0
- package/dist/schemas.mjs +1 -0
- package/dist/server.d.mts +6 -6
- package/dist/storage/ops.d.mts +18 -6
- package/dist/storage/ops.mjs +61 -21
- package/package.json +3 -3
package/dist/api/runs.mjs
CHANGED
|
@@ -75,6 +75,7 @@ const createValidRun = async (threadId, payload, kwargs) => {
|
|
|
75
75
|
feedback_keys: feedbackKeys,
|
|
76
76
|
temporary: threadId == null && (run.on_completion ?? "delete") === "delete",
|
|
77
77
|
subgraphs: run.stream_subgraphs ?? false,
|
|
78
|
+
resumable: run.stream_resumable ?? false,
|
|
78
79
|
}, {
|
|
79
80
|
threadId,
|
|
80
81
|
userId,
|
|
@@ -139,7 +140,11 @@ api.post("/runs/stream", zValidator("json", schemas.RunCreate), async (c) => {
|
|
|
139
140
|
? getDisconnectAbortSignal(c, stream)
|
|
140
141
|
: undefined;
|
|
141
142
|
try {
|
|
142
|
-
for await (const { event, data } of Runs.Stream.join(run.run_id, undefined, {
|
|
143
|
+
for await (const { event, data } of Runs.Stream.join(run.run_id, undefined, {
|
|
144
|
+
cancelOnDisconnect,
|
|
145
|
+
lastEventId: run.kwargs.resumable ? "-1" : undefined,
|
|
146
|
+
ignore404: true,
|
|
147
|
+
}, c.var.auth)) {
|
|
143
148
|
await stream.writeSSE({ data: serialiseAsDict(data), event });
|
|
144
149
|
}
|
|
145
150
|
}
|
|
@@ -148,6 +153,25 @@ api.post("/runs/stream", zValidator("json", schemas.RunCreate), async (c) => {
|
|
|
148
153
|
}
|
|
149
154
|
});
|
|
150
155
|
});
|
|
156
|
+
// TODO: port to Python API
|
|
157
|
+
api.get("/runs/:run_id/stream", zValidator("param", z.object({ run_id: z.string().uuid() })), zValidator("query", z.object({ cancel_on_disconnect: schemas.coercedBoolean.optional() })), async (c) => {
|
|
158
|
+
const { run_id } = c.req.valid("param");
|
|
159
|
+
const query = c.req.valid("query");
|
|
160
|
+
const lastEventId = c.req.header("Last-Event-ID") || undefined;
|
|
161
|
+
return streamSSE(c, async (stream) => {
|
|
162
|
+
const cancelOnDisconnect = query.cancel_on_disconnect
|
|
163
|
+
? getDisconnectAbortSignal(c, stream)
|
|
164
|
+
: undefined;
|
|
165
|
+
try {
|
|
166
|
+
for await (const { id, event, data } of Runs.Stream.join(run_id, undefined, { cancelOnDisconnect, lastEventId, ignore404: true }, c.var.auth)) {
|
|
167
|
+
await stream.writeSSE({ id, data: serialiseAsDict(data), event });
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
catch (error) {
|
|
171
|
+
logError(error, { prefix: "Error streaming run" });
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
});
|
|
151
175
|
api.post("/runs/wait", zValidator("json", schemas.RunCreate), async (c) => {
|
|
152
176
|
// Wait Stateless Run
|
|
153
177
|
const payload = c.req.valid("json");
|
|
@@ -198,6 +222,7 @@ api.post("/threads/:thread_id/runs", zValidator("param", z.object({ thread_id: z
|
|
|
198
222
|
auth: c.var.auth,
|
|
199
223
|
headers: c.req.raw.headers,
|
|
200
224
|
});
|
|
225
|
+
c.header("Content-Location", `/threads/${thread_id}/runs/${run.run_id}`);
|
|
201
226
|
return jsonExtra(c, run);
|
|
202
227
|
});
|
|
203
228
|
api.post("/threads/:thread_id/runs/stream", zValidator("param", z.object({ thread_id: z.string().uuid() })), zValidator("json", schemas.RunCreate), async (c) => {
|
|
@@ -208,13 +233,17 @@ api.post("/threads/:thread_id/runs/stream", zValidator("param", z.object({ threa
|
|
|
208
233
|
auth: c.var.auth,
|
|
209
234
|
headers: c.req.raw.headers,
|
|
210
235
|
});
|
|
236
|
+
c.header("Content-Location", `/threads/${thread_id}/runs/${run.run_id}`);
|
|
211
237
|
return streamSSE(c, async (stream) => {
|
|
212
238
|
const cancelOnDisconnect = payload.on_disconnect === "cancel"
|
|
213
239
|
? getDisconnectAbortSignal(c, stream)
|
|
214
240
|
: undefined;
|
|
215
241
|
try {
|
|
216
|
-
for await (const { event, data } of Runs.Stream.join(run.run_id, thread_id, {
|
|
217
|
-
|
|
242
|
+
for await (const { id, event, data } of Runs.Stream.join(run.run_id, thread_id, {
|
|
243
|
+
cancelOnDisconnect,
|
|
244
|
+
lastEventId: run.kwargs.resumable ? "-1" : undefined,
|
|
245
|
+
}, c.var.auth)) {
|
|
246
|
+
await stream.writeSSE({ id, data: serialiseAsDict(data), event });
|
|
218
247
|
}
|
|
219
248
|
}
|
|
220
249
|
catch (error) {
|
|
@@ -230,6 +259,7 @@ api.post("/threads/:thread_id/runs/wait", zValidator("param", z.object({ thread_
|
|
|
230
259
|
auth: c.var.auth,
|
|
231
260
|
headers: c.req.raw.headers,
|
|
232
261
|
});
|
|
262
|
+
c.header("Content-Location", `/threads/${thread_id}/runs/${run.run_id}`);
|
|
233
263
|
return waitKeepAlive(c, Runs.join(run.run_id, thread_id, c.var.auth));
|
|
234
264
|
});
|
|
235
265
|
api.get("/threads/:thread_id/runs/:run_id", zValidator("param", z.object({ thread_id: z.string().uuid(), run_id: z.string().uuid() })), async (c) => {
|
|
@@ -257,12 +287,13 @@ api.get("/threads/:thread_id/runs/:run_id/stream", zValidator("param", z.object(
|
|
|
257
287
|
// Stream Run Http
|
|
258
288
|
const { thread_id, run_id } = c.req.valid("param");
|
|
259
289
|
const { cancel_on_disconnect } = c.req.valid("query");
|
|
290
|
+
const lastEventId = c.req.header("Last-Event-ID") || undefined;
|
|
260
291
|
return streamSSE(c, async (stream) => {
|
|
261
292
|
const signal = cancel_on_disconnect
|
|
262
293
|
? getDisconnectAbortSignal(c, stream)
|
|
263
294
|
: undefined;
|
|
264
|
-
for await (const { event, data } of Runs.Stream.join(run_id, thread_id, { cancelOnDisconnect: signal }, c.var.auth)) {
|
|
265
|
-
await stream.writeSSE({ data: serialiseAsDict(data), event });
|
|
295
|
+
for await (const { id, event, data } of Runs.Stream.join(run_id, thread_id, { cancelOnDisconnect: signal, lastEventId }, c.var.auth)) {
|
|
296
|
+
await stream.writeSSE({ id, data: serialiseAsDict(data), event });
|
|
266
297
|
}
|
|
267
298
|
});
|
|
268
299
|
});
|
|
@@ -147,12 +147,17 @@ export class SubgraphExtractor {
|
|
|
147
147
|
};
|
|
148
148
|
getAugmentedSourceFile = (suffix, name) => {
|
|
149
149
|
const vars = this.getSubgraphsVariables(name);
|
|
150
|
+
const typeSuffix = suffix.replace(/[^a-zA-Z0-9]/g, "_");
|
|
150
151
|
const typeExports = [
|
|
151
|
-
{
|
|
152
|
+
{
|
|
153
|
+
typeName: `__langgraph__${name}_${typeSuffix}`,
|
|
154
|
+
valueName: name,
|
|
155
|
+
graphName: name,
|
|
156
|
+
},
|
|
152
157
|
];
|
|
153
158
|
for (const { subgraph, node, namespace } of vars) {
|
|
154
159
|
typeExports.push({
|
|
155
|
-
typeName: `__langgraph__${namespace.join("_")}_${node}`,
|
|
160
|
+
typeName: `__langgraph__${namespace.join("_")}_${node}_${typeSuffix}`,
|
|
156
161
|
valueName: subgraph.name,
|
|
157
162
|
graphName: [...namespace, node].join("|"),
|
|
158
163
|
});
|
package/dist/http/middleware.mjs
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import { cors as honoCors } from "hono/cors";
|
|
2
2
|
export const cors = (cors) => {
|
|
3
|
-
if (cors == null)
|
|
4
|
-
return honoCors(
|
|
3
|
+
if (cors == null) {
|
|
4
|
+
return honoCors({
|
|
5
|
+
origin: "*",
|
|
6
|
+
exposeHeaders: ["content-location"],
|
|
7
|
+
});
|
|
8
|
+
}
|
|
5
9
|
const originRegex = cors.allow_origin_regex
|
|
6
10
|
? new RegExp(cors.allow_origin_regex)
|
|
7
11
|
: undefined;
|
|
@@ -13,6 +17,11 @@ export const cors = (cors) => {
|
|
|
13
17
|
return undefined;
|
|
14
18
|
}
|
|
15
19
|
: (cors.allow_origins ?? []);
|
|
20
|
+
if (!!cors.expose_headers?.length &&
|
|
21
|
+
cors.expose_headers.some((i) => i.toLocaleLowerCase() === "content-location")) {
|
|
22
|
+
console.warn("Adding missing `Content-Location` header in `cors.expose_headers`.");
|
|
23
|
+
cors.expose_headers.push("content-location");
|
|
24
|
+
}
|
|
16
25
|
// TODO: handle `cors.allow_credentials`
|
|
17
26
|
return honoCors({
|
|
18
27
|
origin,
|
package/dist/queue.mjs
CHANGED
|
@@ -45,17 +45,24 @@ const worker = async (run, attempt, abortSignal) => {
|
|
|
45
45
|
if (attempt > MAX_RETRY_ATTEMPTS) {
|
|
46
46
|
throw new Error(`Run ${run.run_id} exceeded max attempts`);
|
|
47
47
|
}
|
|
48
|
+
const runId = run.run_id;
|
|
49
|
+
const resumable = run.kwargs?.resumable ?? false;
|
|
48
50
|
try {
|
|
49
51
|
const stream = streamState(run, attempt, {
|
|
50
52
|
signal: abortSignal,
|
|
51
53
|
...(!temporary ? { onCheckpoint, onTaskResult } : undefined),
|
|
52
54
|
});
|
|
53
55
|
for await (const { event, data } of stream) {
|
|
54
|
-
await Runs.Stream.publish(
|
|
56
|
+
await Runs.Stream.publish({ runId, resumable, event, data });
|
|
55
57
|
}
|
|
56
58
|
}
|
|
57
59
|
catch (error) {
|
|
58
|
-
await Runs.Stream.publish(
|
|
60
|
+
await Runs.Stream.publish({
|
|
61
|
+
runId,
|
|
62
|
+
resumable,
|
|
63
|
+
event: "error",
|
|
64
|
+
data: serializeError(error),
|
|
65
|
+
});
|
|
59
66
|
throw error;
|
|
60
67
|
}
|
|
61
68
|
endedAt = new Date();
|
package/dist/schemas.d.mts
CHANGED
|
@@ -687,6 +687,7 @@ export declare const RunCreate: z.ZodObject<{
|
|
|
687
687
|
multitask_strategy: z.ZodOptional<z.ZodEnum<["reject", "rollback", "interrupt", "enqueue"]>>;
|
|
688
688
|
stream_mode: z.ZodOptional<z.ZodUnion<[z.ZodArray<z.ZodEnum<["values", "messages", "messages-tuple", "updates", "events", "debug", "custom"]>, "many">, z.ZodEnum<["values", "messages", "messages-tuple", "updates", "events", "debug", "custom"]>]>>;
|
|
689
689
|
stream_subgraphs: z.ZodOptional<z.ZodBoolean>;
|
|
690
|
+
stream_resumable: z.ZodOptional<z.ZodBoolean>;
|
|
690
691
|
after_seconds: z.ZodOptional<z.ZodNumber>;
|
|
691
692
|
if_not_exists: z.ZodOptional<z.ZodEnum<["reject", "create"]>>;
|
|
692
693
|
on_completion: z.ZodOptional<z.ZodEnum<["delete", "keep"]>>;
|
|
@@ -736,6 +737,7 @@ export declare const RunCreate: z.ZodObject<{
|
|
|
736
737
|
if_not_exists?: "reject" | "create" | undefined;
|
|
737
738
|
after_seconds?: number | undefined;
|
|
738
739
|
stream_subgraphs?: boolean | undefined;
|
|
740
|
+
stream_resumable?: boolean | undefined;
|
|
739
741
|
on_completion?: "delete" | "keep" | undefined;
|
|
740
742
|
}, {
|
|
741
743
|
assistant_id: string;
|
|
@@ -782,6 +784,7 @@ export declare const RunCreate: z.ZodObject<{
|
|
|
782
784
|
after_seconds?: number | undefined;
|
|
783
785
|
on_disconnect?: "cancel" | "continue" | undefined;
|
|
784
786
|
stream_subgraphs?: boolean | undefined;
|
|
787
|
+
stream_resumable?: boolean | undefined;
|
|
785
788
|
on_completion?: "delete" | "keep" | undefined;
|
|
786
789
|
}>;
|
|
787
790
|
export declare const RunBatchCreate: z.ZodArray<z.ZodObject<{
|
|
@@ -892,6 +895,7 @@ export declare const RunBatchCreate: z.ZodArray<z.ZodObject<{
|
|
|
892
895
|
multitask_strategy: z.ZodOptional<z.ZodEnum<["reject", "rollback", "interrupt", "enqueue"]>>;
|
|
893
896
|
stream_mode: z.ZodOptional<z.ZodUnion<[z.ZodArray<z.ZodEnum<["values", "messages", "messages-tuple", "updates", "events", "debug", "custom"]>, "many">, z.ZodEnum<["values", "messages", "messages-tuple", "updates", "events", "debug", "custom"]>]>>;
|
|
894
897
|
stream_subgraphs: z.ZodOptional<z.ZodBoolean>;
|
|
898
|
+
stream_resumable: z.ZodOptional<z.ZodBoolean>;
|
|
895
899
|
after_seconds: z.ZodOptional<z.ZodNumber>;
|
|
896
900
|
if_not_exists: z.ZodOptional<z.ZodEnum<["reject", "create"]>>;
|
|
897
901
|
on_completion: z.ZodOptional<z.ZodEnum<["delete", "keep"]>>;
|
|
@@ -941,6 +945,7 @@ export declare const RunBatchCreate: z.ZodArray<z.ZodObject<{
|
|
|
941
945
|
if_not_exists?: "reject" | "create" | undefined;
|
|
942
946
|
after_seconds?: number | undefined;
|
|
943
947
|
stream_subgraphs?: boolean | undefined;
|
|
948
|
+
stream_resumable?: boolean | undefined;
|
|
944
949
|
on_completion?: "delete" | "keep" | undefined;
|
|
945
950
|
}, {
|
|
946
951
|
assistant_id: string;
|
|
@@ -987,6 +992,7 @@ export declare const RunBatchCreate: z.ZodArray<z.ZodObject<{
|
|
|
987
992
|
after_seconds?: number | undefined;
|
|
988
993
|
on_disconnect?: "cancel" | "continue" | undefined;
|
|
989
994
|
stream_subgraphs?: boolean | undefined;
|
|
995
|
+
stream_resumable?: boolean | undefined;
|
|
990
996
|
on_completion?: "delete" | "keep" | undefined;
|
|
991
997
|
}>, "many">;
|
|
992
998
|
export declare const SearchResult: z.ZodObject<{
|
package/dist/schemas.mjs
CHANGED
|
@@ -205,6 +205,7 @@ export const RunCreate = z
|
|
|
205
205
|
])
|
|
206
206
|
.optional(),
|
|
207
207
|
stream_subgraphs: z.boolean().optional(),
|
|
208
|
+
stream_resumable: z.boolean().optional(),
|
|
208
209
|
after_seconds: z.number().optional(),
|
|
209
210
|
if_not_exists: z.enum(["reject", "create"]).optional(),
|
|
210
211
|
on_completion: z.enum(["delete", "keep"]).optional(),
|
package/dist/server.d.mts
CHANGED
|
@@ -40,19 +40,19 @@ export declare const StartServerSchema: z.ZodObject<{
|
|
|
40
40
|
max_age: z.ZodOptional<z.ZodNumber>;
|
|
41
41
|
}, "strip", z.ZodTypeAny, {
|
|
42
42
|
allow_origin_regex?: string | undefined;
|
|
43
|
+
expose_headers?: string[] | undefined;
|
|
43
44
|
allow_origins?: string[] | undefined;
|
|
44
45
|
allow_methods?: string[] | undefined;
|
|
45
46
|
allow_headers?: string[] | undefined;
|
|
46
47
|
allow_credentials?: boolean | undefined;
|
|
47
|
-
expose_headers?: string[] | undefined;
|
|
48
48
|
max_age?: number | undefined;
|
|
49
49
|
}, {
|
|
50
50
|
allow_origin_regex?: string | undefined;
|
|
51
|
+
expose_headers?: string[] | undefined;
|
|
51
52
|
allow_origins?: string[] | undefined;
|
|
52
53
|
allow_methods?: string[] | undefined;
|
|
53
54
|
allow_headers?: string[] | undefined;
|
|
54
55
|
allow_credentials?: boolean | undefined;
|
|
55
|
-
expose_headers?: string[] | undefined;
|
|
56
56
|
max_age?: number | undefined;
|
|
57
57
|
}>>;
|
|
58
58
|
}, "strip", z.ZodTypeAny, {
|
|
@@ -63,22 +63,22 @@ export declare const StartServerSchema: z.ZodObject<{
|
|
|
63
63
|
disable_meta: boolean;
|
|
64
64
|
cors?: {
|
|
65
65
|
allow_origin_regex?: string | undefined;
|
|
66
|
+
expose_headers?: string[] | undefined;
|
|
66
67
|
allow_origins?: string[] | undefined;
|
|
67
68
|
allow_methods?: string[] | undefined;
|
|
68
69
|
allow_headers?: string[] | undefined;
|
|
69
70
|
allow_credentials?: boolean | undefined;
|
|
70
|
-
expose_headers?: string[] | undefined;
|
|
71
71
|
max_age?: number | undefined;
|
|
72
72
|
} | undefined;
|
|
73
73
|
app?: string | undefined;
|
|
74
74
|
}, {
|
|
75
75
|
cors?: {
|
|
76
76
|
allow_origin_regex?: string | undefined;
|
|
77
|
+
expose_headers?: string[] | undefined;
|
|
77
78
|
allow_origins?: string[] | undefined;
|
|
78
79
|
allow_methods?: string[] | undefined;
|
|
79
80
|
allow_headers?: string[] | undefined;
|
|
80
81
|
allow_credentials?: boolean | undefined;
|
|
81
|
-
expose_headers?: string[] | undefined;
|
|
82
82
|
max_age?: number | undefined;
|
|
83
83
|
} | undefined;
|
|
84
84
|
app?: string | undefined;
|
|
@@ -110,11 +110,11 @@ export declare const StartServerSchema: z.ZodObject<{
|
|
|
110
110
|
disable_meta: boolean;
|
|
111
111
|
cors?: {
|
|
112
112
|
allow_origin_regex?: string | undefined;
|
|
113
|
+
expose_headers?: string[] | undefined;
|
|
113
114
|
allow_origins?: string[] | undefined;
|
|
114
115
|
allow_methods?: string[] | undefined;
|
|
115
116
|
allow_headers?: string[] | undefined;
|
|
116
117
|
allow_credentials?: boolean | undefined;
|
|
117
|
-
expose_headers?: string[] | undefined;
|
|
118
118
|
max_age?: number | undefined;
|
|
119
119
|
} | undefined;
|
|
120
120
|
app?: string | undefined;
|
|
@@ -136,11 +136,11 @@ export declare const StartServerSchema: z.ZodObject<{
|
|
|
136
136
|
http?: {
|
|
137
137
|
cors?: {
|
|
138
138
|
allow_origin_regex?: string | undefined;
|
|
139
|
+
expose_headers?: string[] | undefined;
|
|
139
140
|
allow_origins?: string[] | undefined;
|
|
140
141
|
allow_methods?: string[] | undefined;
|
|
141
142
|
allow_headers?: string[] | undefined;
|
|
142
143
|
allow_credentials?: boolean | undefined;
|
|
143
|
-
expose_headers?: string[] | undefined;
|
|
144
144
|
max_age?: number | undefined;
|
|
145
145
|
} | undefined;
|
|
146
146
|
app?: string | undefined;
|
package/dist/storage/ops.d.mts
CHANGED
|
@@ -46,6 +46,7 @@ export interface RunKwargs {
|
|
|
46
46
|
interrupt_after?: "*" | string[] | undefined;
|
|
47
47
|
config?: RunnableConfig;
|
|
48
48
|
subgraphs?: boolean;
|
|
49
|
+
resumable?: boolean;
|
|
49
50
|
temporary?: boolean;
|
|
50
51
|
webhook?: unknown;
|
|
51
52
|
feedback_keys?: string[] | undefined;
|
|
@@ -75,13 +76,19 @@ interface Message {
|
|
|
75
76
|
data: unknown;
|
|
76
77
|
}
|
|
77
78
|
declare class Queue {
|
|
78
|
-
private
|
|
79
|
+
private log;
|
|
79
80
|
private listeners;
|
|
81
|
+
private nextId;
|
|
82
|
+
private resumable;
|
|
83
|
+
constructor(options: {
|
|
84
|
+
resumable: boolean;
|
|
85
|
+
});
|
|
80
86
|
push(item: Message): void;
|
|
81
87
|
get(options: {
|
|
82
88
|
timeout: number;
|
|
89
|
+
lastEventId?: string;
|
|
83
90
|
signal?: AbortSignal;
|
|
84
|
-
}): Promise<Message>;
|
|
91
|
+
}): Promise<[id: string, message: Message]>;
|
|
85
92
|
}
|
|
86
93
|
declare class CancellationAbortController extends AbortController {
|
|
87
94
|
abort(reason: "rollback" | "interrupt"): void;
|
|
@@ -91,10 +98,8 @@ declare class StreamManagerImpl {
|
|
|
91
98
|
control: Record<string, CancellationAbortController>;
|
|
92
99
|
getQueue(runId: string, options: {
|
|
93
100
|
ifNotFound: "create";
|
|
101
|
+
resumable: boolean;
|
|
94
102
|
}): Queue;
|
|
95
|
-
getQueue(runId: string, options: {
|
|
96
|
-
ifNotFound: "ignore";
|
|
97
|
-
}): Queue | undefined;
|
|
98
103
|
getControl(runId: string): CancellationAbortController | undefined;
|
|
99
104
|
isLocked(runId: string): boolean;
|
|
100
105
|
lock(runId: string): AbortSignal;
|
|
@@ -272,11 +277,18 @@ export declare class Runs {
|
|
|
272
277
|
join(runId: string, threadId: string | undefined, options: {
|
|
273
278
|
ignore404?: boolean;
|
|
274
279
|
cancelOnDisconnect?: AbortSignal;
|
|
280
|
+
lastEventId: string | undefined;
|
|
275
281
|
}, auth: AuthContext | undefined): AsyncGenerator<{
|
|
282
|
+
id?: string;
|
|
276
283
|
event: string;
|
|
277
284
|
data: unknown;
|
|
278
285
|
}>;
|
|
279
|
-
publish(
|
|
286
|
+
publish(payload: {
|
|
287
|
+
runId: string;
|
|
288
|
+
event: string;
|
|
289
|
+
data: unknown;
|
|
290
|
+
resumable: boolean;
|
|
291
|
+
}): Promise<void>;
|
|
280
292
|
};
|
|
281
293
|
}
|
|
282
294
|
export declare class Crons {
|
package/dist/storage/ops.mjs
CHANGED
|
@@ -20,28 +20,58 @@ class TimeoutError extends Error {
|
|
|
20
20
|
class AbortError extends Error {
|
|
21
21
|
}
|
|
22
22
|
class Queue {
|
|
23
|
-
|
|
23
|
+
log = [];
|
|
24
24
|
listeners = [];
|
|
25
|
+
nextId = 0;
|
|
26
|
+
resumable = false;
|
|
27
|
+
constructor(options) {
|
|
28
|
+
this.resumable = options.resumable;
|
|
29
|
+
}
|
|
25
30
|
push(item) {
|
|
26
|
-
this.
|
|
27
|
-
for (const listener of this.listeners)
|
|
28
|
-
listener();
|
|
29
|
-
|
|
31
|
+
this.log.push(item);
|
|
32
|
+
for (const listener of this.listeners)
|
|
33
|
+
listener(this.nextId);
|
|
34
|
+
this.nextId += 1;
|
|
30
35
|
}
|
|
31
36
|
async get(options) {
|
|
32
|
-
if (this.
|
|
33
|
-
|
|
37
|
+
if (this.resumable) {
|
|
38
|
+
const lastEventId = options.lastEventId;
|
|
39
|
+
// Generator stores internal state of the read head index,
|
|
40
|
+
let targetId = lastEventId != null ? +lastEventId + 1 : null;
|
|
41
|
+
if (targetId == null ||
|
|
42
|
+
isNaN(targetId) ||
|
|
43
|
+
targetId < 0 ||
|
|
44
|
+
targetId >= this.log.length) {
|
|
45
|
+
targetId = null;
|
|
46
|
+
}
|
|
47
|
+
if (targetId != null)
|
|
48
|
+
return [String(targetId), this.log[targetId]];
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
if (this.log.length) {
|
|
52
|
+
const nextId = this.nextId - this.log.length;
|
|
53
|
+
const nextItem = this.log.shift();
|
|
54
|
+
return [String(nextId), nextItem];
|
|
55
|
+
}
|
|
34
56
|
}
|
|
35
57
|
let timeout = undefined;
|
|
36
58
|
let resolver = undefined;
|
|
37
59
|
const clean = new AbortController();
|
|
60
|
+
// listen to new item
|
|
38
61
|
return await new Promise((resolve, reject) => {
|
|
39
62
|
timeout = setTimeout(() => reject(new TimeoutError()), options.timeout);
|
|
40
63
|
resolver = resolve;
|
|
41
64
|
options.signal?.addEventListener("abort", () => reject(new AbortError()), { signal: clean.signal });
|
|
42
65
|
this.listeners.push(resolver);
|
|
43
66
|
})
|
|
44
|
-
.then(() =>
|
|
67
|
+
.then((idx) => {
|
|
68
|
+
if (this.resumable) {
|
|
69
|
+
return [String(idx), this.log[idx]];
|
|
70
|
+
}
|
|
71
|
+
const nextId = this.nextId - this.log.length;
|
|
72
|
+
const nextItem = this.log.shift();
|
|
73
|
+
return [String(nextId), nextItem];
|
|
74
|
+
})
|
|
45
75
|
.finally(() => {
|
|
46
76
|
this.listeners = this.listeners.filter((l) => l !== resolver);
|
|
47
77
|
clearTimeout(timeout);
|
|
@@ -59,12 +89,7 @@ class StreamManagerImpl {
|
|
|
59
89
|
control = {};
|
|
60
90
|
getQueue(runId, options) {
|
|
61
91
|
if (this.readers[runId] == null) {
|
|
62
|
-
|
|
63
|
-
this.readers[runId] = new Queue();
|
|
64
|
-
}
|
|
65
|
-
else {
|
|
66
|
-
return undefined;
|
|
67
|
-
}
|
|
92
|
+
this.readers[runId] = new Queue(options);
|
|
68
93
|
}
|
|
69
94
|
return this.readers[runId];
|
|
70
95
|
}
|
|
@@ -883,7 +908,7 @@ export class Runs {
|
|
|
883
908
|
});
|
|
884
909
|
}
|
|
885
910
|
static async wait(runId, threadId, auth) {
|
|
886
|
-
const runStream = Runs.Stream.join(runId, threadId, { ignore404: threadId == null }, auth);
|
|
911
|
+
const runStream = Runs.Stream.join(runId, threadId, { ignore404: threadId == null, lastEventId: undefined }, auth);
|
|
887
912
|
const lastChunk = new Promise(async (resolve, reject) => {
|
|
888
913
|
try {
|
|
889
914
|
let lastChunk = null;
|
|
@@ -1012,7 +1037,10 @@ export class Runs {
|
|
|
1012
1037
|
yield* conn.withGenerator(async function* (STORE) {
|
|
1013
1038
|
// TODO: what if we're joining an already completed run? Should we check before?
|
|
1014
1039
|
const signal = options?.cancelOnDisconnect;
|
|
1015
|
-
const queue = StreamManager.getQueue(runId, {
|
|
1040
|
+
const queue = StreamManager.getQueue(runId, {
|
|
1041
|
+
ifNotFound: "create",
|
|
1042
|
+
resumable: options.lastEventId != null,
|
|
1043
|
+
});
|
|
1016
1044
|
const [filters] = await handleAuthEvent(auth, "threads:read", {
|
|
1017
1045
|
thread_id: threadId,
|
|
1018
1046
|
});
|
|
@@ -1027,16 +1055,22 @@ export class Runs {
|
|
|
1027
1055
|
return;
|
|
1028
1056
|
}
|
|
1029
1057
|
}
|
|
1058
|
+
let lastEventId = options?.lastEventId;
|
|
1030
1059
|
while (!signal?.aborted) {
|
|
1031
1060
|
try {
|
|
1032
|
-
const message = await queue.get({
|
|
1061
|
+
const [id, message] = await queue.get({
|
|
1062
|
+
timeout: 500,
|
|
1063
|
+
signal,
|
|
1064
|
+
lastEventId,
|
|
1065
|
+
});
|
|
1066
|
+
lastEventId = id;
|
|
1033
1067
|
if (message.topic === `run:${runId}:control`) {
|
|
1034
1068
|
if (message.data === "done")
|
|
1035
1069
|
break;
|
|
1036
1070
|
}
|
|
1037
1071
|
else {
|
|
1038
1072
|
const streamTopic = message.topic.substring(`run:${runId}:stream:`.length);
|
|
1039
|
-
yield { event: streamTopic, data: message.data };
|
|
1073
|
+
yield { id, event: streamTopic, data: message.data };
|
|
1040
1074
|
}
|
|
1041
1075
|
}
|
|
1042
1076
|
catch (error) {
|
|
@@ -1058,9 +1092,15 @@ export class Runs {
|
|
|
1058
1092
|
}
|
|
1059
1093
|
});
|
|
1060
1094
|
}
|
|
1061
|
-
static async publish(
|
|
1062
|
-
const queue = StreamManager.getQueue(runId, {
|
|
1063
|
-
|
|
1095
|
+
static async publish(payload) {
|
|
1096
|
+
const queue = StreamManager.getQueue(payload.runId, {
|
|
1097
|
+
ifNotFound: "create",
|
|
1098
|
+
resumable: payload.resumable,
|
|
1099
|
+
});
|
|
1100
|
+
queue.push({
|
|
1101
|
+
topic: `run:${payload.runId}:stream:${payload.event}`,
|
|
1102
|
+
data: payload.data,
|
|
1103
|
+
});
|
|
1064
1104
|
}
|
|
1065
1105
|
};
|
|
1066
1106
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@langchain/langgraph-api",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.37",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": "^18.19.0 || >=20.16.0"
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"winston": "^3.17.0",
|
|
54
54
|
"winston-console-format": "^1.0.8",
|
|
55
55
|
"zod": "^3.23.8",
|
|
56
|
-
"@langchain/langgraph-ui": "0.0.
|
|
56
|
+
"@langchain/langgraph-ui": "0.0.37"
|
|
57
57
|
},
|
|
58
58
|
"peerDependencies": {
|
|
59
59
|
"@langchain/core": "^0.3.42",
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
},
|
|
70
70
|
"devDependencies": {
|
|
71
71
|
"typescript": "^5.5.4",
|
|
72
|
-
"@langchain/langgraph-sdk": "^0.0.
|
|
72
|
+
"@langchain/langgraph-sdk": "^0.0.77",
|
|
73
73
|
"@types/babel__code-frame": "^7.0.6",
|
|
74
74
|
"@types/node": "^22.2.0",
|
|
75
75
|
"@types/react": "^19.0.8",
|