@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.
Files changed (44) hide show
  1. package/dist/adapter/fetch/assistants.d.ts +42 -0
  2. package/dist/adapter/fetch/endpoint.d.ts +25 -0
  3. package/dist/adapter/fetch/index.d.ts +5 -3
  4. package/dist/adapter/fetch/index.js +858 -25
  5. package/dist/adapter/fetch/index.js.map +1 -1
  6. package/dist/adapter/fetch/runs-extended.d.ts +21 -0
  7. package/dist/adapter/fetch/runs-stateless.d.ts +28 -0
  8. package/dist/adapter/fetch/runs.d.ts +0 -4
  9. package/dist/adapter/fetch/threads.d.ts +36 -0
  10. package/dist/adapter/nextjs/index.js +1 -1
  11. package/dist/adapter/zod.d.ts +292 -1
  12. package/dist/agents/ask_subagents.d.ts +1 -1
  13. package/dist/createEndpoint-BViLxrhh.js +208 -0
  14. package/dist/createEndpoint-BViLxrhh.js.map +1 -0
  15. package/dist/createEndpoint.d.ts +25 -0
  16. package/dist/index.js +2 -2
  17. package/dist/queue/stream_queue.d.ts +2 -2
  18. package/dist/queue-CtVch_az.js +153 -0
  19. package/dist/queue-CtVch_az.js.map +1 -0
  20. package/dist/remote/index.js +158 -0
  21. package/dist/remote/index.js.map +1 -0
  22. package/dist/remote-threads-CrG03ZS7.js +255 -0
  23. package/dist/remote-threads-CrG03ZS7.js.map +1 -0
  24. package/dist/storage/index.d.ts +1 -1
  25. package/dist/storage/kysely/index.d.ts +1 -0
  26. package/dist/storage/kysely/remote-threads.d.ts +124 -0
  27. package/dist/storage/kysely/threads.d.ts +26 -2
  28. package/dist/storage/kysely/types.d.ts +10 -0
  29. package/dist/storage/memory/threads.d.ts +27 -1
  30. package/dist/storage/redis/queue.d.ts +14 -11
  31. package/dist/storage/remote/fetch.d.ts +20 -0
  32. package/dist/storage/remote/remote-server.d.ts +17 -0
  33. package/dist/storage/remote/server.d.ts +11 -0
  34. package/dist/storage/remote/types.d.ts +121 -0
  35. package/dist/{stream-D0YD2Pjq.js → stream-umoA6h4q.js} +502 -78
  36. package/dist/stream-umoA6h4q.js.map +1 -0
  37. package/dist/threads/index.d.ts +25 -1
  38. package/dist/types.d.ts +53 -1
  39. package/package.json +10 -4
  40. package/dist/createEndpoint-CN_RHDEd.js +0 -122
  41. package/dist/createEndpoint-CN_RHDEd.js.map +0 -1
  42. package/dist/queue-D6tEGCGs.js +0 -146
  43. package/dist/queue-D6tEGCGs.js.map +0 -1
  44. 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 || crypto.randomUUID();
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").selectAll();
476
- if (query?.withoutDetails) {
477
- queryBuilder = this.db.selectFrom("threads").select(["thread_id", "created_at", "updated_at", "metadata", "status"]);
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
- created_at: this.adapter.dbToDate(row.created_at).toISOString(),
501
- updated_at: this.adapter.dbToDate(row.updated_at).toISOString(),
502
- metadata: this.adapter.dbToJson(row.metadata),
503
- status: row.status,
504
- values: row.values ? this.adapter.dbToJson(row.values) : null,
505
- interrupts: this.adapter.dbToJson(row.interrupts)
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 = crypto.randomUUID();
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
- if (this.cancelSignal.signal.aborted) {
690
- return;
691
- }
871
+ let isCleanupDone = false;
692
872
  const handleData = async (item) => {
693
- const data = this.compressMessages ? await this.decodeData(item) : item;
694
- queue.push(data);
695
- if (data.event === "__stream_end__" || data.event === "__stream_error__" || data.event === "__stream_cancel__") {
696
- setTimeout(() => {
697
- isStreamEnded = true;
698
- if (pendingResolve) {
699
- pendingResolve();
700
- pendingResolve = null;
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
- if (pendingResolve) {
708
- pendingResolve();
709
- pendingResolve = null;
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
- this.off("dataChange", handleData);
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 || crypto.randomUUID();
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 - aValue;
1040
+ return bValue > aValue ? 1 : bValue < aValue ? -1 : 0;
814
1041
  } else {
815
- return aValue - bValue;
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
- return filteredThreads.slice(offset, offset + limit).map((i) => {
822
- if (query?.withoutDetails) {
823
- i.values = null;
824
- i.interrupts = null;
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
- return i;
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] = { ...targetThread, values: thread.values };
860
- if (!targetThread.metadata?.graph_id) {
861
- throw new Error(`Thread with ID ${threadId} has no graph_id.`);
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
- const graphId = targetThread.metadata?.graph_id;
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 = crypto.randomUUID();
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-D6tEGCGs.js');
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 && config.checkpointer) {
963
- console.debug("LG | Using PostgreSQL ThreadsManager");
964
- const { PostgresAdapter } = await import('./pg-adapter-BFtir1GE.js');
965
- const pool = config.checkpointer.pool;
966
- const threadsManager = new KyselyThreadsManager(new PostgresAdapter(pool));
967
- if (process.env.DATABASE_INIT === "true") {
968
- await threadsManager.setup();
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
- const sendedMetadataMessage = /* @__PURE__ */ new Set();
1059
- const messageChunks = /* @__PURE__ */ new Map();
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-D0YD2Pjq.js.map
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