@langgraph-js/pure-graph 2.8.1 → 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 (48) 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 +32 -0
  13. package/dist/agents/index.d.ts +1 -0
  14. package/dist/createEndpoint-BViLxrhh.js +208 -0
  15. package/dist/createEndpoint-BViLxrhh.js.map +1 -0
  16. package/dist/createEndpoint.d.ts +25 -0
  17. package/dist/index.d.ts +1 -1
  18. package/dist/index.js +75 -17
  19. package/dist/index.js.map +1 -1
  20. package/dist/queue/stream_queue.d.ts +4 -4
  21. package/dist/queue-CtVch_az.js +153 -0
  22. package/dist/queue-CtVch_az.js.map +1 -0
  23. package/dist/remote/index.js +158 -0
  24. package/dist/remote/index.js.map +1 -0
  25. package/dist/remote-threads-CrG03ZS7.js +255 -0
  26. package/dist/remote-threads-CrG03ZS7.js.map +1 -0
  27. package/dist/storage/index.d.ts +1 -1
  28. package/dist/storage/kysely/index.d.ts +1 -0
  29. package/dist/storage/kysely/remote-threads.d.ts +124 -0
  30. package/dist/storage/kysely/threads.d.ts +26 -2
  31. package/dist/storage/kysely/types.d.ts +10 -0
  32. package/dist/storage/memory/queue.d.ts +1 -1
  33. package/dist/storage/memory/threads.d.ts +27 -1
  34. package/dist/storage/redis/queue.d.ts +15 -12
  35. package/dist/storage/remote/fetch.d.ts +20 -0
  36. package/dist/storage/remote/remote-server.d.ts +17 -0
  37. package/dist/storage/remote/server.d.ts +11 -0
  38. package/dist/storage/remote/types.d.ts +121 -0
  39. package/dist/{stream-B7KiKwj1.js → stream-umoA6h4q.js} +513 -77
  40. package/dist/stream-umoA6h4q.js.map +1 -0
  41. package/dist/threads/index.d.ts +25 -1
  42. package/dist/types.d.ts +53 -1
  43. package/package.json +11 -5
  44. package/dist/createEndpoint-BsPsukFX.js +0 -122
  45. package/dist/createEndpoint-BsPsukFX.js.map +0 -1
  46. package/dist/queue-BSCnCent.js +0 -134
  47. package/dist/queue-BSCnCent.js.map +0 -1
  48. package/dist/stream-B7KiKwj1.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) => {
@@ -318,10 +319,10 @@ class StreamQueueManager {
318
319
  * Cancel queue with specified id
319
320
  * @param id 队列 ID / Queue ID
320
321
  */
321
- cancelQueue(id) {
322
+ async cancelQueue(id) {
322
323
  const queue = this.queues.get(id);
323
324
  if (queue) {
324
- queue.cancel();
325
+ await queue.cancel();
325
326
  this.removeQueue(id);
326
327
  }
327
328
  }
@@ -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,29 +868,67 @@ class MemoryStreamQueue extends BaseStreamQueue {
686
868
  let queue = [];
687
869
  let pendingResolve = null;
688
870
  let isStreamEnded = false;
871
+ let isCleanupDone = false;
689
872
  const handleData = async (item) => {
690
- const data = this.compressMessages ? await this.decodeData(item) : item;
691
- queue.push(data);
692
- if (data.event === "__stream_end__" || data.event === "__stream_error__" || data.event === "__stream_cancel__") {
693
- setTimeout(() => {
694
- isStreamEnded = true;
695
- if (pendingResolve) {
696
- pendingResolve();
697
- 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();
698
886
  }
699
- }, 300);
700
- if (data.event === "__stream_cancel__") {
701
- this.cancel();
702
887
  }
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
+ }
898
+ }
899
+ };
900
+ this.on("dataChange", handleData);
901
+ const abortHandler = () => {
902
+ isStreamEnded = true;
903
+ if (pendingResolve) {
904
+ pendingResolve();
905
+ pendingResolve = null;
906
+ }
907
+ };
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);
703
921
  }
704
922
  if (pendingResolve) {
705
923
  pendingResolve();
706
924
  pendingResolve = null;
707
925
  }
708
926
  };
