@cloudbase/agent-adapter-langgraph 0.0.16 → 0.0.19
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 +75 -1
- package/dist/index.d.ts +75 -1
- package/dist/index.js +499 -2675
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +475 -108
- package/dist/index.mjs.map +1 -1
- package/package.json +18 -9
- package/dist/chunk-NSSMTXJJ.mjs +0 -8
- package/dist/chunk-NSSMTXJJ.mjs.map +0 -1
- package/dist/langchain-6EY35CAB.mjs +0 -2323
- package/dist/langchain-6EY35CAB.mjs.map +0 -1
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import "./chunk-NSSMTXJJ.mjs";
|
|
2
|
-
|
|
3
1
|
// src/agent.ts
|
|
4
2
|
import {
|
|
5
3
|
EventType,
|
|
@@ -8,8 +6,7 @@ import {
|
|
|
8
6
|
import { Observable } from "rxjs";
|
|
9
7
|
import {
|
|
10
8
|
Annotation,
|
|
11
|
-
MessagesAnnotation
|
|
12
|
-
Command
|
|
9
|
+
MessagesAnnotation
|
|
13
10
|
} from "@langchain/langgraph";
|
|
14
11
|
|
|
15
12
|
// src/util.ts
|
|
@@ -62,7 +59,6 @@ function convertJsonSchemaToZodSchema(jsonSchema, required) {
|
|
|
62
59
|
// src/agent.ts
|
|
63
60
|
import { noopLogger, isErrorWithCode } from "@cloudbase/agent-shared";
|
|
64
61
|
var LangChainCallbackHandler;
|
|
65
|
-
var observabilityLoadAttempted = false;
|
|
66
62
|
var ClientPropertiesAnnotation = Annotation.Root({
|
|
67
63
|
tools: Annotation
|
|
68
64
|
});
|
|
@@ -87,45 +83,7 @@ var LanggraphAgent = class extends AbstractAgent {
|
|
|
87
83
|
const { messages, runId, threadId } = input;
|
|
88
84
|
const logger = this.logger.child?.({ runId, threadId }) ?? this.logger;
|
|
89
85
|
logger.info?.("Run started");
|
|
90
|
-
|
|
91
|
-
observabilityLoadAttempted = true;
|
|
92
|
-
try {
|
|
93
|
-
logger.debug?.("Attempting to load observability...");
|
|
94
|
-
const obsModule = await import("./langchain-6EY35CAB.mjs");
|
|
95
|
-
LangChainCallbackHandler = obsModule.CallbackHandler;
|
|
96
|
-
if (LangChainCallbackHandler) {
|
|
97
|
-
this.observabilityCallback = new LangChainCallbackHandler({
|
|
98
|
-
adapterName: this.adapterName,
|
|
99
|
-
logger
|
|
100
|
-
// Reuse the run-specific logger with context
|
|
101
|
-
});
|
|
102
|
-
logger.debug?.("\u2713 Observability callback created");
|
|
103
|
-
}
|
|
104
|
-
} catch (e) {
|
|
105
|
-
logger.debug?.(
|
|
106
|
-
"\u2717 Observability not available:",
|
|
107
|
-
e instanceof Error ? e.message : String(e)
|
|
108
|
-
);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
if (this.observabilityCallback && input.forwardedProps?.__agui_server_context) {
|
|
112
|
-
try {
|
|
113
|
-
const serverContextData = input.forwardedProps.__agui_server_context;
|
|
114
|
-
const serverSpanContext = {
|
|
115
|
-
traceId: serverContextData.traceId,
|
|
116
|
-
spanId: serverContextData.spanId,
|
|
117
|
-
traceFlags: serverContextData.traceFlags,
|
|
118
|
-
isRemote: false
|
|
119
|
-
};
|
|
120
|
-
this.observabilityCallback.setExternalParentContext(serverSpanContext);
|
|
121
|
-
logger.debug?.("\u2713 Server context restored:", {
|
|
122
|
-
traceId: serverSpanContext.traceId,
|
|
123
|
-
spanId: serverSpanContext.spanId
|
|
124
|
-
});
|
|
125
|
-
} catch (e) {
|
|
126
|
-
logger.debug?.("Failed to restore server context:", e);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
86
|
+
await this.setupObservability(input, logger);
|
|
129
87
|
const runStartedEvent = {
|
|
130
88
|
type: EventType.RUN_STARTED,
|
|
131
89
|
threadId,
|
|
@@ -133,11 +91,9 @@ var LanggraphAgent = class extends AbstractAgent {
|
|
|
133
91
|
};
|
|
134
92
|
logger.trace?.({ aguiEvent: runStartedEvent }, "Emitting AGUI event");
|
|
135
93
|
subscriber.next(runStartedEvent);
|
|
136
|
-
const isResume = !!input.forwardedProps?.resume;
|
|
137
94
|
const lastUserMessage = messages.filter((m) => m.role === "user").pop();
|
|
138
95
|
logger.debug?.(
|
|
139
96
|
{
|
|
140
|
-
isResume,
|
|
141
97
|
messageCount: messages.length,
|
|
142
98
|
toolCount: input.tools?.length ?? 0,
|
|
143
99
|
tools: input.tools?.map((t) => t.name),
|
|
@@ -146,25 +102,21 @@ var LanggraphAgent = class extends AbstractAgent {
|
|
|
146
102
|
"Preparing stream input"
|
|
147
103
|
);
|
|
148
104
|
logger.trace?.({ messages, tools: input.tools }, "Full input messages");
|
|
149
|
-
const langChainMessages =
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
}
|
|
165
|
-
const streamEventInput = isResume ? new Command({
|
|
166
|
-
resume: JSON.stringify(input.forwardedProps?.resume?.payload)
|
|
167
|
-
}) : {
|
|
105
|
+
const langChainMessages = aguiMessagesToLangChain(messages, logger);
|
|
106
|
+
logger.trace?.(
|
|
107
|
+
{
|
|
108
|
+
langChainMessages,
|
|
109
|
+
messageCount: langChainMessages.length,
|
|
110
|
+
messageTypes: langChainMessages.map((m) => ({
|
|
111
|
+
type: m.type,
|
|
112
|
+
id: m.id,
|
|
113
|
+
hasToolCalls: "tool_calls" in m && Array.isArray(m.tool_calls) && m.tool_calls.length > 0,
|
|
114
|
+
toolCallId: "tool_call_id" in m ? m.tool_call_id : void 0
|
|
115
|
+
}))
|
|
116
|
+
},
|
|
117
|
+
"Converted LangGraph messages"
|
|
118
|
+
);
|
|
119
|
+
const streamEventInput = {
|
|
168
120
|
messages: langChainMessages,
|
|
169
121
|
client: {
|
|
170
122
|
tools: convertActionsToDynamicStructuredTools(
|
|
@@ -197,7 +149,6 @@ var LanggraphAgent = class extends AbstractAgent {
|
|
|
197
149
|
"Pre-populated handled tool call IDs from input messages"
|
|
198
150
|
);
|
|
199
151
|
}
|
|
200
|
-
let interrupt;
|
|
201
152
|
let currentToolCall = null;
|
|
202
153
|
let eventCount = 0;
|
|
203
154
|
let toolCallCount = 0;
|
|
@@ -522,46 +473,17 @@ var LanggraphAgent = class extends AbstractAgent {
|
|
|
522
473
|
"State update with messages"
|
|
523
474
|
);
|
|
524
475
|
}
|
|
525
|
-
if (chunk?.__interrupt__ && Array.isArray(chunk.__interrupt__) && chunk.__interrupt__.length > 0) {
|
|
526
|
-
const rawInterrupt = chunk.__interrupt__[0];
|
|
527
|
-
logger.debug?.(
|
|
528
|
-
{ interruptId: rawInterrupt.id },
|
|
529
|
-
"Interrupt received"
|
|
530
|
-
);
|
|
531
|
-
interrupt = {
|
|
532
|
-
id: rawInterrupt.id,
|
|
533
|
-
// TODO: replace with actual reason
|
|
534
|
-
reason: "agent requested interrupt",
|
|
535
|
-
payload: rawInterrupt.value
|
|
536
|
-
};
|
|
537
|
-
}
|
|
538
476
|
}
|
|
539
477
|
}
|
|
540
478
|
const stats = { eventCount, toolCallCount, textChunkCount };
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
logger.info?.(
|
|
550
|
-
{ outcome: "interrupt", interruptId: interrupt.id, ...stats },
|
|
551
|
-
"Run finished with interrupt"
|
|
552
|
-
);
|
|
553
|
-
logger.trace?.({ aguiEvent: runFinishedEvent }, "Emitting AGUI event");
|
|
554
|
-
subscriber.next(runFinishedEvent);
|
|
555
|
-
} else {
|
|
556
|
-
const runFinishedEvent = {
|
|
557
|
-
type: EventType.RUN_FINISHED,
|
|
558
|
-
threadId,
|
|
559
|
-
runId
|
|
560
|
-
};
|
|
561
|
-
logger.info?.({ outcome: "complete", ...stats }, "Run finished");
|
|
562
|
-
logger.trace?.({ aguiEvent: runFinishedEvent }, "Emitting AGUI event");
|
|
563
|
-
subscriber.next(runFinishedEvent);
|
|
564
|
-
}
|
|
479
|
+
const runFinishedEvent = {
|
|
480
|
+
type: EventType.RUN_FINISHED,
|
|
481
|
+
threadId,
|
|
482
|
+
runId
|
|
483
|
+
};
|
|
484
|
+
logger.info?.({ outcome: "complete", ...stats }, "Run finished");
|
|
485
|
+
logger.trace?.({ aguiEvent: runFinishedEvent }, "Emitting AGUI event");
|
|
486
|
+
subscriber.next(runFinishedEvent);
|
|
565
487
|
} catch (error) {
|
|
566
488
|
logger.error?.(
|
|
567
489
|
{ err: error, eventCount, toolCallCount, textChunkCount },
|
|
@@ -587,9 +509,56 @@ var LanggraphAgent = class extends AbstractAgent {
|
|
|
587
509
|
cloned.logger = logger;
|
|
588
510
|
return cloned;
|
|
589
511
|
}
|
|
512
|
+
/**
|
|
513
|
+
* Setup observability for agent execution.
|
|
514
|
+
* Lazy loads observability callback, restores server context from forwardedProps,
|
|
515
|
+
* and configures the callback for graph execution.
|
|
516
|
+
*/
|
|
517
|
+
async setupObservability(input, logger) {
|
|
518
|
+
if (!LangChainCallbackHandler) {
|
|
519
|
+
try {
|
|
520
|
+
logger.debug?.("Attempting to load observability...");
|
|
521
|
+
const obsModule = await import("@cloudbase/agent-observability/langchain");
|
|
522
|
+
LangChainCallbackHandler = obsModule.CallbackHandler;
|
|
523
|
+
logger.debug?.("\u2713 Observability handler loaded");
|
|
524
|
+
} catch (e) {
|
|
525
|
+
logger.debug?.(
|
|
526
|
+
"\u2717 Observability not available:",
|
|
527
|
+
e instanceof Error ? e.message : String(e)
|
|
528
|
+
);
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
this.observabilityCallback = LangChainCallbackHandler ? new LangChainCallbackHandler({
|
|
532
|
+
adapterName: this.adapterName,
|
|
533
|
+
logger
|
|
534
|
+
}) : void 0;
|
|
535
|
+
if (this.observabilityCallback && input.forwardedProps?.__agui_server_context) {
|
|
536
|
+
try {
|
|
537
|
+
const serverContextData = input.forwardedProps.__agui_server_context;
|
|
538
|
+
const serverSpanContext = {
|
|
539
|
+
traceId: serverContextData.traceId,
|
|
540
|
+
spanId: serverContextData.spanId,
|
|
541
|
+
traceFlags: serverContextData.traceFlags,
|
|
542
|
+
isRemote: false
|
|
543
|
+
};
|
|
544
|
+
this.observabilityCallback.setExternalParentContext(serverSpanContext, {
|
|
545
|
+
threadId: input.threadId,
|
|
546
|
+
runId: input.runId
|
|
547
|
+
});
|
|
548
|
+
logger.debug?.("\u2713 Server context restored:", {
|
|
549
|
+
traceId: serverSpanContext.traceId,
|
|
550
|
+
spanId: serverSpanContext.spanId,
|
|
551
|
+
threadId: input.threadId,
|
|
552
|
+
runId: input.runId
|
|
553
|
+
});
|
|
554
|
+
} catch (e) {
|
|
555
|
+
logger.debug?.("Failed to restore server context:", e);
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
}
|
|
590
559
|
};
|
|
591
|
-
function aguiMessagesToLangChain(messages) {
|
|
592
|
-
return messages.map((message
|
|
560
|
+
function aguiMessagesToLangChain(messages, logger) {
|
|
561
|
+
return messages.map((message) => {
|
|
593
562
|
switch (message.role) {
|
|
594
563
|
case "user":
|
|
595
564
|
if (typeof message.content === "string") {
|
|
@@ -600,10 +569,34 @@ function aguiMessagesToLangChain(messages) {
|
|
|
600
569
|
type: "human"
|
|
601
570
|
};
|
|
602
571
|
} else {
|
|
572
|
+
const langChainContent = [];
|
|
573
|
+
for (const part of message.content) {
|
|
574
|
+
if (part.type === "text") {
|
|
575
|
+
langChainContent.push({ type: "text", text: part.text });
|
|
576
|
+
} else if (part.type === "binary") {
|
|
577
|
+
const imageUrl = convertBinaryToImageUrl(
|
|
578
|
+
part,
|
|
579
|
+
message.id,
|
|
580
|
+
logger
|
|
581
|
+
);
|
|
582
|
+
if (imageUrl) {
|
|
583
|
+
langChainContent.push({
|
|
584
|
+
type: "image_url",
|
|
585
|
+
image_url: { url: imageUrl }
|
|
586
|
+
});
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
if (langChainContent.length === 0) {
|
|
591
|
+
logger?.warn?.(
|
|
592
|
+
{ messageId: message.id },
|
|
593
|
+
"User message content array resulted in empty LangChain content"
|
|
594
|
+
);
|
|
595
|
+
}
|
|
603
596
|
return {
|
|
604
597
|
id: message.id,
|
|
605
598
|
role: message.role,
|
|
606
|
-
content:
|
|
599
|
+
content: langChainContent,
|
|
607
600
|
type: "human"
|
|
608
601
|
};
|
|
609
602
|
}
|
|
@@ -648,6 +641,22 @@ function isValidJson(json) {
|
|
|
648
641
|
return false;
|
|
649
642
|
}
|
|
650
643
|
}
|
|
644
|
+
function convertBinaryToImageUrl(part, messageId, logger) {
|
|
645
|
+
if (!part.mimeType.startsWith("image/")) {
|
|
646
|
+
logger?.debug?.(
|
|
647
|
+
{ mimeType: part.mimeType, messageId },
|
|
648
|
+
"Skipping non-image binary content"
|
|
649
|
+
);
|
|
650
|
+
return void 0;
|
|
651
|
+
}
|
|
652
|
+
if (part.url) {
|
|
653
|
+
return part.url;
|
|
654
|
+
}
|
|
655
|
+
if (part.data) {
|
|
656
|
+
return `data:${part.mimeType};base64,${part.data}`;
|
|
657
|
+
}
|
|
658
|
+
return void 0;
|
|
659
|
+
}
|
|
651
660
|
|
|
652
661
|
// src/checkpoint.ts
|
|
653
662
|
import {
|
|
@@ -904,11 +913,368 @@ var TDAISaver = class extends BaseCheckpointSaver {
|
|
|
904
913
|
}
|
|
905
914
|
};
|
|
906
915
|
|
|
916
|
+
// src/cloudbase-saver.ts
|
|
917
|
+
import {
|
|
918
|
+
BaseCheckpointSaver as BaseCheckpointSaver3
|
|
919
|
+
} from "@langchain/langgraph";
|
|
920
|
+
|
|
921
|
+
// ../../../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
|
|
922
|
+
var ERROR = "__error__";
|
|
923
|
+
var SCHEDULED = "__scheduled__";
|
|
924
|
+
var INTERRUPT = "__interrupt__";
|
|
925
|
+
var RESUME = "__resume__";
|
|
926
|
+
|
|
927
|
+
// ../../../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
|
|
928
|
+
import { load } from "@langchain/core/load";
|
|
929
|
+
|
|
930
|
+
// ../../../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
|
|
931
|
+
function deepCopy(obj) {
|
|
932
|
+
if (typeof obj !== "object" || obj === null) return obj;
|
|
933
|
+
const newObj = Array.isArray(obj) ? [] : {};
|
|
934
|
+
for (const key in obj) if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = deepCopy(obj[key]);
|
|
935
|
+
return newObj;
|
|
936
|
+
}
|
|
937
|
+
function copyCheckpoint(checkpoint) {
|
|
938
|
+
return {
|
|
939
|
+
v: checkpoint.v,
|
|
940
|
+
id: checkpoint.id,
|
|
941
|
+
ts: checkpoint.ts,
|
|
942
|
+
channel_values: { ...checkpoint.channel_values },
|
|
943
|
+
channel_versions: { ...checkpoint.channel_versions },
|
|
944
|
+
versions_seen: deepCopy(checkpoint.versions_seen)
|
|
945
|
+
};
|
|
946
|
+
}
|
|
947
|
+
var WRITES_IDX_MAP = {
|
|
948
|
+
[ERROR]: -1,
|
|
949
|
+
[SCHEDULED]: -2,
|
|
950
|
+
[INTERRUPT]: -3,
|
|
951
|
+
[RESUME]: -4
|
|
952
|
+
};
|
|
953
|
+
|
|
954
|
+
// src/cloudbase-saver.ts
|
|
955
|
+
var CloudBaseSaver = class extends BaseCheckpointSaver3 {
|
|
956
|
+
constructor(config) {
|
|
957
|
+
super();
|
|
958
|
+
this.db = config.db;
|
|
959
|
+
this.userId = config.userId;
|
|
960
|
+
this.agentId = config.agentId ?? "default";
|
|
961
|
+
this.checkpointsCollection = config.checkpointsCollection ?? "checkpoints";
|
|
962
|
+
this.writesCollection = config.writesCollection ?? "checkpoint_writes";
|
|
963
|
+
}
|
|
964
|
+
/**
|
|
965
|
+
* Setup the required collections in CloudBase.
|
|
966
|
+
* Call this once before using the saver. Safe to call multiple times.
|
|
967
|
+
*/
|
|
968
|
+
async setup() {
|
|
969
|
+
const createIfNotExists = async (name) => {
|
|
970
|
+
try {
|
|
971
|
+
await this.db.createCollection(name);
|
|
972
|
+
} catch (e) {
|
|
973
|
+
const error = e;
|
|
974
|
+
if (error.code !== "DATABASE_COLLECTION_ALREADY_EXIST") {
|
|
975
|
+
throw e;
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
};
|
|
979
|
+
await Promise.all([
|
|
980
|
+
createIfNotExists(this.checkpointsCollection),
|
|
981
|
+
createIfNotExists(this.writesCollection)
|
|
982
|
+
]);
|
|
983
|
+
}
|
|
984
|
+
/**
|
|
985
|
+
* Serialize data to JSON object using serde.
|
|
986
|
+
* CloudBase is a document database, so we store JSON directly instead of Base64.
|
|
987
|
+
* Binary data (Uint8Array) is not supported - fail early if encountered.
|
|
988
|
+
*/
|
|
989
|
+
async serialize(data) {
|
|
990
|
+
const [type, bytes] = await this.serde.dumpsTyped(data);
|
|
991
|
+
if (type === "bytes") {
|
|
992
|
+
throw new Error(
|
|
993
|
+
"Binary data (Uint8Array) is not supported in CloudBaseSaver. CloudBase is a document database that only supports JSON-serializable data."
|
|
994
|
+
);
|
|
995
|
+
}
|
|
996
|
+
const jsonString = new TextDecoder().decode(bytes);
|
|
997
|
+
return JSON.parse(jsonString);
|
|
998
|
+
}
|
|
999
|
+
/**
|
|
1000
|
+
* Deserialize JSON object back to data using serde.
|
|
1001
|
+
*/
|
|
1002
|
+
async deserialize(data) {
|
|
1003
|
+
const jsonString = JSON.stringify(data);
|
|
1004
|
+
const bytes = new TextEncoder().encode(jsonString);
|
|
1005
|
+
return this.serde.loadsTyped("json", bytes);
|
|
1006
|
+
}
|
|
1007
|
+
/**
|
|
1008
|
+
* Retrieves a checkpoint from CloudBase based on the provided config.
|
|
1009
|
+
*/
|
|
1010
|
+
async getTuple(config) {
|
|
1011
|
+
const {
|
|
1012
|
+
thread_id,
|
|
1013
|
+
checkpoint_ns = "",
|
|
1014
|
+
checkpoint_id
|
|
1015
|
+
} = config.configurable ?? {};
|
|
1016
|
+
if (!thread_id) {
|
|
1017
|
+
return void 0;
|
|
1018
|
+
}
|
|
1019
|
+
const query = {
|
|
1020
|
+
_openid: this.userId,
|
|
1021
|
+
agent_id: this.agentId,
|
|
1022
|
+
thread_id,
|
|
1023
|
+
checkpoint_ns
|
|
1024
|
+
};
|
|
1025
|
+
if (checkpoint_id) {
|
|
1026
|
+
query.checkpoint_id = checkpoint_id;
|
|
1027
|
+
}
|
|
1028
|
+
const { data: docs } = await this.db.collection(this.checkpointsCollection).where(query).orderBy("checkpoint_id", "desc").limit(1).get();
|
|
1029
|
+
if (docs.length === 0) {
|
|
1030
|
+
return void 0;
|
|
1031
|
+
}
|
|
1032
|
+
const doc = docs[0];
|
|
1033
|
+
const checkpointId = doc.checkpoint_id;
|
|
1034
|
+
const checkpoint = await this.deserialize(doc.checkpoint);
|
|
1035
|
+
const metadata = await this.deserialize(
|
|
1036
|
+
doc.metadata
|
|
1037
|
+
);
|
|
1038
|
+
const writeDocs = [];
|
|
1039
|
+
let writeSkip = 0;
|
|
1040
|
+
const writePageSize = 100;
|
|
1041
|
+
while (true) {
|
|
1042
|
+
const { data: docs2 } = await this.db.collection(this.writesCollection).where({
|
|
1043
|
+
_openid: this.userId,
|
|
1044
|
+
agent_id: this.agentId,
|
|
1045
|
+
thread_id,
|
|
1046
|
+
checkpoint_ns,
|
|
1047
|
+
checkpoint_id: checkpointId
|
|
1048
|
+
}).orderBy("idx", "asc").skip(writeSkip).limit(writePageSize).get();
|
|
1049
|
+
writeDocs.push(...docs2);
|
|
1050
|
+
if (docs2.length < writePageSize) break;
|
|
1051
|
+
writeSkip += docs2.length;
|
|
1052
|
+
}
|
|
1053
|
+
const pendingWrites = await Promise.all(
|
|
1054
|
+
writeDocs.map(async (writeDoc) => {
|
|
1055
|
+
const value = await this.deserialize(writeDoc.value);
|
|
1056
|
+
return [
|
|
1057
|
+
writeDoc.task_id,
|
|
1058
|
+
writeDoc.channel,
|
|
1059
|
+
value
|
|
1060
|
+
];
|
|
1061
|
+
})
|
|
1062
|
+
);
|
|
1063
|
+
return {
|
|
1064
|
+
config: {
|
|
1065
|
+
configurable: {
|
|
1066
|
+
thread_id,
|
|
1067
|
+
checkpoint_ns,
|
|
1068
|
+
checkpoint_id: checkpointId
|
|
1069
|
+
}
|
|
1070
|
+
},
|
|
1071
|
+
checkpoint,
|
|
1072
|
+
metadata,
|
|
1073
|
+
pendingWrites,
|
|
1074
|
+
parentConfig: doc.parent_checkpoint_id ? {
|
|
1075
|
+
configurable: {
|
|
1076
|
+
thread_id,
|
|
1077
|
+
checkpoint_ns,
|
|
1078
|
+
checkpoint_id: doc.parent_checkpoint_id
|
|
1079
|
+
}
|
|
1080
|
+
} : void 0
|
|
1081
|
+
};
|
|
1082
|
+
}
|
|
1083
|
+
/**
|
|
1084
|
+
* List checkpoints from CloudBase, ordered by checkpoint_id descending.
|
|
1085
|
+
*/
|
|
1086
|
+
async *list(config, options) {
|
|
1087
|
+
const { limit, before, filter } = options ?? {};
|
|
1088
|
+
const thread_id = config.configurable?.thread_id;
|
|
1089
|
+
const checkpoint_ns = config.configurable?.checkpoint_ns;
|
|
1090
|
+
if (!thread_id) {
|
|
1091
|
+
throw new Error("Thread ID is required");
|
|
1092
|
+
}
|
|
1093
|
+
const _ = this.db.command;
|
|
1094
|
+
const query = {
|
|
1095
|
+
_openid: this.userId,
|
|
1096
|
+
agent_id: this.agentId,
|
|
1097
|
+
thread_id
|
|
1098
|
+
};
|
|
1099
|
+
if (checkpoint_ns !== void 0 && checkpoint_ns !== null) {
|
|
1100
|
+
query.checkpoint_ns = checkpoint_ns;
|
|
1101
|
+
}
|
|
1102
|
+
if (before?.configurable?.checkpoint_id) {
|
|
1103
|
+
query.checkpoint_id = _.lt(before.configurable.checkpoint_id);
|
|
1104
|
+
}
|
|
1105
|
+
const validCheckpointMetadataKeys = ["source", "step", "writes", "parents"];
|
|
1106
|
+
if (filter) {
|
|
1107
|
+
for (const [key, value] of Object.entries(filter)) {
|
|
1108
|
+
if (value !== void 0 && validCheckpointMetadataKeys.includes(key)) {
|
|
1109
|
+
query[`metadata.${key}`] = value;
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
let skip = 0;
|
|
1114
|
+
const pageSize = Math.min(limit ?? 100, 100);
|
|
1115
|
+
let yielded = 0;
|
|
1116
|
+
while (true) {
|
|
1117
|
+
const { data: docs } = await this.db.collection(this.checkpointsCollection).where(query).orderBy("checkpoint_id", "desc").skip(skip).limit(pageSize).get();
|
|
1118
|
+
if (docs.length === 0) break;
|
|
1119
|
+
for (const doc of docs) {
|
|
1120
|
+
const checkpoint = await this.deserialize(
|
|
1121
|
+
doc.checkpoint
|
|
1122
|
+
);
|
|
1123
|
+
const metadata = await this.deserialize(
|
|
1124
|
+
doc.metadata
|
|
1125
|
+
);
|
|
1126
|
+
const writeDocs = [];
|
|
1127
|
+
let writeSkip = 0;
|
|
1128
|
+
const writePageSize = 100;
|
|
1129
|
+
while (true) {
|
|
1130
|
+
const { data: pageWriteDocs } = await this.db.collection(this.writesCollection).where({
|
|
1131
|
+
_openid: this.userId,
|
|
1132
|
+
agent_id: this.agentId,
|
|
1133
|
+
thread_id: doc.thread_id,
|
|
1134
|
+
checkpoint_ns: doc.checkpoint_ns,
|
|
1135
|
+
checkpoint_id: doc.checkpoint_id
|
|
1136
|
+
}).orderBy("idx", "asc").skip(writeSkip).limit(writePageSize).get();
|
|
1137
|
+
writeDocs.push(...pageWriteDocs);
|
|
1138
|
+
if (pageWriteDocs.length < writePageSize) break;
|
|
1139
|
+
writeSkip += pageWriteDocs.length;
|
|
1140
|
+
}
|
|
1141
|
+
const pendingWrites = await Promise.all(
|
|
1142
|
+
writeDocs.map(async (writeDoc) => {
|
|
1143
|
+
const value = await this.deserialize(writeDoc.value);
|
|
1144
|
+
return [
|
|
1145
|
+
writeDoc.task_id,
|
|
1146
|
+
writeDoc.channel,
|
|
1147
|
+
value
|
|
1148
|
+
];
|
|
1149
|
+
})
|
|
1150
|
+
);
|
|
1151
|
+
yield {
|
|
1152
|
+
config: {
|
|
1153
|
+
configurable: {
|
|
1154
|
+
thread_id: doc.thread_id,
|
|
1155
|
+
checkpoint_ns: doc.checkpoint_ns,
|
|
1156
|
+
checkpoint_id: doc.checkpoint_id
|
|
1157
|
+
}
|
|
1158
|
+
},
|
|
1159
|
+
checkpoint,
|
|
1160
|
+
metadata,
|
|
1161
|
+
pendingWrites,
|
|
1162
|
+
parentConfig: doc.parent_checkpoint_id ? {
|
|
1163
|
+
configurable: {
|
|
1164
|
+
thread_id: doc.thread_id,
|
|
1165
|
+
checkpoint_ns: doc.checkpoint_ns,
|
|
1166
|
+
checkpoint_id: doc.parent_checkpoint_id
|
|
1167
|
+
}
|
|
1168
|
+
} : void 0
|
|
1169
|
+
};
|
|
1170
|
+
yielded++;
|
|
1171
|
+
if (limit && yielded >= limit) return;
|
|
1172
|
+
}
|
|
1173
|
+
skip += docs.length;
|
|
1174
|
+
if (docs.length < pageSize) break;
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
/**
|
|
1178
|
+
* Save a checkpoint to CloudBase.
|
|
1179
|
+
*/
|
|
1180
|
+
async put(config, checkpoint, metadata) {
|
|
1181
|
+
if (!config.configurable) {
|
|
1182
|
+
throw new Error("Empty configuration supplied.");
|
|
1183
|
+
}
|
|
1184
|
+
const thread_id = config.configurable.thread_id;
|
|
1185
|
+
const checkpoint_ns = config.configurable.checkpoint_ns ?? "";
|
|
1186
|
+
const checkpoint_id = checkpoint.id;
|
|
1187
|
+
if (!thread_id) {
|
|
1188
|
+
throw new Error(
|
|
1189
|
+
'Missing "thread_id" field in passed "config.configurable".'
|
|
1190
|
+
);
|
|
1191
|
+
}
|
|
1192
|
+
const preparedCheckpoint = copyCheckpoint(checkpoint);
|
|
1193
|
+
const serializedCheckpoint = await this.serialize(preparedCheckpoint);
|
|
1194
|
+
const serializedMetadata = await this.serialize(metadata);
|
|
1195
|
+
const doc = {
|
|
1196
|
+
_openid: this.userId,
|
|
1197
|
+
agent_id: this.agentId,
|
|
1198
|
+
thread_id,
|
|
1199
|
+
checkpoint_ns,
|
|
1200
|
+
checkpoint_id,
|
|
1201
|
+
parent_checkpoint_id: config.configurable.checkpoint_id ?? null,
|
|
1202
|
+
checkpoint: serializedCheckpoint,
|
|
1203
|
+
metadata: serializedMetadata,
|
|
1204
|
+
created_at: /* @__PURE__ */ new Date()
|
|
1205
|
+
};
|
|
1206
|
+
const docId = `${this.agentId}:${thread_id}:${checkpoint_ns}:${checkpoint_id}`;
|
|
1207
|
+
await this.db.collection(this.checkpointsCollection).doc(docId).set(doc);
|
|
1208
|
+
return {
|
|
1209
|
+
configurable: {
|
|
1210
|
+
thread_id,
|
|
1211
|
+
checkpoint_ns,
|
|
1212
|
+
checkpoint_id
|
|
1213
|
+
}
|
|
1214
|
+
};
|
|
1215
|
+
}
|
|
1216
|
+
/**
|
|
1217
|
+
* Save intermediate writes to CloudBase.
|
|
1218
|
+
*/
|
|
1219
|
+
async putWrites(config, writes, taskId) {
|
|
1220
|
+
if (!config.configurable) {
|
|
1221
|
+
throw new Error("Empty configuration supplied.");
|
|
1222
|
+
}
|
|
1223
|
+
if (!config.configurable.thread_id) {
|
|
1224
|
+
throw new Error("Missing thread_id field in config.configurable.");
|
|
1225
|
+
}
|
|
1226
|
+
if (!config.configurable.checkpoint_id) {
|
|
1227
|
+
throw new Error("Missing checkpoint_id field in config.configurable.");
|
|
1228
|
+
}
|
|
1229
|
+
const thread_id = config.configurable.thread_id;
|
|
1230
|
+
const checkpoint_ns = config.configurable.checkpoint_ns ?? "";
|
|
1231
|
+
const checkpoint_id = config.configurable.checkpoint_id;
|
|
1232
|
+
const serializedWrites = await Promise.all(
|
|
1233
|
+
writes.map(async ([channel, value], idx) => ({
|
|
1234
|
+
channel,
|
|
1235
|
+
value: await this.serialize(value),
|
|
1236
|
+
idx,
|
|
1237
|
+
docId: `${this.agentId}:${thread_id}:${checkpoint_ns}:${checkpoint_id}:${taskId}:${idx}`
|
|
1238
|
+
}))
|
|
1239
|
+
);
|
|
1240
|
+
await this.db.runTransaction(async (transaction) => {
|
|
1241
|
+
for (const { channel, value, idx, docId } of serializedWrites) {
|
|
1242
|
+
await transaction.collection(this.writesCollection).doc(docId).set({
|
|
1243
|
+
_openid: this.userId,
|
|
1244
|
+
agent_id: this.agentId,
|
|
1245
|
+
thread_id,
|
|
1246
|
+
checkpoint_ns,
|
|
1247
|
+
checkpoint_id,
|
|
1248
|
+
task_id: taskId,
|
|
1249
|
+
idx,
|
|
1250
|
+
channel,
|
|
1251
|
+
value
|
|
1252
|
+
});
|
|
1253
|
+
}
|
|
1254
|
+
});
|
|
1255
|
+
}
|
|
1256
|
+
/**
|
|
1257
|
+
* Delete all checkpoints and writes for a thread.
|
|
1258
|
+
*/
|
|
1259
|
+
async deleteThread(threadId) {
|
|
1260
|
+
await this.db.collection(this.checkpointsCollection).where({
|
|
1261
|
+
_openid: this.userId,
|
|
1262
|
+
agent_id: this.agentId,
|
|
1263
|
+
thread_id: threadId
|
|
1264
|
+
}).remove();
|
|
1265
|
+
await this.db.collection(this.writesCollection).where({
|
|
1266
|
+
_openid: this.userId,
|
|
1267
|
+
agent_id: this.agentId,
|
|
1268
|
+
thread_id: threadId
|
|
1269
|
+
}).remove();
|
|
1270
|
+
}
|
|
1271
|
+
};
|
|
1272
|
+
|
|
907
1273
|
// src/store/tdai-store.ts
|
|
908
1274
|
import {
|
|
909
|
-
BaseStore
|
|
1275
|
+
BaseStore as BaseStore2
|
|
910
1276
|
} from "@langchain/langgraph";
|
|
911
|
-
var TDAIStore = class extends
|
|
1277
|
+
var TDAIStore = class extends BaseStore2 {
|
|
912
1278
|
constructor(config) {
|
|
913
1279
|
super();
|
|
914
1280
|
this.isSetup = false;
|
|
@@ -1337,6 +1703,7 @@ import { noopLogger as noopLogger2, createConsoleLogger } from "@cloudbase/agent
|
|
|
1337
1703
|
export {
|
|
1338
1704
|
ClientPropertiesAnnotation,
|
|
1339
1705
|
ClientStateAnnotation,
|
|
1706
|
+
CloudBaseSaver,
|
|
1340
1707
|
LanggraphAgent,
|
|
1341
1708
|
TDAISaver,
|
|
1342
1709
|
TDAIStore,
|