@cloudbase/agent-adapter-langgraph 0.0.15 → 0.0.18

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/index.js CHANGED
@@ -32,6 +32,7 @@ var index_exports = {};
32
32
  __export(index_exports, {
33
33
  ClientPropertiesAnnotation: () => ClientPropertiesAnnotation,
34
34
  ClientStateAnnotation: () => ClientStateAnnotation,
35
+ CloudBaseSaver: () => CloudBaseSaver,
35
36
  LanggraphAgent: () => LanggraphAgent,
36
37
  TDAISaver: () => TDAISaver,
37
38
  TDAIStore: () => TDAIStore,
@@ -94,6 +95,7 @@ function convertJsonSchemaToZodSchema(jsonSchema, required) {
94
95
 
95
96
  // src/agent.ts
96
97
  var import_agent_shared = require("@cloudbase/agent-shared");
98
+ var LangChainCallbackHandler;
97
99
  var ClientPropertiesAnnotation = import_langgraph.Annotation.Root({
98
100
  tools: import_langgraph.Annotation
99
101
  });
@@ -105,6 +107,7 @@ var LanggraphAgent = class extends import_client.AbstractAgent {
105
107
  constructor(agentConfig) {
106
108
  super(agentConfig);
107
109
  this.compiledWorkflow = agentConfig.compiledWorkflow;
110
+ this.adapterName = agentConfig.adapterName || "LangGraph";
108
111
  const baseLogger = agentConfig.logger ?? import_agent_shared.noopLogger;
109
112
  this.logger = baseLogger.child?.({ component: "langgraph-agent" }) ?? baseLogger;
110
113
  }
@@ -117,6 +120,7 @@ var LanggraphAgent = class extends import_client.AbstractAgent {
117
120
  const { messages, runId, threadId } = input;
118
121
  const logger = this.logger.child?.({ runId, threadId }) ?? this.logger;
119
122
  logger.info?.("Run started");
123
+ await this.setupObservability(input, logger);
120
124
  const runStartedEvent = {
121
125
  type: import_client.EventType.RUN_STARTED,
122
126
  threadId,
@@ -124,11 +128,9 @@ var LanggraphAgent = class extends import_client.AbstractAgent {
124
128
  };
125
129
  logger.trace?.({ aguiEvent: runStartedEvent }, "Emitting AGUI event");
126
130
  subscriber.next(runStartedEvent);
127
- const isResume = !!input.forwardedProps?.resume;
128
131
  const lastUserMessage = messages.filter((m) => m.role === "user").pop();
129
132
  logger.debug?.(
130
133
  {
131
- isResume,
132
134
  messageCount: messages.length,
133
135
  toolCount: input.tools?.length ?? 0,
134
136
  tools: input.tools?.map((t) => t.name),
@@ -137,25 +139,21 @@ var LanggraphAgent = class extends import_client.AbstractAgent {
137
139
  "Preparing stream input"
138
140
  );
139
141
  logger.trace?.({ messages, tools: input.tools }, "Full input messages");
140
- const langChainMessages = isResume ? void 0 : aguiMessagesToLangChain(messages);
141
- if (langChainMessages) {
142
- logger.trace?.(
143
- {
144
- langChainMessages,
145
- messageCount: langChainMessages.length,
146
- messageTypes: langChainMessages.map((m) => ({
147
- type: m.type,
148
- id: m.id,
149
- hasToolCalls: "tool_calls" in m && Array.isArray(m.tool_calls) && m.tool_calls.length > 0,
150
- toolCallId: "tool_call_id" in m ? m.tool_call_id : void 0
151
- }))
152
- },
153
- "Converted LangGraph messages"
154
- );
155
- }
156
- const streamEventInput = isResume ? new import_langgraph.Command({
157
- resume: JSON.stringify(input.forwardedProps?.resume?.payload)
158
- }) : {
142
+ const langChainMessages = aguiMessagesToLangChain(messages, logger);
143
+ logger.trace?.(
144
+ {
145
+ langChainMessages,
146
+ messageCount: langChainMessages.length,
147
+ messageTypes: langChainMessages.map((m) => ({
148
+ type: m.type,
149
+ id: m.id,
150
+ hasToolCalls: "tool_calls" in m && Array.isArray(m.tool_calls) && m.tool_calls.length > 0,
151
+ toolCallId: "tool_call_id" in m ? m.tool_call_id : void 0
152
+ }))
153
+ },
154
+ "Converted LangGraph messages"
155
+ );
156
+ const streamEventInput = {
159
157
  messages: langChainMessages,
160
158
  client: {
161
159
  tools: convertActionsToDynamicStructuredTools(
@@ -168,6 +166,7 @@ var LanggraphAgent = class extends import_client.AbstractAgent {
168
166
  };
169
167
  const stream = this.compiledWorkflow.streamEvents(streamEventInput, {
170
168
  version: "v2",
169
+ ...this.observabilityCallback ? { callbacks: [this.observabilityCallback] } : {},
171
170
  runId,
172
171
  configurable: {
173
172
  thread_id: threadId
@@ -187,7 +186,6 @@ var LanggraphAgent = class extends import_client.AbstractAgent {
187
186
  "Pre-populated handled tool call IDs from input messages"
188
187
  );
189
188
  }
190
- let interrupt;
191
189
  let currentToolCall = null;
192
190
  let eventCount = 0;
193
191
  let toolCallCount = 0;
@@ -512,46 +510,17 @@ var LanggraphAgent = class extends import_client.AbstractAgent {
512
510
  "State update with messages"
513
511
  );
514
512
  }
515
- if (chunk?.__interrupt__ && Array.isArray(chunk.__interrupt__) && chunk.__interrupt__.length > 0) {
516
- const rawInterrupt = chunk.__interrupt__[0];
517
- logger.debug?.(
518
- { interruptId: rawInterrupt.id },
519
- "Interrupt received"
520
- );
521
- interrupt = {
522
- id: rawInterrupt.id,
523
- // TODO: replace with actual reason
524
- reason: "agent requested interrupt",
525
- payload: rawInterrupt.value
526
- };
527
- }
528
513
  }
529
514
  }
530
515
  const stats = { eventCount, toolCallCount, textChunkCount };
531
- if (interrupt) {
532
- const runFinishedEvent = {
533
- type: import_client.EventType.RUN_FINISHED,
534
- threadId,
535
- runId,
536
- outcome: "interrupt",
537
- interrupt
538
- };
539
- logger.info?.(
540
- { outcome: "interrupt", interruptId: interrupt.id, ...stats },
541
- "Run finished with interrupt"
542
- );
543
- logger.trace?.({ aguiEvent: runFinishedEvent }, "Emitting AGUI event");
544
- subscriber.next(runFinishedEvent);
545
- } else {
546
- const runFinishedEvent = {
547
- type: import_client.EventType.RUN_FINISHED,
548
- threadId,
549
- runId
550
- };
551
- logger.info?.({ outcome: "complete", ...stats }, "Run finished");
552
- logger.trace?.({ aguiEvent: runFinishedEvent }, "Emitting AGUI event");
553
- subscriber.next(runFinishedEvent);
554
- }
516
+ const runFinishedEvent = {
517
+ type: import_client.EventType.RUN_FINISHED,
518
+ threadId,
519
+ runId
520
+ };
521
+ logger.info?.({ outcome: "complete", ...stats }, "Run finished");
522
+ logger.trace?.({ aguiEvent: runFinishedEvent }, "Emitting AGUI event");
523
+ subscriber.next(runFinishedEvent);
555
524
  } catch (error) {
556
525
  logger.error?.(
557
526
  { err: error, eventCount, toolCallCount, textChunkCount },
@@ -577,9 +546,56 @@ var LanggraphAgent = class extends import_client.AbstractAgent {
577
546
  cloned.logger = logger;
578
547
  return cloned;
579
548
  }
549
+ /**
550
+ * Setup observability for agent execution.
551
+ * Lazy loads observability callback, restores server context from forwardedProps,
552
+ * and configures the callback for graph execution.
553
+ */
554
+ async setupObservability(input, logger) {
555
+ if (!LangChainCallbackHandler) {
556
+ try {
557
+ logger.debug?.("Attempting to load observability...");
558
+ const obsModule = await import("@cloudbase/agent-observability/langchain");
559
+ LangChainCallbackHandler = obsModule.CallbackHandler;
560
+ logger.debug?.("\u2713 Observability handler loaded");
561
+ } catch (e) {
562
+ logger.debug?.(
563
+ "\u2717 Observability not available:",
564
+ e instanceof Error ? e.message : String(e)
565
+ );
566
+ }
567
+ }
568
+ this.observabilityCallback = LangChainCallbackHandler ? new LangChainCallbackHandler({
569
+ adapterName: this.adapterName,
570
+ logger
571
+ }) : void 0;
572
+ if (this.observabilityCallback && input.forwardedProps?.__agui_server_context) {
573
+ try {
574
+ const serverContextData = input.forwardedProps.__agui_server_context;
575
+ const serverSpanContext = {
576
+ traceId: serverContextData.traceId,
577
+ spanId: serverContextData.spanId,
578
+ traceFlags: serverContextData.traceFlags,
579
+ isRemote: false
580
+ };
581
+ this.observabilityCallback.setExternalParentContext(serverSpanContext, {
582
+ threadId: input.threadId,
583
+ runId: input.runId
584
+ });
585
+ logger.debug?.("\u2713 Server context restored:", {
586
+ traceId: serverSpanContext.traceId,
587
+ spanId: serverSpanContext.spanId,
588
+ threadId: input.threadId,
589
+ runId: input.runId
590
+ });
591
+ } catch (e) {
592
+ logger.debug?.("Failed to restore server context:", e);
593
+ }
594
+ }
595
+ }
580
596
  };
581
- function aguiMessagesToLangChain(messages) {
582
- return messages.map((message, index) => {
597
+ function aguiMessagesToLangChain(messages, logger) {
598
+ return messages.map((message) => {
583
599
  switch (message.role) {
584
600
  case "user":
585
601
  if (typeof message.content === "string") {
@@ -590,10 +606,34 @@ function aguiMessagesToLangChain(messages) {
590
606
  type: "human"
591
607
  };
592
608
  } else {
609
+ const langChainContent = [];
610
+ for (const part of message.content) {
611
+ if (part.type === "text") {
612
+ langChainContent.push({ type: "text", text: part.text });
613
+ } else if (part.type === "binary") {
614
+ const imageUrl = convertBinaryToImageUrl(
615
+ part,
616
+ message.id,
617
+ logger
618
+ );
619
+ if (imageUrl) {
620
+ langChainContent.push({
621
+ type: "image_url",
622
+ image_url: { url: imageUrl }
623
+ });
624
+ }
625
+ }
626
+ }
627
+ if (langChainContent.length === 0) {
628
+ logger?.warn?.(
629
+ { messageId: message.id },
630
+ "User message content array resulted in empty LangChain content"
631
+ );
632
+ }
593
633
  return {
594
634
  id: message.id,
595
635
  role: message.role,
596
- content: message.content.filter((m) => m.type === "text"),
636
+ content: langChainContent,
597
637
  type: "human"
598
638
  };
599
639
  }
@@ -638,6 +678,22 @@ function isValidJson(json) {
638
678
  return false;
639
679
  }
640
680
  }
681
+ function convertBinaryToImageUrl(part, messageId, logger) {
682
+ if (!part.mimeType.startsWith("image/")) {
683
+ logger?.debug?.(
684
+ { mimeType: part.mimeType, messageId },
685
+ "Skipping non-image binary content"
686
+ );
687
+ return void 0;
688
+ }
689
+ if (part.url) {
690
+ return part.url;
691
+ }
692
+ if (part.data) {
693
+ return `data:${part.mimeType};base64,${part.data}`;
694
+ }
695
+ return void 0;
696
+ }
641
697
 
642
698
  // src/checkpoint.ts
643
699
  var import_langgraph2 = require("@langchain/langgraph");
@@ -889,9 +945,364 @@ var TDAISaver = class extends import_langgraph2.BaseCheckpointSaver {
889
945
  }
890
946
  };
891
947
 
892
- // src/store/tdai-store.ts
948
+ // src/cloudbase-saver.ts
893
949
  var import_langgraph3 = require("@langchain/langgraph");
894
- var TDAIStore = class extends import_langgraph3.BaseStore {
950
+
951
+ // ../../../node_modules/.pnpm/@langchain+langgraph-checkpoint@1.0.0_@langchain+core@1.1.16_@opentelemetry+api@1.9.0_@_6d00458ac2f8320fcf28949c4b5d31a3/node_modules/@langchain/langgraph-checkpoint/dist/serde/types.js
952
+ var ERROR = "__error__";
953
+ var SCHEDULED = "__scheduled__";
954
+ var INTERRUPT = "__interrupt__";
955
+ var RESUME = "__resume__";
956
+
957
+ // ../../../node_modules/.pnpm/@langchain+langgraph-checkpoint@1.0.0_@langchain+core@1.1.16_@opentelemetry+api@1.9.0_@_6d00458ac2f8320fcf28949c4b5d31a3/node_modules/@langchain/langgraph-checkpoint/dist/serde/jsonplus.js
958
+ var import_load = require("@langchain/core/load");
959
+
960
+ // ../../../node_modules/.pnpm/@langchain+langgraph-checkpoint@1.0.0_@langchain+core@1.1.16_@opentelemetry+api@1.9.0_@_6d00458ac2f8320fcf28949c4b5d31a3/node_modules/@langchain/langgraph-checkpoint/dist/base.js
961
+ function deepCopy(obj) {
962
+ if (typeof obj !== "object" || obj === null) return obj;
963
+ const newObj = Array.isArray(obj) ? [] : {};
964
+ for (const key in obj) if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = deepCopy(obj[key]);
965
+ return newObj;
966
+ }
967
+ function copyCheckpoint(checkpoint) {
968
+ return {
969
+ v: checkpoint.v,
970
+ id: checkpoint.id,
971
+ ts: checkpoint.ts,
972
+ channel_values: { ...checkpoint.channel_values },
973
+ channel_versions: { ...checkpoint.channel_versions },
974
+ versions_seen: deepCopy(checkpoint.versions_seen)
975
+ };
976
+ }
977
+ var WRITES_IDX_MAP = {
978
+ [ERROR]: -1,
979
+ [SCHEDULED]: -2,
980
+ [INTERRUPT]: -3,
981
+ [RESUME]: -4
982
+ };
983
+
984
+ // src/cloudbase-saver.ts
985
+ var CloudBaseSaver = class extends import_langgraph3.BaseCheckpointSaver {
986
+ constructor(config) {
987
+ super();
988
+ this.db = config.db;
989
+ this.userId = config.userId;
990
+ this.agentId = config.agentId ?? "default";
991
+ this.checkpointsCollection = config.checkpointsCollection ?? "checkpoints";
992
+ this.writesCollection = config.writesCollection ?? "checkpoint_writes";
993
+ }
994
+ /**
995
+ * Setup the required collections in CloudBase.
996
+ * Call this once before using the saver. Safe to call multiple times.
997
+ */
998
+ async setup() {
999
+ const createIfNotExists = async (name) => {
1000
+ try {
1001
+ await this.db.createCollection(name);
1002
+ } catch (e) {
1003
+ const error = e;
1004
+ if (error.code !== "DATABASE_COLLECTION_ALREADY_EXIST") {
1005
+ throw e;
1006
+ }
1007
+ }
1008
+ };
1009
+ await Promise.all([
1010
+ createIfNotExists(this.checkpointsCollection),
1011
+ createIfNotExists(this.writesCollection)
1012
+ ]);
1013
+ }
1014
+ /**
1015
+ * Serialize data to JSON object using serde.
1016
+ * CloudBase is a document database, so we store JSON directly instead of Base64.
1017
+ * Binary data (Uint8Array) is not supported - fail early if encountered.
1018
+ */
1019
+ async serialize(data) {
1020
+ const [type, bytes] = await this.serde.dumpsTyped(data);
1021
+ if (type === "bytes") {
1022
+ throw new Error(
1023
+ "Binary data (Uint8Array) is not supported in CloudBaseSaver. CloudBase is a document database that only supports JSON-serializable data."
1024
+ );
1025
+ }
1026
+ const jsonString = new TextDecoder().decode(bytes);
1027
+ return JSON.parse(jsonString);
1028
+ }
1029
+ /**
1030
+ * Deserialize JSON object back to data using serde.
1031
+ */
1032
+ async deserialize(data) {
1033
+ const jsonString = JSON.stringify(data);
1034
+ const bytes = new TextEncoder().encode(jsonString);
1035
+ return this.serde.loadsTyped("json", bytes);
1036
+ }
1037
+ /**
1038
+ * Retrieves a checkpoint from CloudBase based on the provided config.
1039
+ */
1040
+ async getTuple(config) {
1041
+ const {
1042
+ thread_id,
1043
+ checkpoint_ns = "",
1044
+ checkpoint_id
1045
+ } = config.configurable ?? {};
1046
+ if (!thread_id) {
1047
+ return void 0;
1048
+ }
1049
+ const query = {
1050
+ _openid: this.userId,
1051
+ agent_id: this.agentId,
1052
+ thread_id,
1053
+ checkpoint_ns
1054
+ };
1055
+ if (checkpoint_id) {
1056
+ query.checkpoint_id = checkpoint_id;
1057
+ }
1058
+ const { data: docs } = await this.db.collection(this.checkpointsCollection).where(query).orderBy("checkpoint_id", "desc").limit(1).get();
1059
+ if (docs.length === 0) {
1060
+ return void 0;
1061
+ }
1062
+ const doc = docs[0];
1063
+ const checkpointId = doc.checkpoint_id;
1064
+ const checkpoint = await this.deserialize(doc.checkpoint);
1065
+ const metadata = await this.deserialize(
1066
+ doc.metadata
1067
+ );
1068
+ const writeDocs = [];
1069
+ let writeSkip = 0;
1070
+ const writePageSize = 100;
1071
+ while (true) {
1072
+ const { data: docs2 } = await this.db.collection(this.writesCollection).where({
1073
+ _openid: this.userId,
1074
+ agent_id: this.agentId,
1075
+ thread_id,
1076
+ checkpoint_ns,
1077
+ checkpoint_id: checkpointId
1078
+ }).orderBy("idx", "asc").skip(writeSkip).limit(writePageSize).get();
1079
+ writeDocs.push(...docs2);
1080
+ if (docs2.length < writePageSize) break;
1081
+ writeSkip += docs2.length;
1082
+ }
1083
+ const pendingWrites = await Promise.all(
1084
+ writeDocs.map(async (writeDoc) => {
1085
+ const value = await this.deserialize(writeDoc.value);
1086
+ return [
1087
+ writeDoc.task_id,
1088
+ writeDoc.channel,
1089
+ value
1090
+ ];
1091
+ })
1092
+ );
1093
+ return {
1094
+ config: {
1095
+ configurable: {
1096
+ thread_id,
1097
+ checkpoint_ns,
1098
+ checkpoint_id: checkpointId
1099
+ }
1100
+ },
1101
+ checkpoint,
1102
+ metadata,
1103
+ pendingWrites,
1104
+ parentConfig: doc.parent_checkpoint_id ? {
1105
+ configurable: {
1106
+ thread_id,
1107
+ checkpoint_ns,
1108
+ checkpoint_id: doc.parent_checkpoint_id
1109
+ }
1110
+ } : void 0
1111
+ };
1112
+ }
1113
+ /**
1114
+ * List checkpoints from CloudBase, ordered by checkpoint_id descending.
1115
+ */
1116
+ async *list(config, options) {
1117
+ const { limit, before, filter } = options ?? {};
1118
+ const thread_id = config.configurable?.thread_id;
1119
+ const checkpoint_ns = config.configurable?.checkpoint_ns;
1120
+ if (!thread_id) {
1121
+ throw new Error("Thread ID is required");
1122
+ }
1123
+ const _ = this.db.command;
1124
+ const query = {
1125
+ _openid: this.userId,
1126
+ agent_id: this.agentId,
1127
+ thread_id
1128
+ };
1129
+ if (checkpoint_ns !== void 0 && checkpoint_ns !== null) {
1130
+ query.checkpoint_ns = checkpoint_ns;
1131
+ }
1132
+ if (before?.configurable?.checkpoint_id) {
1133
+ query.checkpoint_id = _.lt(before.configurable.checkpoint_id);
1134
+ }
1135
+ const validCheckpointMetadataKeys = ["source", "step", "writes", "parents"];
1136
+ if (filter) {
1137
+ for (const [key, value] of Object.entries(filter)) {
1138
+ if (value !== void 0 && validCheckpointMetadataKeys.includes(key)) {
1139
+ query[`metadata.${key}`] = value;
1140
+ }
1141
+ }
1142
+ }
1143
+ let skip = 0;
1144
+ const pageSize = Math.min(limit ?? 100, 100);
1145
+ let yielded = 0;
1146
+ while (true) {
1147
+ const { data: docs } = await this.db.collection(this.checkpointsCollection).where(query).orderBy("checkpoint_id", "desc").skip(skip).limit(pageSize).get();
1148
+ if (docs.length === 0) break;
1149
+ for (const doc of docs) {
1150
+ const checkpoint = await this.deserialize(
1151
+ doc.checkpoint
1152
+ );
1153
+ const metadata = await this.deserialize(
1154
+ doc.metadata
1155
+ );
1156
+ const writeDocs = [];
1157
+ let writeSkip = 0;
1158
+ const writePageSize = 100;
1159
+ while (true) {
1160
+ const { data: pageWriteDocs } = await this.db.collection(this.writesCollection).where({
1161
+ _openid: this.userId,
1162
+ agent_id: this.agentId,
1163
+ thread_id: doc.thread_id,
1164
+ checkpoint_ns: doc.checkpoint_ns,
1165
+ checkpoint_id: doc.checkpoint_id
1166
+ }).orderBy("idx", "asc").skip(writeSkip).limit(writePageSize).get();
1167
+ writeDocs.push(...pageWriteDocs);
1168
+ if (pageWriteDocs.length < writePageSize) break;
1169
+ writeSkip += pageWriteDocs.length;
1170
+ }
1171
+ const pendingWrites = await Promise.all(
1172
+ writeDocs.map(async (writeDoc) => {
1173
+ const value = await this.deserialize(writeDoc.value);
1174
+ return [
1175
+ writeDoc.task_id,
1176
+ writeDoc.channel,
1177
+ value
1178
+ ];
1179
+ })
1180
+ );
1181
+ yield {
1182
+ config: {
1183
+ configurable: {
1184
+ thread_id: doc.thread_id,
1185
+ checkpoint_ns: doc.checkpoint_ns,
1186
+ checkpoint_id: doc.checkpoint_id
1187
+ }
1188
+ },
1189
+ checkpoint,
1190
+ metadata,
1191
+ pendingWrites,
1192
+ parentConfig: doc.parent_checkpoint_id ? {
1193
+ configurable: {
1194
+ thread_id: doc.thread_id,
1195
+ checkpoint_ns: doc.checkpoint_ns,
1196
+ checkpoint_id: doc.parent_checkpoint_id
1197
+ }
1198
+ } : void 0
1199
+ };
1200
+ yielded++;
1201
+ if (limit && yielded >= limit) return;
1202
+ }
1203
+ skip += docs.length;
1204
+ if (docs.length < pageSize) break;
1205
+ }
1206
+ }
1207
+ /**
1208
+ * Save a checkpoint to CloudBase.
1209
+ */
1210
+ async put(config, checkpoint, metadata) {
1211
+ if (!config.configurable) {
1212
+ throw new Error("Empty configuration supplied.");
1213
+ }
1214
+ const thread_id = config.configurable.thread_id;
1215
+ const checkpoint_ns = config.configurable.checkpoint_ns ?? "";
1216
+ const checkpoint_id = checkpoint.id;
1217
+ if (!thread_id) {
1218
+ throw new Error(
1219
+ 'Missing "thread_id" field in passed "config.configurable".'
1220
+ );
1221
+ }
1222
+ const preparedCheckpoint = copyCheckpoint(checkpoint);
1223
+ const serializedCheckpoint = await this.serialize(preparedCheckpoint);
1224
+ const serializedMetadata = await this.serialize(metadata);
1225
+ const doc = {
1226
+ _openid: this.userId,
1227
+ agent_id: this.agentId,
1228
+ thread_id,
1229
+ checkpoint_ns,
1230
+ checkpoint_id,
1231
+ parent_checkpoint_id: config.configurable.checkpoint_id ?? null,
1232
+ checkpoint: serializedCheckpoint,
1233
+ metadata: serializedMetadata,
1234
+ created_at: /* @__PURE__ */ new Date()
1235
+ };
1236
+ const docId = `${this.agentId}:${thread_id}:${checkpoint_ns}:${checkpoint_id}`;
1237
+ await this.db.collection(this.checkpointsCollection).doc(docId).set(doc);
1238
+ return {
1239
+ configurable: {
1240
+ thread_id,
1241
+ checkpoint_ns,
1242
+ checkpoint_id
1243
+ }
1244
+ };
1245
+ }
1246
+ /**
1247
+ * Save intermediate writes to CloudBase.
1248
+ */
1249
+ async putWrites(config, writes, taskId) {
1250
+ if (!config.configurable) {
1251
+ throw new Error("Empty configuration supplied.");
1252
+ }
1253
+ if (!config.configurable.thread_id) {
1254
+ throw new Error("Missing thread_id field in config.configurable.");
1255
+ }
1256
+ if (!config.configurable.checkpoint_id) {
1257
+ throw new Error("Missing checkpoint_id field in config.configurable.");
1258
+ }
1259
+ const thread_id = config.configurable.thread_id;
1260
+ const checkpoint_ns = config.configurable.checkpoint_ns ?? "";
1261
+ const checkpoint_id = config.configurable.checkpoint_id;
1262
+ const serializedWrites = await Promise.all(
1263
+ writes.map(async ([channel, value], idx) => ({
1264
+ channel,
1265
+ value: await this.serialize(value),
1266
+ idx,
1267
+ docId: `${this.agentId}:${thread_id}:${checkpoint_ns}:${checkpoint_id}:${taskId}:${idx}`
1268
+ }))
1269
+ );
1270
+ await this.db.runTransaction(async (transaction) => {
1271
+ for (const { channel, value, idx, docId } of serializedWrites) {
1272
+ await transaction.collection(this.writesCollection).doc(docId).set({
1273
+ _openid: this.userId,
1274
+ agent_id: this.agentId,
1275
+ thread_id,
1276
+ checkpoint_ns,
1277
+ checkpoint_id,
1278
+ task_id: taskId,
1279
+ idx,
1280
+ channel,
1281
+ value
1282
+ });
1283
+ }
1284
+ });
1285
+ }
1286
+ /**
1287
+ * Delete all checkpoints and writes for a thread.
1288
+ */
1289
+ async deleteThread(threadId) {
1290
+ await this.db.collection(this.checkpointsCollection).where({
1291
+ _openid: this.userId,
1292
+ agent_id: this.agentId,
1293
+ thread_id: threadId
1294
+ }).remove();
1295
+ await this.db.collection(this.writesCollection).where({
1296
+ _openid: this.userId,
1297
+ agent_id: this.agentId,
1298
+ thread_id: threadId
1299
+ }).remove();
1300
+ }
1301
+ };
1302
+
1303
+ // src/store/tdai-store.ts
1304
+ var import_langgraph4 = require("@langchain/langgraph");
1305
+ var TDAIStore = class extends import_langgraph4.BaseStore {
895
1306
  constructor(config) {
896
1307
  super();
897
1308
  this.isSetup = false;
@@ -1321,6 +1732,7 @@ var import_agent_shared2 = require("@cloudbase/agent-shared");
1321
1732
  0 && (module.exports = {
1322
1733
  ClientPropertiesAnnotation,
1323
1734
  ClientStateAnnotation,
1735
+ CloudBaseSaver,
1324
1736
  LanggraphAgent,
1325
1737
  TDAISaver,
1326
1738
  TDAIStore,