709
- this.on("dataChange", handleData);
710
927
  try {
711
- while (!isStreamEnded) {
928
+ if (this.cancelSignal.signal.aborted) {
929
+ return;
930
+ }
931
+ while (!isStreamEnded && !this.cancelSignal.signal.aborted) {
712
932
  if (queue.length > 0) {
713
933
  for (const item of queue) {
714
934
  yield item;
@@ -721,7 +941,7 @@ class MemoryStreamQueue extends BaseStreamQueue {
721
941
  }
722
942
  }
723
943
  } finally {
724
- this.off("dataChange", handleData);
944
+ cleanup();
725
945
  }
726
946
  }
727
947
  async getAll() {
@@ -733,12 +953,12 @@ class MemoryStreamQueue extends BaseStreamQueue {
733
953
  this.data = [];
734
954
  }
735
955
  cancelSignal = new AbortController();
736
- cancel() {
737
- this.push(new CancelEventMessage());
956
+ async cancel() {
738
957
  this.cancelSignal.abort("user cancel this run");
958
+ await this.push(new CancelEventMessage());
739
959
  }
740
960
  async copyToQueue(toId, ttl) {
741
- const data = this.data;
961
+ const data = this.data.slice();
742
962
  const queue = new MemoryStreamQueue(toId, this.compressMessages, ttl ?? this.ttl);
743
963
  queue.data = data;
744
964
  return queue;
@@ -747,11 +967,12 @@ class MemoryStreamQueue extends BaseStreamQueue {
747
967
 
748
968
  class MemoryThreadsManager {
749
969
  threads = [];
970
+ checkpoints = /* @__PURE__ */ new Map();
750
971
  async setup() {
751
972
  return;
752
973
  }
753
974
  async create(payload) {
754
- const threadId = payload?.threadId || crypto.randomUUID();
975
+ const threadId = payload?.threadId || v7();
755
976
  if (payload?.ifExists === "raise" && this.threads.some((t) => t.thread_id === threadId)) {
756
977
  throw new Error(`Thread with ID ${threadId} already exists.`);
757
978
  }
@@ -764,11 +985,15 @@ class MemoryThreadsManager {
764
985
  values: null,
765
986
  interrupts: {}
766
987
  };
988
+ this.checkpoints.set(threadId, []);
767
989
  this.threads.push(thread);
768
990
  return thread;
769
991
  }
770
992
  async search(query) {
771
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
+ }
772
997
  if (query?.status) {
773
998
  filteredThreads = filteredThreads.filter((t) => t.status === query.status);
774
999
  }
@@ -781,11 +1006,21 @@ class MemoryThreadsManager {
781
1006
  }
782
1007
  }
783
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
+ }
784
1015
  if (query?.sortBy) {
785
1016
  filteredThreads.sort((a, b) => {
786
1017
  let aValue;
787
1018
  let bValue;
788
1019
  switch (query.sortBy) {
1020
+ case "thread_id":
1021
+ aValue = a.thread_id;
1022
+ bValue = b.thread_id;
1023
+ break;
789
1024
  case "created_at":
790
1025
  aValue = new Date(a.created_at).getTime();
791
1026
  bValue = new Date(b.created_at).getTime();
@@ -794,26 +1029,64 @@ class MemoryThreadsManager {
794
1029
  aValue = new Date(a.updated_at).getTime();
795
1030
  bValue = new Date(b.updated_at).getTime();
796
1031
  break;
1032
+ case "status":
1033
+ aValue = a.status;
1034
+ bValue = b.status;
1035
+ break;
797
1036
  default:
798
1037
  return 0;
799
1038
  }
800
1039
  if (query.sortOrder === "desc") {
801
- return bValue - aValue;
1040
+ return bValue > aValue ? 1 : bValue < aValue ? -1 : 0;
802
1041
  } else {
803
- return aValue - bValue;
1042
+ return aValue > bValue ? 1 : aValue < bValue ? -1 : 0;
804
1043
  }
805
1044
  });
806
1045
  }
807
1046
  const offset = query?.offset || 0;
808
1047
  const limit = query?.limit || filteredThreads.length;
809
- return filteredThreads.slice(offset, offset + limit).map((i) => {
810
- if (query?.withoutDetails) {
811
- i.values = null;
812
- 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
+ ]);
813
1066
  }
814
- 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;
815
1075
  });
816
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
+ }
817
1090
  async get(threadId) {
818
1091
  const thread = this.threads.find((t) => t.thread_id === threadId);
819
1092
  if (!thread) {
@@ -831,6 +1104,7 @@ class MemoryThreadsManager {
831
1104
  async delete(threadId) {
832
1105
  const initialLength = this.threads.length;
833
1106
  this.threads = this.threads.filter((t) => t.thread_id !== threadId);
1107
+ this.checkpoints.delete(threadId);
834
1108
  if (this.threads.length === initialLength) {
835
1109
  throw new Error(`Thread with ID ${threadId} not found.`);
836
1110
  }
@@ -844,26 +1118,38 @@ class MemoryThreadsManager {
844
1118
  if (targetThread.status === "busy") {
845
1119
  throw new Error(`Thread with ID ${threadId} is busy, can't update state.`);
846
1120
  }
847
- this.threads[index] = { ...targetThread, values: thread.values };
848
- if (!targetThread.metadata?.graph_id) {
849
- 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
+ }
850
1143
  }
851
- const graphId = targetThread.metadata?.graph_id;
852
- const config = {
1144
+ return {
853
1145
  configurable: {
854
- thread_id: threadId,
855
- graph_id: graphId
1146
+ thread_id: threadId
856
1147
  }
857
1148
  };
858
- const graph = await getGraph(graphId, config);
859
- const nextConfig = await graph.updateState(config, thread.values);
860
- const graphState = await graph.getState(config);
861
- await this.set(threadId, { values: JSON.parse(serialiseAsDict(graphState.values)) });
862
- return nextConfig;
863
1149
  }
864
1150
  runs = [];
865
1151
  async createRun(threadId, assistantId, payload) {
866
- const runId = crypto.randomUUID();
1152
+ const runId = v7();
867
1153
  const run = {
868
1154
  run_id: runId,
869
1155
  thread_id: threadId,
@@ -894,6 +1180,127 @@ class MemoryThreadsManager {
894
1180
  }
895
1181
  this.runs[index] = { ...this.runs[index], ...run };
896
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
+ }
897
1304
  }
898
1305
 
899
1306
  const createCheckPointer = async () => {
@@ -913,7 +1320,7 @@ const createCheckPointer = async () => {
913
1320
  return await ShallowRedisSaver.fromUrl(process.env.REDIS_URL);
914
1321
  }
915
1322
  }
916
- if (process.env.DATABASE_URL) {
1323
+ if (process.env.DATABASE_URL && getDatabaseType(process.env.DATABASE_URL) === "postgres") {
917
1324
  console.debug("LG | Using postgres as checkpoint");
918
1325
  const { createPGCheckpoint } = await import('./checkpoint-DxiUsHMy.js');
919
1326
  return createPGCheckpoint();
@@ -939,23 +1346,43 @@ const createMessageQueue = async () => {
939
1346
  let q;
940
1347
  if (process.env.REDIS_URL) {
941
1348
  console.debug("LG | Using redis as stream queue");
942
- const { RedisStreamQueue } = await import('./queue-BSCnCent.js');
1349
+ const { RedisStreamQueue } = await import('./queue-CtVch_az.js');
943
1350
  q = RedisStreamQueue;
944
1351
  } else {
945
1352
  q = MemoryStreamQueue;
946
1353
  }
947
1354
  return new StreamQueueManager(q);
948
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
+ }
949
1363
  const createThreadManager = async (config) => {
950
- if (process.env.DATABASE_URL && config.checkpointer) {
951
- console.debug("LG | Using PostgreSQL ThreadsManager");
952
- const { PostgresAdapter } = await import('./pg-adapter-BFtir1GE.js');
953
- const pool = config.checkpointer.pool;
954
- const threadsManager = new KyselyThreadsManager(new PostgresAdapter(pool));
955
- if (process.env.DATABASE_INIT === "true") {
956
- 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
+ }
957
1385
  }
958
- return threadsManager;
959
1386
  }
960
1387
  if (process.env.SQLITE_DATABASE_URI && config.checkpointer) {
961
1388
  console.debug("LG | Using SQLite ThreadsManager");
@@ -1042,9 +1469,11 @@ async function streamStateWithQueue(threads, run, queue, payload, options) {
1042
1469
  signal: queue.cancelSignal.signal
1043
1470
  }
1044
1471
  );
1472
+ let sendedMetadataMessage;
1473
+ let messageChunks;
1045
1474
  try {
1046
- const sendedMetadataMessage = /* @__PURE__ */ new Set();
1047
- const messageChunks = /* @__PURE__ */ new Map();
1475
+ sendedMetadataMessage = /* @__PURE__ */ new Set();
1476
+ messageChunks = /* @__PURE__ */ new Map();
1048
1477
  for await (const event of await events) {
1049
1478
  let ns = [];
1050
1479
  if (event.length === 3) {
@@ -1099,6 +1528,12 @@ async function streamStateWithQueue(threads, run, queue, payload, options) {
1099
1528
  }
1100
1529
  } finally {
1101
1530
  await queue.push(new StreamEndEventMessage());
1531
+ if (sendedMetadataMessage) {
1532
+ sendedMetadataMessage.clear();
1533
+ }
1534
+ if (messageChunks) {
1535
+ messageChunks.clear();
1536
+ }
1102
1537
  }
1103
1538
  }
1104
1539
  const serialiseAsDict = (obj, indent = 0) => {
@@ -1125,7 +1560,7 @@ async function* streamState(threads, run, payload, options) {
1125
1560
  const queue = LangGraphGlobal.globalMessageQueue.createQueue(queueId);
1126
1561
  const state = queue.onDataReceive();
1127
1562
  streamStateWithQueue(threads, run, queue, payload, options).catch((error) => {
1128
- console.error("Queue task error:", error);
1563
+ if (error.message !== "user cancel this run") console.error("Queue task error:", error);
1129
1564
  LangGraphGlobal.globalMessageQueue.pushToQueue(queueId, new StreamErrorEventMessage(error));
1130
1565
  });
1131
1566
  for await (const data of state) {
@@ -1143,9 +1578,10 @@ async function* streamState(threads, run, payload, options) {
1143
1578
  } else {
1144
1579
  await threads.set(threadId, { status: "idle", interrupts: {} });
1145
1580
  }
1581
+ await LangGraphGlobal.globalMessageQueue.clearQueue(queueId);
1146
1582
  LangGraphGlobal.globalMessageQueue.removeQueue(queueId);
1147
1583
  }
1148
1584
  }
1149
1585
 
1150
- 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 };
1151
- //# sourceMappingURL=stream-B7KiKwj1.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