@langgraph-js/pure-graph 2.10.0 → 3.0.0
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/assistants.d.ts +42 -0
- package/dist/adapter/fetch/endpoint.d.ts +25 -0
- package/dist/adapter/fetch/index.d.ts +5 -3
- package/dist/adapter/fetch/index.js +858 -25
- package/dist/adapter/fetch/index.js.map +1 -1
- package/dist/adapter/fetch/runs-extended.d.ts +21 -0
- package/dist/adapter/fetch/runs-stateless.d.ts +28 -0
- package/dist/adapter/fetch/runs.d.ts +0 -4
- package/dist/adapter/fetch/threads.d.ts +36 -0
- package/dist/adapter/nextjs/index.js +1 -1
- package/dist/adapter/zod.d.ts +292 -1
- package/dist/agents/ask_subagents.d.ts +1 -1
- package/dist/createEndpoint-BViLxrhh.js +208 -0
- package/dist/createEndpoint-BViLxrhh.js.map +1 -0
- package/dist/createEndpoint.d.ts +25 -0
- package/dist/index.js +2 -2
- package/dist/queue/stream_queue.d.ts +2 -2
- package/dist/queue-CtVch_az.js +153 -0
- package/dist/queue-CtVch_az.js.map +1 -0
- package/dist/remote/index.js +158 -0
- package/dist/remote/index.js.map +1 -0
- package/dist/remote-threads-CrG03ZS7.js +255 -0
- package/dist/remote-threads-CrG03ZS7.js.map +1 -0
- package/dist/storage/index.d.ts +1 -1
- package/dist/storage/kysely/index.d.ts +1 -0
- package/dist/storage/kysely/remote-threads.d.ts +124 -0
- package/dist/storage/kysely/threads.d.ts +26 -2
- package/dist/storage/kysely/types.d.ts +10 -0
- package/dist/storage/memory/threads.d.ts +27 -1
- package/dist/storage/redis/queue.d.ts +14 -11
- package/dist/storage/remote/fetch.d.ts +20 -0
- package/dist/storage/remote/remote-server.d.ts +17 -0
- package/dist/storage/remote/server.d.ts +11 -0
- package/dist/storage/remote/types.d.ts +121 -0
- package/dist/{stream-D0YD2Pjq.js → stream-umoA6h4q.js} +502 -78
- package/dist/stream-umoA6h4q.js.map +1 -0
- package/dist/threads/index.d.ts +25 -1
- package/dist/types.d.ts +53 -1
- package/package.json +10 -4
- package/dist/createEndpoint-CN_RHDEd.js +0 -122
- package/dist/createEndpoint-CN_RHDEd.js.map +0 -1
- package/dist/queue-D6tEGCGs.js +0 -146
- package/dist/queue-D6tEGCGs.js.map +0 -1
- package/dist/stream-D0YD2Pjq.js.map +0 -1
|
@@ -3,6 +3,7 @@ import { Command, Send } from '@langchain/langgraph';
|
|
|
3
3
|
import { concat } from '@langchain/core/utils/stream';
|
|
4
4
|
import { EventEmitter } from 'eventemitter3';
|
|
5
5
|
import { load } from '@langchain/core/load';
|
|
6
|
+
import { v7 } from 'uuid';
|
|
6
7
|
import { MemorySaver } from '@langchain/langgraph-checkpoint';
|
|
7
8
|
|
|
8
9
|
const getLangGraphCommand = (command) => {
|
|
@@ -353,10 +354,13 @@ class StreamQueueManager {
|
|
|
353
354
|
* Clear queue with specified id
|
|
354
355
|
* @param id 队列 ID / Queue ID
|
|
355
356
|
*/
|
|
356
|
-
clearQueue(id) {
|
|
357
|
+
async clearQueue(id) {
|
|
357
358
|
const queue = this.queues.get(id);
|
|
358
359
|
if (queue) {
|
|
359
|
-
queue.clear();
|
|
360
|
+
const result = queue.clear();
|
|
361
|
+
if (result instanceof Promise) {
|
|
362
|
+
await result;
|
|
363
|
+
}
|
|
360
364
|
}
|
|
361
365
|
}
|
|
362
366
|
/**
|
|
@@ -442,7 +446,7 @@ class KyselyThreadsManager {
|
|
|
442
446
|
await this.adapter.createIndexes(this.db);
|
|
443
447
|
}
|
|
444
448
|
async create(payload) {
|
|
445
|
-
const threadId = payload?.threadId ||
|
|
449
|
+
const threadId = payload?.threadId || v7();
|
|
446
450
|
if (payload?.ifExists === "raise") {
|
|
447
451
|
const existing = await this.db.selectFrom("threads").select("thread_id").where("thread_id", "=", threadId).executeTakeFirst();
|
|
448
452
|
if (existing) {
|
|
@@ -472,9 +476,38 @@ class KyselyThreadsManager {
|
|
|
472
476
|
};
|
|
473
477
|
}
|
|
474
478
|
async search(query) {
|
|
475
|
-
let queryBuilder = this.db.selectFrom("threads")
|
|
476
|
-
|
|
477
|
-
|
|
479
|
+
let queryBuilder = this.db.selectFrom("threads");
|
|
480
|
+
let selectedFields;
|
|
481
|
+
if (query?.select) {
|
|
482
|
+
selectedFields = new Set(query.select);
|
|
483
|
+
} else if (query?.withoutDetails) {
|
|
484
|
+
selectedFields = /* @__PURE__ */ new Set(["thread_id", "created_at", "updated_at", "metadata", "status"]);
|
|
485
|
+
} else {
|
|
486
|
+
selectedFields = /* @__PURE__ */ new Set([
|
|
487
|
+
"thread_id",
|
|
488
|
+
"created_at",
|
|
489
|
+
"updated_at",
|
|
490
|
+
"metadata",
|
|
491
|
+
"status",
|
|
492
|
+
"values",
|
|
493
|
+
"interrupts"
|
|
494
|
+
]);
|
|
495
|
+
}
|
|
496
|
+
const selections = [];
|
|
497
|
+
if (selectedFields.has("thread_id")) selections.push("thread_id");
|
|
498
|
+
if (selectedFields.has("created_at")) selections.push("created_at");
|
|
499
|
+
if (selectedFields.has("updated_at")) selections.push("updated_at");
|
|
500
|
+
if (selectedFields.has("metadata")) selections.push("metadata");
|
|
501
|
+
if (selectedFields.has("status")) selections.push("status");
|
|
502
|
+
if (selectedFields.has("values")) selections.push("values");
|
|
503
|
+
if (selectedFields.has("interrupts")) selections.push("interrupts");
|
|
504
|
+
if (selections.length > 0) {
|
|
505
|
+
queryBuilder = queryBuilder.select(selections);
|
|
506
|
+
} else {
|
|
507
|
+
queryBuilder = queryBuilder.selectAll();
|
|
508
|
+
}
|
|
509
|
+
if (query?.ids && query.ids.length > 0) {
|
|
510
|
+
queryBuilder = queryBuilder.where("thread_id", "in", query.ids);
|
|
478
511
|
}
|
|
479
512
|
if (query?.status) {
|
|
480
513
|
queryBuilder = queryBuilder.where("status", "=", query.status);
|
|
@@ -484,6 +517,11 @@ class KyselyThreadsManager {
|
|
|
484
517
|
queryBuilder = queryBuilder.where(this.adapter.buildJsonQuery(this.db, "metadata", key, value));
|
|
485
518
|
}
|
|
486
519
|
}
|
|
520
|
+
if (query?.values) {
|
|
521
|
+
queryBuilder = queryBuilder.where((eb) => {
|
|
522
|
+
return eb("values", "=", this.adapter.jsonToDb(query.values));
|
|
523
|
+
});
|
|
524
|
+
}
|
|
487
525
|
if (query?.sortBy) {
|
|
488
526
|
const order = query.sortOrder === "desc" ? "desc" : "asc";
|
|
489
527
|
queryBuilder = queryBuilder.orderBy(query.sortBy, order);
|
|
@@ -495,15 +533,19 @@ class KyselyThreadsManager {
|
|
|
495
533
|
}
|
|
496
534
|
}
|
|
497
535
|
const rows = await queryBuilder.execute();
|
|
498
|
-
return rows.map((row) =>
|
|
499
|
-
thread_id: row.thread_id
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
536
|
+
return rows.map((row) => {
|
|
537
|
+
const result = { thread_id: row.thread_id };
|
|
538
|
+
if (selectedFields.has("created_at"))
|
|
539
|
+
result.created_at = this.adapter.dbToDate(row.created_at).toISOString();
|
|
540
|
+
if (selectedFields.has("updated_at"))
|
|
541
|
+
result.updated_at = this.adapter.dbToDate(row.updated_at).toISOString();
|
|
542
|
+
if (selectedFields.has("metadata")) result.metadata = this.adapter.dbToJson(row.metadata);
|
|
543
|
+
if (selectedFields.has("status")) result.status = row.status;
|
|
544
|
+
if (selectedFields.has("values"))
|
|
545
|
+
result.values = row.values ? this.adapter.dbToJson(row.values) : null;
|
|
546
|
+
if (selectedFields.has("interrupts")) result.interrupts = this.adapter.dbToJson(row.interrupts);
|
|
547
|
+
return result;
|
|
548
|
+
});
|
|
507
549
|
}
|
|
508
550
|
async get(threadId) {
|
|
509
551
|
const row = await this.db.selectFrom("threads").selectAll().where("thread_id", "=", threadId).executeTakeFirst();
|
|
@@ -542,6 +584,12 @@ class KyselyThreadsManager {
|
|
|
542
584
|
}
|
|
543
585
|
await this.db.updateTable("threads").set(updates).where("thread_id", "=", threadId).execute();
|
|
544
586
|
}
|
|
587
|
+
async delete(threadId) {
|
|
588
|
+
const result = await this.db.deleteFrom("threads").where("thread_id", "=", threadId).executeTakeFirst();
|
|
589
|
+
if (result.numDeletedRows === 0n) {
|
|
590
|
+
throw new Error(`Thread with ID ${threadId} not found.`);
|
|
591
|
+
}
|
|
592
|
+
}
|
|
545
593
|
async updateState(threadId, thread) {
|
|
546
594
|
const targetThread = await this.get(threadId);
|
|
547
595
|
if (targetThread.status === "busy") {
|
|
@@ -563,14 +611,8 @@ class KyselyThreadsManager {
|
|
|
563
611
|
await this.set(threadId, { values: JSON.parse(serialiseAsDict(graphState.values)) });
|
|
564
612
|
return nextConfig;
|
|
565
613
|
}
|
|
566
|
-
async delete(threadId) {
|
|
567
|
-
const result = await this.db.deleteFrom("threads").where("thread_id", "=", threadId).executeTakeFirst();
|
|
568
|
-
if (result.numDeletedRows === 0n) {
|
|
569
|
-
throw new Error(`Thread with ID ${threadId} not found.`);
|
|
570
|
-
}
|
|
571
|
-
}
|
|
572
614
|
async createRun(threadId, assistantId, payload) {
|
|
573
|
-
const runId =
|
|
615
|
+
const runId = v7();
|
|
574
616
|
const now = /* @__PURE__ */ new Date();
|
|
575
617
|
const metadata = payload?.metadata ?? {};
|
|
576
618
|
await this.db.insertInto("runs").values({
|
|
@@ -636,6 +678,146 @@ class KyselyThreadsManager {
|
|
|
636
678
|
}
|
|
637
679
|
await this.db.updateTable("runs").set(updates).where("run_id", "=", runId).execute();
|
|
638
680
|
}
|
|
681
|
+
// New methods for Threads API
|
|
682
|
+
async count(query) {
|
|
683
|
+
const threads = await this.search(query);
|
|
684
|
+
return threads.length;
|
|
685
|
+
}
|
|
686
|
+
async patch(threadId, updates) {
|
|
687
|
+
const existing = await this.db.selectFrom("threads").selectAll().where("thread_id", "=", threadId).executeTakeFirst();
|
|
688
|
+
if (!existing) {
|
|
689
|
+
throw new Error(`Thread with ID ${threadId} not found.`);
|
|
690
|
+
}
|
|
691
|
+
const patchUpdates = {
|
|
692
|
+
updated_at: this.adapter.dateToDb(/* @__PURE__ */ new Date())
|
|
693
|
+
};
|
|
694
|
+
if (updates.metadata !== void 0) {
|
|
695
|
+
const existingMetadata = this.adapter.dbToJson(existing.metadata) || {};
|
|
696
|
+
patchUpdates.metadata = this.adapter.jsonToDb({ ...existingMetadata, ...updates.metadata });
|
|
697
|
+
}
|
|
698
|
+
if (updates.status !== void 0) {
|
|
699
|
+
patchUpdates.status = updates.status;
|
|
700
|
+
}
|
|
701
|
+
if (updates.values !== void 0) {
|
|
702
|
+
patchUpdates.values = updates.values ? this.adapter.jsonToDb(updates.values) : null;
|
|
703
|
+
}
|
|
704
|
+
if (updates.interrupts !== void 0) {
|
|
705
|
+
patchUpdates.interrupts = this.adapter.jsonToDb(updates.interrupts);
|
|
706
|
+
}
|
|
707
|
+
await this.db.updateTable("threads").set(patchUpdates).where("thread_id", "=", threadId).execute();
|
|
708
|
+
return await this.get(threadId);
|
|
709
|
+
}
|
|
710
|
+
async getState(threadId, options) {
|
|
711
|
+
const thread = await this.get(threadId);
|
|
712
|
+
if (options?.checkpointId) {
|
|
713
|
+
const checkpoint = await this.db.selectFrom("checkpoints").selectAll().where("checkpoint_id", "=", options.checkpointId).where("thread_id", "=", threadId).executeTakeFirst();
|
|
714
|
+
if (!checkpoint) {
|
|
715
|
+
throw new Error(`Checkpoint with ID ${options.checkpointId} not found for thread ${threadId}`);
|
|
716
|
+
}
|
|
717
|
+
return {
|
|
718
|
+
values: this.adapter.dbToJson(checkpoint.values),
|
|
719
|
+
next: this.adapter.dbToJson(checkpoint.next),
|
|
720
|
+
metadata: this.adapter.dbToJson(checkpoint.metadata),
|
|
721
|
+
checkpoint: {
|
|
722
|
+
/** @ts-ignore */
|
|
723
|
+
id: checkpoint.checkpoint_id,
|
|
724
|
+
thread_id: threadId,
|
|
725
|
+
parent_checkpoint_id: null,
|
|
726
|
+
checkpoint_ns: "",
|
|
727
|
+
metadata: this.adapter.dbToJson(checkpoint.metadata),
|
|
728
|
+
created_at: this.adapter.dbToDate(checkpoint.created_at).toISOString()
|
|
729
|
+
},
|
|
730
|
+
created_at: this.adapter.dbToDate(checkpoint.created_at).toISOString(),
|
|
731
|
+
parent_checkpoint: null,
|
|
732
|
+
tasks: []
|
|
733
|
+
};
|
|
734
|
+
}
|
|
735
|
+
const state = {
|
|
736
|
+
values: thread.values || {},
|
|
737
|
+
next: [],
|
|
738
|
+
metadata: thread.metadata,
|
|
739
|
+
/**@ts-ignore 没有查询 checkpointer */
|
|
740
|
+
checkpoint: null,
|
|
741
|
+
created_at: thread.created_at,
|
|
742
|
+
parent_checkpoint: null,
|
|
743
|
+
tasks: []
|
|
744
|
+
};
|
|
745
|
+
return state;
|
|
746
|
+
}
|
|
747
|
+
async getStateHistory(threadId, options) {
|
|
748
|
+
let queryBuilder = this.db.selectFrom("checkpoints").selectAll().where("thread_id", "=", threadId).orderBy("created_at", "asc");
|
|
749
|
+
const checkpoints = await queryBuilder.execute();
|
|
750
|
+
let history = checkpoints.map(
|
|
751
|
+
(cp) => ({
|
|
752
|
+
values: this.adapter.dbToJson(cp.values),
|
|
753
|
+
next: this.adapter.dbToJson(cp.next),
|
|
754
|
+
metadata: this.adapter.dbToJson(cp.metadata),
|
|
755
|
+
checkpoint: {
|
|
756
|
+
thread_id: threadId,
|
|
757
|
+
checkpoint_ns: "",
|
|
758
|
+
checkpoint_id: cp.checkpoint_id,
|
|
759
|
+
checkpoint_map: null
|
|
760
|
+
},
|
|
761
|
+
created_at: this.adapter.dbToDate(cp.created_at).toISOString(),
|
|
762
|
+
parent_checkpoint: null,
|
|
763
|
+
tasks: []
|
|
764
|
+
})
|
|
765
|
+
);
|
|
766
|
+
if (options?.before) {
|
|
767
|
+
const beforeIndex = checkpoints.findIndex((c) => c.checkpoint_id === options.before);
|
|
768
|
+
if (beforeIndex !== -1) {
|
|
769
|
+
history = history.slice(beforeIndex + 1);
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
if (options?.limit) {
|
|
773
|
+
history = history.slice(0, options.limit);
|
|
774
|
+
}
|
|
775
|
+
return history;
|
|
776
|
+
}
|
|
777
|
+
async copy(threadId) {
|
|
778
|
+
const originalThread = await this.get(threadId);
|
|
779
|
+
const newThreadId = v7();
|
|
780
|
+
const now = /* @__PURE__ */ new Date();
|
|
781
|
+
await this.db.insertInto("threads").values({
|
|
782
|
+
thread_id: newThreadId,
|
|
783
|
+
created_at: this.adapter.dateToDb(now),
|
|
784
|
+
updated_at: this.adapter.dateToDb(now),
|
|
785
|
+
metadata: this.adapter.jsonToDb(originalThread.metadata),
|
|
786
|
+
status: originalThread.status,
|
|
787
|
+
values: originalThread.values ? this.adapter.jsonToDb(originalThread.values) : null,
|
|
788
|
+
interrupts: this.adapter.jsonToDb(originalThread.interrupts)
|
|
789
|
+
}).execute();
|
|
790
|
+
const checkpoints = await this.db.selectFrom("checkpoints").selectAll().where("thread_id", "=", threadId).orderBy("created_at", "asc").execute();
|
|
791
|
+
for (const cp of checkpoints) {
|
|
792
|
+
await this.db.insertInto("checkpoints").values({
|
|
793
|
+
checkpoint_id: v7(),
|
|
794
|
+
thread_id: newThreadId,
|
|
795
|
+
values: cp.values,
|
|
796
|
+
next: cp.next,
|
|
797
|
+
config: cp.config,
|
|
798
|
+
created_at: cp.created_at,
|
|
799
|
+
metadata: cp.metadata
|
|
800
|
+
}).execute();
|
|
801
|
+
}
|
|
802
|
+
return {
|
|
803
|
+
...originalThread,
|
|
804
|
+
thread_id: newThreadId,
|
|
805
|
+
created_at: now.toISOString(),
|
|
806
|
+
updated_at: now.toISOString()
|
|
807
|
+
};
|
|
808
|
+
}
|
|
809
|
+
// Helper method to save checkpoint (used internally)
|
|
810
|
+
async saveCheckpoint(threadId, values, next, config, metadata) {
|
|
811
|
+
await this.db.insertInto("checkpoints").values({
|
|
812
|
+
checkpoint_id: v7(),
|
|
813
|
+
thread_id: threadId,
|
|
814
|
+
values: this.adapter.jsonToDb(values),
|
|
815
|
+
next: this.adapter.jsonToDb(next),
|
|
816
|
+
config: this.adapter.jsonToDb(config),
|
|
817
|
+
created_at: this.adapter.dateToDb(/* @__PURE__ */ new Date()),
|
|
818
|
+
metadata: this.adapter.jsonToDb(metadata || {})
|
|
819
|
+
}).execute();
|
|
820
|
+
}
|
|
639
821
|
}
|
|
640
822
|
|
|
641
823
|
class EventMessage {
|
|
@@ -686,27 +868,33 @@ class MemoryStreamQueue extends BaseStreamQueue {
|
|
|
686
868
|
let queue = [];
|
|
687
869
|
let pendingResolve = null;
|
|
688
870
|
let isStreamEnded = false;
|
|
689
|
-
|
|
690
|
-
return;
|
|
691
|
-
}
|
|
871
|
+
let isCleanupDone = false;
|
|
692
872
|
const handleData = async (item) => {
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
pendingResolve
|
|
700
|
-
|
|
873
|
+
try {
|
|
874
|
+
const data = this.compressMessages ? await this.decodeData(item) : item;
|
|
875
|
+
queue.push(data);
|
|
876
|
+
if (data.event === "__stream_end__" || data.event === "__stream_error__" || data.event === "__stream_cancel__") {
|
|
877
|
+
setTimeout(() => {
|
|
878
|
+
isStreamEnded = true;
|
|
879
|
+
if (pendingResolve) {
|
|
880
|
+
pendingResolve();
|
|
881
|
+
pendingResolve = null;
|
|
882
|
+
}
|
|
883
|
+
}, 300);
|
|
884
|
+
if (data.event === "__stream_cancel__") {
|
|
885
|
+
await this.cancel();
|
|
701
886
|
}
|
|
702
|
-
}, 300);
|
|
703
|
-
if (data.event === "__stream_cancel__") {
|
|
704
|
-
await this.cancel();
|
|
705
887
|
}
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
888
|
+
if (pendingResolve) {
|
|
889
|
+
pendingResolve();
|
|
890
|
+
pendingResolve = null;
|
|
891
|
+
}
|
|
892
|
+
} catch (error) {
|
|
893
|
+
console.error("Error in handleData:", error);
|
|
894
|
+
if (pendingResolve) {
|
|
895
|
+
pendingResolve();
|
|
896
|
+
pendingResolve = null;
|
|
897
|
+
}
|
|
710
898
|
}
|
|
711
899
|
};
|
|
712
900
|
this.on("dataChange", handleData);
|
|
@@ -718,7 +906,28 @@ class MemoryStreamQueue extends BaseStreamQueue {
|
|
|
718
906
|
}
|
|
719
907
|
};
|
|
720
908
|
this.cancelSignal.signal.addEventListener("abort", abortHandler);
|
|
909
|
+
const cleanup = () => {
|
|
910
|
+
if (isCleanupDone) return;
|
|
911
|
+
isCleanupDone = true;
|
|
912
|
+
try {
|
|
913
|
+
this.off("dataChange", handleData);
|
|
914
|
+
} catch (e) {
|
|
915
|
+
console.error("Error removing dataChange listener:", e);
|
|
916
|
+
}
|
|
917
|
+
try {
|
|
918
|
+
this.cancelSignal.signal.removeEventListener("abort", abortHandler);
|
|
919
|
+
} catch (e) {
|
|
920
|
+
console.error("Error removing abort listener:", e);
|
|
921
|
+
}
|
|
922
|
+
if (pendingResolve) {
|
|
923
|
+
pendingResolve();
|
|
924
|
+
pendingResolve = null;
|
|
925
|
+
}
|
|
926
|
+
};
|
|
721
927
|
try {
|
|
928
|
+
if (this.cancelSignal.signal.aborted) {
|
|
929
|
+
return;
|
|
930
|
+
}
|
|
722
931
|
while (!isStreamEnded && !this.cancelSignal.signal.aborted) {
|
|
723
932
|
if (queue.length > 0) {
|
|
724
933
|
for (const item of queue) {
|
|
@@ -732,8 +941,7 @@ class MemoryStreamQueue extends BaseStreamQueue {
|
|
|
732
941
|
}
|
|
733
942
|
}
|
|
734
943
|
} finally {
|
|
735
|
-
|
|
736
|
-
this.cancelSignal.signal.removeEventListener("abort", abortHandler);
|
|
944
|
+
cleanup();
|
|
737
945
|
}
|
|
738
946
|
}
|
|
739
947
|
async getAll() {
|
|
@@ -750,7 +958,7 @@ class MemoryStreamQueue extends BaseStreamQueue {
|
|
|
750
958
|
await this.push(new CancelEventMessage());
|
|
751
959
|
}
|
|
752
960
|
async copyToQueue(toId, ttl) {
|
|
753
|
-
const data = this.data;
|
|
961
|
+
const data = this.data.slice();
|
|
754
962
|
const queue = new MemoryStreamQueue(toId, this.compressMessages, ttl ?? this.ttl);
|
|
755
963
|
queue.data = data;
|
|
756
964
|
return queue;
|
|
@@ -759,11 +967,12 @@ class MemoryStreamQueue extends BaseStreamQueue {
|
|
|
759
967
|
|
|
760
968
|
class MemoryThreadsManager {
|
|
761
969
|
threads = [];
|
|
970
|
+
checkpoints = /* @__PURE__ */ new Map();
|
|
762
971
|
async setup() {
|
|
763
972
|
return;
|
|
764
973
|
}
|
|
765
974
|
async create(payload) {
|
|
766
|
-
const threadId = payload?.threadId ||
|
|
975
|
+
const threadId = payload?.threadId || v7();
|
|
767
976
|
if (payload?.ifExists === "raise" && this.threads.some((t) => t.thread_id === threadId)) {
|
|
768
977
|
throw new Error(`Thread with ID ${threadId} already exists.`);
|
|
769
978
|
}
|
|
@@ -776,11 +985,15 @@ class MemoryThreadsManager {
|
|
|
776
985
|
values: null,
|
|
777
986
|
interrupts: {}
|
|
778
987
|
};
|
|
988
|
+
this.checkpoints.set(threadId, []);
|
|
779
989
|
this.threads.push(thread);
|
|
780
990
|
return thread;
|
|
781
991
|
}
|
|
782
992
|
async search(query) {
|
|
783
993
|
let filteredThreads = [...this.threads];
|
|
994
|
+
if (query?.ids && query.ids.length > 0) {
|
|
995
|
+
filteredThreads = filteredThreads.filter((t) => query.ids.includes(t.thread_id));
|
|
996
|
+
}
|
|
784
997
|
if (query?.status) {
|
|
785
998
|
filteredThreads = filteredThreads.filter((t) => t.status === query.status);
|
|
786
999
|
}
|
|
@@ -793,11 +1006,21 @@ class MemoryThreadsManager {
|
|
|
793
1006
|
}
|
|
794
1007
|
}
|
|
795
1008
|
}
|
|
1009
|
+
if (query?.values) {
|
|
1010
|
+
filteredThreads = filteredThreads.filter((t) => {
|
|
1011
|
+
if (!t.values) return false;
|
|
1012
|
+
return this.deepEqual(t.values, query.values);
|
|
1013
|
+
});
|
|
1014
|
+
}
|
|
796
1015
|
if (query?.sortBy) {
|
|
797
1016
|
filteredThreads.sort((a, b) => {
|
|
798
1017
|
let aValue;
|
|
799
1018
|
let bValue;
|
|
800
1019
|
switch (query.sortBy) {
|
|
1020
|
+
case "thread_id":
|
|
1021
|
+
aValue = a.thread_id;
|
|
1022
|
+
bValue = b.thread_id;
|
|
1023
|
+
break;
|
|
801
1024
|
case "created_at":
|
|
802
1025
|
aValue = new Date(a.created_at).getTime();
|
|
803
1026
|
bValue = new Date(b.created_at).getTime();
|
|
@@ -806,26 +1029,64 @@ class MemoryThreadsManager {
|
|
|
806
1029
|
aValue = new Date(a.updated_at).getTime();
|
|
807
1030
|
bValue = new Date(b.updated_at).getTime();
|
|
808
1031
|
break;
|
|
1032
|
+
case "status":
|
|
1033
|
+
aValue = a.status;
|
|
1034
|
+
bValue = b.status;
|
|
1035
|
+
break;
|
|
809
1036
|
default:
|
|
810
1037
|
return 0;
|
|
811
1038
|
}
|
|
812
1039
|
if (query.sortOrder === "desc") {
|
|
813
|
-
return bValue -
|
|
1040
|
+
return bValue > aValue ? 1 : bValue < aValue ? -1 : 0;
|
|
814
1041
|
} else {
|
|
815
|
-
return aValue -
|
|
1042
|
+
return aValue > bValue ? 1 : aValue < bValue ? -1 : 0;
|
|
816
1043
|
}
|
|
817
1044
|
});
|
|
818
1045
|
}
|
|
819
1046
|
const offset = query?.offset || 0;
|
|
820
1047
|
const limit = query?.limit || filteredThreads.length;
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
1048
|
+
const paginatedThreads = filteredThreads.slice(offset, offset + limit);
|
|
1049
|
+
return paginatedThreads.map((i) => {
|
|
1050
|
+
const result = { thread_id: i.thread_id };
|
|
1051
|
+
let includeFields;
|
|
1052
|
+
if (query?.select) {
|
|
1053
|
+
includeFields = new Set(query.select);
|
|
1054
|
+
} else if (query?.withoutDetails) {
|
|
1055
|
+
includeFields = /* @__PURE__ */ new Set(["thread_id", "created_at", "updated_at", "metadata", "status"]);
|
|
1056
|
+
} else {
|
|
1057
|
+
includeFields = /* @__PURE__ */ new Set([
|
|
1058
|
+
"thread_id",
|
|
1059
|
+
"created_at",
|
|
1060
|
+
"updated_at",
|
|
1061
|
+
"metadata",
|
|
1062
|
+
"status",
|
|
1063
|
+
"values",
|
|
1064
|
+
"interrupts"
|
|
1065
|
+
]);
|
|
825
1066
|
}
|
|
826
|
-
|
|
1067
|
+
if (includeFields.has("thread_id")) result.thread_id = i.thread_id;
|
|
1068
|
+
if (includeFields.has("created_at")) result.created_at = i.created_at;
|
|
1069
|
+
if (includeFields.has("updated_at")) result.updated_at = i.updated_at;
|
|
1070
|
+
if (includeFields.has("metadata")) result.metadata = i.metadata;
|
|
1071
|
+
if (includeFields.has("status")) result.status = i.status;
|
|
1072
|
+
if (includeFields.has("values")) result.values = i.values;
|
|
1073
|
+
if (includeFields.has("interrupts")) result.interrupts = i.interrupts;
|
|
1074
|
+
return result;
|
|
827
1075
|
});
|
|
828
1076
|
}
|
|
1077
|
+
deepEqual(a, b) {
|
|
1078
|
+
if (a === b) return true;
|
|
1079
|
+
if (typeof a !== typeof b) return false;
|
|
1080
|
+
if (typeof a !== "object" || a === null || b === null) return false;
|
|
1081
|
+
const keysA = Object.keys(a);
|
|
1082
|
+
const keysB = Object.keys(b);
|
|
1083
|
+
if (keysA.length !== keysB.length) return false;
|
|
1084
|
+
for (const key of keysA) {
|
|
1085
|
+
if (!keysB.includes(key)) return false;
|
|
1086
|
+
if (!this.deepEqual(a[key], b[key])) return false;
|
|
1087
|
+
}
|
|
1088
|
+
return true;
|
|
1089
|
+
}
|
|
829
1090
|
async get(threadId) {
|
|
830
1091
|
const thread = this.threads.find((t) => t.thread_id === threadId);
|
|
831
1092
|
if (!thread) {
|
|
@@ -843,6 +1104,7 @@ class MemoryThreadsManager {
|
|
|
843
1104
|
async delete(threadId) {
|
|
844
1105
|
const initialLength = this.threads.length;
|
|
845
1106
|
this.threads = this.threads.filter((t) => t.thread_id !== threadId);
|
|
1107
|
+
this.checkpoints.delete(threadId);
|
|
846
1108
|
if (this.threads.length === initialLength) {
|
|
847
1109
|
throw new Error(`Thread with ID ${threadId} not found.`);
|
|
848
1110
|
}
|
|
@@ -856,26 +1118,38 @@ class MemoryThreadsManager {
|
|
|
856
1118
|
if (targetThread.status === "busy") {
|
|
857
1119
|
throw new Error(`Thread with ID ${threadId} is busy, can't update state.`);
|
|
858
1120
|
}
|
|
859
|
-
this.threads[index] = {
|
|
860
|
-
|
|
861
|
-
|
|
1121
|
+
this.threads[index] = {
|
|
1122
|
+
...targetThread,
|
|
1123
|
+
values: thread.values,
|
|
1124
|
+
updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
1125
|
+
};
|
|
1126
|
+
if (targetThread.metadata?.graph_id) {
|
|
1127
|
+
const graphId = targetThread.metadata?.graph_id;
|
|
1128
|
+
const config = {
|
|
1129
|
+
configurable: {
|
|
1130
|
+
thread_id: threadId,
|
|
1131
|
+
graph_id: graphId
|
|
1132
|
+
}
|
|
1133
|
+
};
|
|
1134
|
+
try {
|
|
1135
|
+
const graph = await getGraph(graphId, config);
|
|
1136
|
+
const nextConfig = await graph.updateState(config, thread.values);
|
|
1137
|
+
const graphState = await graph.getState(config);
|
|
1138
|
+
await this.set(threadId, { values: JSON.parse(serialiseAsDict(graphState.values)) });
|
|
1139
|
+
return nextConfig;
|
|
1140
|
+
} catch (error) {
|
|
1141
|
+
console.warn("Failed to update graph state:", error);
|
|
1142
|
+
}
|
|
862
1143
|
}
|
|
863
|
-
|
|
864
|
-
const config = {
|
|
1144
|
+
return {
|
|
865
1145
|
configurable: {
|
|
866
|
-
thread_id: threadId
|
|
867
|
-
graph_id: graphId
|
|
1146
|
+
thread_id: threadId
|
|
868
1147
|
}
|
|
869
1148
|
};
|
|
870
|
-
const graph = await getGraph(graphId, config);
|
|
871
|
-
const nextConfig = await graph.updateState(config, thread.values);
|
|
872
|
-
const graphState = await graph.getState(config);
|
|
873
|
-
await this.set(threadId, { values: JSON.parse(serialiseAsDict(graphState.values)) });
|
|
874
|
-
return nextConfig;
|
|
875
1149
|
}
|
|
876
1150
|
runs = [];
|
|
877
1151
|
async createRun(threadId, assistantId, payload) {
|
|
878
|
-
const runId =
|
|
1152
|
+
const runId = v7();
|
|
879
1153
|
const run = {
|
|
880
1154
|
run_id: runId,
|
|
881
1155
|
thread_id: threadId,
|
|
@@ -906,6 +1180,127 @@ class MemoryThreadsManager {
|
|
|
906
1180
|
}
|
|
907
1181
|
this.runs[index] = { ...this.runs[index], ...run };
|
|
908
1182
|
}
|
|
1183
|
+
// New methods for Threads API
|
|
1184
|
+
async count(query) {
|
|
1185
|
+
const threads = await this.search(query);
|
|
1186
|
+
return threads.length;
|
|
1187
|
+
}
|
|
1188
|
+
async patch(threadId, updates) {
|
|
1189
|
+
const index = this.threads.findIndex((t) => t.thread_id === threadId);
|
|
1190
|
+
if (index === -1) {
|
|
1191
|
+
throw new Error(`Thread with ID ${threadId} not found.`);
|
|
1192
|
+
}
|
|
1193
|
+
const updatedThread = {
|
|
1194
|
+
...this.threads[index],
|
|
1195
|
+
...updates,
|
|
1196
|
+
updated_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1197
|
+
metadata: updates.metadata ? { ...this.threads[index].metadata, ...updates.metadata } : this.threads[index].metadata
|
|
1198
|
+
};
|
|
1199
|
+
this.threads[index] = updatedThread;
|
|
1200
|
+
return updatedThread;
|
|
1201
|
+
}
|
|
1202
|
+
async getState(threadId, options) {
|
|
1203
|
+
const thread = await this.get(threadId);
|
|
1204
|
+
if (options?.checkpointId) {
|
|
1205
|
+
const checkpoints = this.checkpoints.get(threadId) || [];
|
|
1206
|
+
const checkpoint = checkpoints.find((c) => c.checkpoint_id === options.checkpointId);
|
|
1207
|
+
if (!checkpoint) {
|
|
1208
|
+
throw new Error(`Checkpoint with ID ${options.checkpointId} not found for thread ${threadId}`);
|
|
1209
|
+
}
|
|
1210
|
+
return {
|
|
1211
|
+
values: checkpoint.values,
|
|
1212
|
+
next: checkpoint.next,
|
|
1213
|
+
metadata: checkpoint.metadata,
|
|
1214
|
+
checkpoint: {
|
|
1215
|
+
/** @ts-ignore 附加属性 */
|
|
1216
|
+
id: checkpoint.checkpoint_id,
|
|
1217
|
+
thread_id: threadId,
|
|
1218
|
+
parent_checkpoint_id: null,
|
|
1219
|
+
checkpoint_ns: "",
|
|
1220
|
+
metadata: checkpoint.metadata,
|
|
1221
|
+
created_at: checkpoint.created_at
|
|
1222
|
+
},
|
|
1223
|
+
created_at: checkpoint.created_at,
|
|
1224
|
+
parent_checkpoint: null,
|
|
1225
|
+
tasks: []
|
|
1226
|
+
};
|
|
1227
|
+
}
|
|
1228
|
+
const state = {
|
|
1229
|
+
values: thread.values || {},
|
|
1230
|
+
next: [],
|
|
1231
|
+
metadata: thread.metadata,
|
|
1232
|
+
/** @ts-ignore 没有查询过 */
|
|
1233
|
+
checkpoint: null,
|
|
1234
|
+
created_at: thread.created_at,
|
|
1235
|
+
parent_checkpoint: null,
|
|
1236
|
+
tasks: []
|
|
1237
|
+
};
|
|
1238
|
+
return state;
|
|
1239
|
+
}
|
|
1240
|
+
async getStateHistory(threadId, options) {
|
|
1241
|
+
const checkpoints = this.checkpoints.get(threadId) || [];
|
|
1242
|
+
let history = checkpoints.map(
|
|
1243
|
+
(cp) => ({
|
|
1244
|
+
values: cp.values,
|
|
1245
|
+
next: cp.next,
|
|
1246
|
+
metadata: cp.metadata,
|
|
1247
|
+
checkpoint: {
|
|
1248
|
+
checkpoint_id: cp.checkpoint_id,
|
|
1249
|
+
thread_id: threadId,
|
|
1250
|
+
checkpoint_ns: "",
|
|
1251
|
+
checkpoint_map: void 0
|
|
1252
|
+
},
|
|
1253
|
+
created_at: cp.created_at,
|
|
1254
|
+
parent_checkpoint: null,
|
|
1255
|
+
tasks: []
|
|
1256
|
+
})
|
|
1257
|
+
);
|
|
1258
|
+
if (options?.before) {
|
|
1259
|
+
const beforeIndex = checkpoints.findIndex((c) => c.checkpoint_id === options.before);
|
|
1260
|
+
if (beforeIndex !== -1) {
|
|
1261
|
+
history = history.slice(beforeIndex + 1);
|
|
1262
|
+
}
|
|
1263
|
+
}
|
|
1264
|
+
if (options?.limit) {
|
|
1265
|
+
history = history.slice(0, options.limit);
|
|
1266
|
+
}
|
|
1267
|
+
return history;
|
|
1268
|
+
}
|
|
1269
|
+
async copy(threadId) {
|
|
1270
|
+
const originalThread = await this.get(threadId);
|
|
1271
|
+
const newThreadId = v7();
|
|
1272
|
+
const newThread = {
|
|
1273
|
+
...originalThread,
|
|
1274
|
+
thread_id: newThreadId,
|
|
1275
|
+
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1276
|
+
updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
1277
|
+
};
|
|
1278
|
+
this.threads.push(newThread);
|
|
1279
|
+
const originalCheckpoints = this.checkpoints.get(threadId) || [];
|
|
1280
|
+
const newCheckpoints = originalCheckpoints.map((cp) => ({
|
|
1281
|
+
...cp,
|
|
1282
|
+
checkpoint_id: v7(),
|
|
1283
|
+
// Generate new checkpoint IDs
|
|
1284
|
+
thread_id: newThreadId
|
|
1285
|
+
}));
|
|
1286
|
+
this.checkpoints.set(newThreadId, newCheckpoints);
|
|
1287
|
+
return newThread;
|
|
1288
|
+
}
|
|
1289
|
+
// Helper method to save checkpoint (used internally)
|
|
1290
|
+
async saveCheckpoint(threadId, values, next, config, metadata) {
|
|
1291
|
+
const checkpoints = this.checkpoints.get(threadId) || [];
|
|
1292
|
+
const checkpoint = {
|
|
1293
|
+
checkpoint_id: v7(),
|
|
1294
|
+
thread_id: threadId,
|
|
1295
|
+
values,
|
|
1296
|
+
next,
|
|
1297
|
+
config,
|
|
1298
|
+
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1299
|
+
metadata
|
|
1300
|
+
};
|
|
1301
|
+
checkpoints.push(checkpoint);
|
|
1302
|
+
this.checkpoints.set(threadId, checkpoints);
|
|
1303
|
+
}
|
|
909
1304
|
}
|
|
910
1305
|
|
|
911
1306
|
const createCheckPointer = async () => {
|
|
@@ -925,7 +1320,7 @@ const createCheckPointer = async () => {
|
|
|
925
1320
|
return await ShallowRedisSaver.fromUrl(process.env.REDIS_URL);
|
|
926
1321
|
}
|
|
927
1322
|
}
|
|
928
|
-
if (process.env.DATABASE_URL) {
|
|
1323
|
+
if (process.env.DATABASE_URL && getDatabaseType(process.env.DATABASE_URL) === "postgres") {
|
|
929
1324
|
console.debug("LG | Using postgres as checkpoint");
|
|
930
1325
|
const { createPGCheckpoint } = await import('./checkpoint-DxiUsHMy.js');
|
|
931
1326
|
return createPGCheckpoint();
|
|
@@ -951,23 +1346,43 @@ const createMessageQueue = async () => {
|
|
|
951
1346
|
let q;
|
|
952
1347
|
if (process.env.REDIS_URL) {
|
|
953
1348
|
console.debug("LG | Using redis as stream queue");
|
|
954
|
-
const { RedisStreamQueue } = await import('./queue-
|
|
1349
|
+
const { RedisStreamQueue } = await import('./queue-CtVch_az.js');
|
|
955
1350
|
q = RedisStreamQueue;
|
|
956
1351
|
} else {
|
|
957
1352
|
q = MemoryStreamQueue;
|
|
958
1353
|
}
|
|
959
1354
|
return new StreamQueueManager(q);
|
|
960
1355
|
};
|
|
1356
|
+
function getDatabaseType(databaseUrl) {
|
|
1357
|
+
const url = databaseUrl.toLowerCase();
|
|
1358
|
+
if (url.startsWith("http://") || url.startsWith("https://")) {
|
|
1359
|
+
return "remote";
|
|
1360
|
+
}
|
|
1361
|
+
return "postgres";
|
|
1362
|
+
}
|
|
961
1363
|
const createThreadManager = async (config) => {
|
|
962
|
-
if (process.env.DATABASE_URL
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
1364
|
+
if (process.env.DATABASE_URL) {
|
|
1365
|
+
const dbType = getDatabaseType(process.env.DATABASE_URL);
|
|
1366
|
+
if (dbType === "remote") {
|
|
1367
|
+
console.debug("LG | Using Remote PostgreSQL ThreadsManager");
|
|
1368
|
+
const { RemoteKyselyThreadsManager } = await import('./remote-threads-CrG03ZS7.js');
|
|
1369
|
+
const threadsManager = new RemoteKyselyThreadsManager(process.env.DATABASE_URL);
|
|
1370
|
+
if (process.env.DATABASE_INIT === "true") {
|
|
1371
|
+
await threadsManager.setup();
|
|
1372
|
+
}
|
|
1373
|
+
return threadsManager;
|
|
1374
|
+
} else {
|
|
1375
|
+
if (config.checkpointer) {
|
|
1376
|
+
console.debug("LG | Using PostgreSQL ThreadsManager");
|
|
1377
|
+
const { PostgresAdapter } = await import('./pg-adapter-BFtir1GE.js');
|
|
1378
|
+
const pool = config.checkpointer.pool;
|
|
1379
|
+
const threadsManager = new KyselyThreadsManager(new PostgresAdapter(pool));
|
|
1380
|
+
if (process.env.DATABASE_INIT === "true") {
|
|
1381
|
+
await threadsManager.setup();
|
|
1382
|
+
}
|
|
1383
|
+
return threadsManager;
|
|
1384
|
+
}
|
|
969
1385
|
}
|
|
970
|
-
return threadsManager;
|
|
971
1386
|
}
|
|
972
1387
|
if (process.env.SQLITE_DATABASE_URI && config.checkpointer) {
|
|
973
1388
|
console.debug("LG | Using SQLite ThreadsManager");
|
|
@@ -1054,9 +1469,11 @@ async function streamStateWithQueue(threads, run, queue, payload, options) {
|
|
|
1054
1469
|
signal: queue.cancelSignal.signal
|
|
1055
1470
|
}
|
|
1056
1471
|
);
|
|
1472
|
+
let sendedMetadataMessage;
|
|
1473
|
+
let messageChunks;
|
|
1057
1474
|
try {
|
|
1058
|
-
|
|
1059
|
-
|
|
1475
|
+
sendedMetadataMessage = /* @__PURE__ */ new Set();
|
|
1476
|
+
messageChunks = /* @__PURE__ */ new Map();
|
|
1060
1477
|
for await (const event of await events) {
|
|
1061
1478
|
let ns = [];
|
|
1062
1479
|
if (event.length === 3) {
|
|
@@ -1111,6 +1528,12 @@ async function streamStateWithQueue(threads, run, queue, payload, options) {
|
|
|
1111
1528
|
}
|
|
1112
1529
|
} finally {
|
|
1113
1530
|
await queue.push(new StreamEndEventMessage());
|
|
1531
|
+
if (sendedMetadataMessage) {
|
|
1532
|
+
sendedMetadataMessage.clear();
|
|
1533
|
+
}
|
|
1534
|
+
if (messageChunks) {
|
|
1535
|
+
messageChunks.clear();
|
|
1536
|
+
}
|
|
1114
1537
|
}
|
|
1115
1538
|
}
|
|
1116
1539
|
const serialiseAsDict = (obj, indent = 0) => {
|
|
@@ -1155,9 +1578,10 @@ async function* streamState(threads, run, payload, options) {
|
|
|
1155
1578
|
} else {
|
|
1156
1579
|
await threads.set(threadId, { status: "idle", interrupts: {} });
|
|
1157
1580
|
}
|
|
1581
|
+
await LangGraphGlobal.globalMessageQueue.clearQueue(queueId);
|
|
1158
1582
|
LangGraphGlobal.globalMessageQueue.removeQueue(queueId);
|
|
1159
1583
|
}
|
|
1160
1584
|
}
|
|
1161
1585
|
|
|
1162
|
-
export { BaseStreamQueue as B, CancelEventMessage as C, GRAPHS as G, LangGraphGlobal as L, streamState as a, getGraph as g, registerGraph as r, serialiseAsDict as s };
|
|
1163
|
-
//# sourceMappingURL=stream-
|
|
1586
|
+
export { BaseStreamQueue as B, CancelEventMessage as C, GRAPHS as G, KyselyThreadsManager as K, LangGraphGlobal as L, streamState as a, getGraph as g, registerGraph as r, serialiseAsDict as s };
|
|
1587
|
+
//# sourceMappingURL=stream-umoA6h4q.js.map
|