@langgraph-js/pure-graph 3.2.0 → 3.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapter/fetch/index.js +128 -30
- package/dist/adapter/fetch/index.js.map +1 -1
- package/dist/adapter/fetch/utils.d.ts +13 -3
- package/dist/adapter/nextjs/index.js +1 -1
- package/dist/{checkpoint-C5AFBYE-.js → checkpoint-CY59Lr2q.js} +3 -3
- package/dist/checkpoint-CY59Lr2q.js.map +1 -0
- package/dist/{createEndpoint-C2KsYHDx.js → createEndpoint-vMmFiMSz.js} +20 -5
- package/dist/createEndpoint-vMmFiMSz.js.map +1 -0
- package/dist/createEndpoint.d.ts +2 -1
- package/dist/index.js +2 -2
- package/dist/queue/stream_queue.d.ts +3 -1
- package/dist/{queue-DySatFkr.js → queue-CUe5TDP1.js} +120 -16
- package/dist/queue-CUe5TDP1.js.map +1 -0
- package/dist/remote/index.js +1 -1
- package/dist/{shallow-checkpoint-BEhTdp7z.js → shallow-checkpoint-ImbLxYNR.js} +4 -4
- package/dist/shallow-checkpoint-ImbLxYNR.js.map +1 -0
- package/dist/{sqlite-adapter-oBA95xba.js → sqlite-adapter-CJXgit1j.js} +7 -12
- package/dist/sqlite-adapter-CJXgit1j.js.map +1 -0
- package/dist/storage/memory/queue.d.ts +6 -0
- package/dist/storage/redis/queue.d.ts +14 -1
- package/dist/{stream-pZfO6Y-p.js → stream-jYlUzTZO.js} +190 -62
- package/dist/stream-jYlUzTZO.js.map +1 -0
- package/package.json +2 -1
- package/dist/checkpoint-C5AFBYE-.js.map +0 -1
- package/dist/createEndpoint-C2KsYHDx.js.map +0 -1
- package/dist/queue-DySatFkr.js.map +0 -1
- package/dist/shallow-checkpoint-BEhTdp7z.js.map +0 -1
- package/dist/sqlite-adapter-oBA95xba.js.map +0 -1
- package/dist/stream-pZfO6Y-p.js.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { s as serialiseAsDict, a as streamState, g as getGraph, L as LangGraphGlobal } from '../../stream-
|
|
2
|
-
import { c as createEndpoint } from '../../createEndpoint-
|
|
1
|
+
import { s as serialiseAsDict, a as streamState, g as getGraph, L as LangGraphGlobal } from '../../stream-jYlUzTZO.js';
|
|
2
|
+
import { c as createEndpoint } from '../../createEndpoint-vMmFiMSz.js';
|
|
3
3
|
import z from 'zod';
|
|
4
4
|
import camelcaseKeys from 'camelcase-keys';
|
|
5
5
|
|
|
@@ -284,13 +284,15 @@ function errorResponse(error, status = 500) {
|
|
|
284
284
|
function createSSEStream(streamFn) {
|
|
285
285
|
let controller;
|
|
286
286
|
let isClosed = false;
|
|
287
|
+
const abortController = new AbortController();
|
|
287
288
|
const stream = new ReadableStream({
|
|
288
289
|
async start(ctrl) {
|
|
289
290
|
controller = ctrl;
|
|
290
291
|
const encoder = new TextEncoder();
|
|
291
292
|
const writer = {
|
|
293
|
+
signal: abortController.signal,
|
|
292
294
|
writeSSE: async ({ data, event, id }) => {
|
|
293
|
-
if (isClosed) {
|
|
295
|
+
if (isClosed || abortController.signal.aborted) {
|
|
294
296
|
return;
|
|
295
297
|
}
|
|
296
298
|
try {
|
|
@@ -308,7 +310,7 @@ function createSSEStream(streamFn) {
|
|
|
308
310
|
`;
|
|
309
311
|
controller.enqueue(encoder.encode(message));
|
|
310
312
|
} catch (error) {
|
|
311
|
-
if (!isClosed) {
|
|
313
|
+
if (!isClosed && !abortController.signal.aborted) {
|
|
312
314
|
throw error;
|
|
313
315
|
}
|
|
314
316
|
}
|
|
@@ -326,7 +328,9 @@ function createSSEStream(streamFn) {
|
|
|
326
328
|
try {
|
|
327
329
|
await streamFn(writer);
|
|
328
330
|
} catch (error) {
|
|
329
|
-
|
|
331
|
+
if (!abortController.signal.aborted) {
|
|
332
|
+
console.error("SSE stream error:", error);
|
|
333
|
+
}
|
|
330
334
|
} finally {
|
|
331
335
|
if (!isClosed) {
|
|
332
336
|
isClosed = true;
|
|
@@ -339,6 +343,9 @@ function createSSEStream(streamFn) {
|
|
|
339
343
|
},
|
|
340
344
|
cancel() {
|
|
341
345
|
isClosed = true;
|
|
346
|
+
if (!abortController.signal.aborted) {
|
|
347
|
+
abortController.abort("Client disconnected");
|
|
348
|
+
}
|
|
342
349
|
}
|
|
343
350
|
});
|
|
344
351
|
return new Response(stream, {
|
|
@@ -352,32 +359,42 @@ function createSSEStream(streamFn) {
|
|
|
352
359
|
function withHeartbeat(streamFn, heartbeatInterval = process.env.HEARTBEAT_INTERVAL ? parseInt(process.env.HEARTBEAT_INTERVAL) : 1500) {
|
|
353
360
|
return async (writer) => {
|
|
354
361
|
let heartbeatTimer = null;
|
|
355
|
-
|
|
362
|
+
let isCleaningUp = false;
|
|
363
|
+
const stopHeartbeat = () => {
|
|
356
364
|
if (heartbeatTimer) {
|
|
357
365
|
clearInterval(heartbeatTimer);
|
|
366
|
+
heartbeatTimer = null;
|
|
358
367
|
}
|
|
368
|
+
};
|
|
369
|
+
const startHeartbeat = () => {
|
|
370
|
+
stopHeartbeat();
|
|
359
371
|
heartbeatTimer = setInterval(async () => {
|
|
372
|
+
if (writer.signal.aborted || isCleaningUp) {
|
|
373
|
+
stopHeartbeat();
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
360
376
|
try {
|
|
361
377
|
await writer.writeSSE({ event: "ping", data: "{}" });
|
|
362
378
|
} catch (error) {
|
|
363
|
-
|
|
364
|
-
clearInterval(heartbeatTimer);
|
|
365
|
-
heartbeatTimer = null;
|
|
366
|
-
}
|
|
379
|
+
stopHeartbeat();
|
|
367
380
|
}
|
|
368
381
|
}, heartbeatInterval);
|
|
369
382
|
};
|
|
370
|
-
const
|
|
371
|
-
|
|
372
|
-
clearInterval(heartbeatTimer);
|
|
373
|
-
heartbeatTimer = null;
|
|
374
|
-
}
|
|
383
|
+
const abortHandler = () => {
|
|
384
|
+
stopHeartbeat();
|
|
375
385
|
};
|
|
386
|
+
writer.signal.addEventListener("abort", abortHandler);
|
|
376
387
|
const proxiedWriter = {
|
|
388
|
+
signal: writer.signal,
|
|
377
389
|
writeSSE: async (data) => {
|
|
390
|
+
if (writer.signal.aborted) {
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
378
393
|
stopHeartbeat();
|
|
379
394
|
await writer.writeSSE(data);
|
|
380
|
-
|
|
395
|
+
if (!writer.signal.aborted) {
|
|
396
|
+
startHeartbeat();
|
|
397
|
+
}
|
|
381
398
|
},
|
|
382
399
|
close: () => {
|
|
383
400
|
stopHeartbeat();
|
|
@@ -388,7 +405,9 @@ function withHeartbeat(streamFn, heartbeatInterval = process.env.HEARTBEAT_INTER
|
|
|
388
405
|
try {
|
|
389
406
|
await streamFn(proxiedWriter);
|
|
390
407
|
} finally {
|
|
408
|
+
isCleaningUp = true;
|
|
391
409
|
stopHeartbeat();
|
|
410
|
+
writer.signal.removeEventListener("abort", abortHandler);
|
|
392
411
|
}
|
|
393
412
|
};
|
|
394
413
|
}
|
|
@@ -715,12 +734,37 @@ async function streamRun(req, context) {
|
|
|
715
734
|
if (langgraphContext) {
|
|
716
735
|
Object.assign(payload.config.configurable, langgraphContext);
|
|
717
736
|
}
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
737
|
+
let generator = null;
|
|
738
|
+
let isCleaningUp = false;
|
|
739
|
+
const abortHandler = () => {
|
|
740
|
+
isCleaningUp = true;
|
|
741
|
+
};
|
|
742
|
+
writer.signal.addEventListener("abort", abortHandler);
|
|
743
|
+
try {
|
|
744
|
+
generator = client.runs.stream(
|
|
745
|
+
thread_id,
|
|
746
|
+
payload.assistant_id,
|
|
747
|
+
camelcaseKeys(payload)
|
|
748
|
+
);
|
|
749
|
+
for await (const { event, data } of generator) {
|
|
750
|
+
if (isCleaningUp || writer.signal.aborted) {
|
|
751
|
+
break;
|
|
752
|
+
}
|
|
753
|
+
await writer.writeSSE({ data: serialiseAsDict(data) ?? "", event });
|
|
754
|
+
}
|
|
755
|
+
} catch (error) {
|
|
756
|
+
if (!writer.signal.aborted && !isCleaningUp) {
|
|
757
|
+
throw error;
|
|
758
|
+
}
|
|
759
|
+
} finally {
|
|
760
|
+
writer.signal.removeEventListener("abort", abortHandler);
|
|
761
|
+
if (generator) {
|
|
762
|
+
try {
|
|
763
|
+
await generator.return(void 0);
|
|
764
|
+
} catch (e) {
|
|
765
|
+
}
|
|
766
|
+
generator = null;
|
|
767
|
+
}
|
|
724
768
|
}
|
|
725
769
|
})
|
|
726
770
|
);
|
|
@@ -737,19 +781,33 @@ async function joinRunStream(req, context) {
|
|
|
737
781
|
return createSSEStream(
|
|
738
782
|
withHeartbeat(async (writer) => {
|
|
739
783
|
const controller = new AbortController();
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
784
|
+
let generator = null;
|
|
785
|
+
let isCleaningUp = false;
|
|
786
|
+
const abortHandlers = [];
|
|
787
|
+
const cleanup = () => {
|
|
788
|
+
controller.abort("Client disconnected");
|
|
789
|
+
};
|
|
790
|
+
if (req.signal) {
|
|
791
|
+
req.signal.addEventListener("abort", cleanup);
|
|
792
|
+
abortHandlers.push({ signal: req.signal, handler: cleanup });
|
|
745
793
|
}
|
|
794
|
+
const writerAbortHandler = () => {
|
|
795
|
+
isCleaningUp = true;
|
|
796
|
+
controller.abort("SSE stream closed");
|
|
797
|
+
};
|
|
798
|
+
writer.signal.addEventListener("abort", writerAbortHandler);
|
|
799
|
+
abortHandlers.push({ signal: writer.signal, handler: writerAbortHandler });
|
|
746
800
|
try {
|
|
747
|
-
|
|
801
|
+
generator = client.runs.joinStream(thread_id, run_id, {
|
|
748
802
|
signal: controller.signal,
|
|
749
803
|
cancelOnDisconnect: cancel_on_disconnect,
|
|
750
804
|
lastEventId: last_event_id,
|
|
751
805
|
streamMode: stream_mode ? [stream_mode] : void 0
|
|
752
|
-
})
|
|
806
|
+
});
|
|
807
|
+
for await (const { event, data, id } of generator) {
|
|
808
|
+
if (isCleaningUp || writer.signal.aborted || controller.signal.aborted) {
|
|
809
|
+
break;
|
|
810
|
+
}
|
|
753
811
|
await writer.writeSSE({
|
|
754
812
|
data: serialiseAsDict(data) ?? "",
|
|
755
813
|
event,
|
|
@@ -757,7 +815,8 @@ async function joinRunStream(req, context) {
|
|
|
757
815
|
});
|
|
758
816
|
}
|
|
759
817
|
} catch (error) {
|
|
760
|
-
|
|
818
|
+
const isAbortError = controller.signal.aborted || writer.signal.aborted;
|
|
819
|
+
if (!isAbortError && !(error instanceof Error && error.message.includes("user cancel"))) {
|
|
761
820
|
console.error("Join stream error:", error);
|
|
762
821
|
await writer.writeSSE({
|
|
763
822
|
event: "error",
|
|
@@ -766,6 +825,20 @@ async function joinRunStream(req, context) {
|
|
|
766
825
|
})
|
|
767
826
|
});
|
|
768
827
|
}
|
|
828
|
+
} finally {
|
|
829
|
+
for (const { signal, handler } of abortHandlers) {
|
|
830
|
+
try {
|
|
831
|
+
signal.removeEventListener("abort", handler);
|
|
832
|
+
} catch (e) {
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
if (generator) {
|
|
836
|
+
try {
|
|
837
|
+
await generator.return(void 0);
|
|
838
|
+
} catch (e) {
|
|
839
|
+
}
|
|
840
|
+
generator = null;
|
|
841
|
+
}
|
|
769
842
|
}
|
|
770
843
|
})
|
|
771
844
|
);
|
|
@@ -819,6 +892,7 @@ async function createRun(req, context) {
|
|
|
819
892
|
const threads = client.threads;
|
|
820
893
|
const run = await threads.createRun(thread_id, payload.assistant_id, camelPayload);
|
|
821
894
|
(async () => {
|
|
895
|
+
let queueCleared = false;
|
|
822
896
|
try {
|
|
823
897
|
for await (const _ of streamState(threads, run, camelPayload, {
|
|
824
898
|
attempt: 0,
|
|
@@ -827,6 +901,14 @@ async function createRun(req, context) {
|
|
|
827
901
|
}
|
|
828
902
|
} catch (error) {
|
|
829
903
|
console.error("Background run error:", error);
|
|
904
|
+
} finally {
|
|
905
|
+
if (!queueCleared) {
|
|
906
|
+
queueCleared = true;
|
|
907
|
+
try {
|
|
908
|
+
await LangGraphGlobal.globalMessageQueue.removeQueue(run.run_id);
|
|
909
|
+
} catch (e) {
|
|
910
|
+
}
|
|
911
|
+
}
|
|
830
912
|
}
|
|
831
913
|
})();
|
|
832
914
|
return jsonResponse(run, 200, {
|
|
@@ -955,6 +1037,7 @@ async function createStatelessRun(req, context) {
|
|
|
955
1037
|
camelPayload.temporary = true;
|
|
956
1038
|
const run = await threads.createRun(thread.thread_id, payload.assistant_id, camelPayload);
|
|
957
1039
|
(async () => {
|
|
1040
|
+
let queueCleared = false;
|
|
958
1041
|
try {
|
|
959
1042
|
for await (const _ of streamState(threads, run, camelPayload, {
|
|
960
1043
|
attempt: 0,
|
|
@@ -969,6 +1052,13 @@ async function createStatelessRun(req, context) {
|
|
|
969
1052
|
} catch (e) {
|
|
970
1053
|
console.error("Error cleaning up temporary thread:", e);
|
|
971
1054
|
}
|
|
1055
|
+
if (!queueCleared) {
|
|
1056
|
+
queueCleared = true;
|
|
1057
|
+
try {
|
|
1058
|
+
await LangGraphGlobal.globalMessageQueue.removeQueue(run.run_id);
|
|
1059
|
+
} catch (e) {
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
972
1062
|
}
|
|
973
1063
|
})();
|
|
974
1064
|
return jsonResponse(run, 200, {
|
|
@@ -1092,6 +1182,7 @@ async function createBatchRuns(req, context) {
|
|
|
1092
1182
|
camelPayload.temporary = true;
|
|
1093
1183
|
const run = await threads.createRun(thread.thread_id, payload.assistant_id, camelPayload);
|
|
1094
1184
|
(async () => {
|
|
1185
|
+
let queueCleared = false;
|
|
1095
1186
|
try {
|
|
1096
1187
|
for await (const _ of streamState(threads, run, camelPayload, {
|
|
1097
1188
|
attempt: 0,
|
|
@@ -1106,6 +1197,13 @@ async function createBatchRuns(req, context) {
|
|
|
1106
1197
|
} catch (e) {
|
|
1107
1198
|
console.error("Error cleaning up temporary thread:", e);
|
|
1108
1199
|
}
|
|
1200
|
+
if (!queueCleared) {
|
|
1201
|
+
queueCleared = true;
|
|
1202
|
+
try {
|
|
1203
|
+
await LangGraphGlobal.globalMessageQueue.removeQueue(run.run_id);
|
|
1204
|
+
} catch (e) {
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
1109
1207
|
}
|
|
1110
1208
|
})();
|
|
1111
1209
|
return { thread_id: thread.thread_id, run_id: run.run_id };
|