@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/README.md +121 -0
- package/dist/index.d.mts +79 -2
- package/dist/index.d.ts +79 -2
- package/dist/index.js +476 -64
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +478 -66
- package/dist/index.mjs.map +1 -1
- package/package.json +23 -9
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 =
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
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
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
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
|
|
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:
|
|
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/
|
|
948
|
+
// src/cloudbase-saver.ts
|
|
893
949
|
var import_langgraph3 = require("@langchain/langgraph");
|
|
894
|
-
|
|
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,
|