@langgraph-js/pure-graph 3.2.0 → 3.2.3
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/adapter/fetch/index.js +124 -30
- package/dist/adapter/fetch/index.js.map +1 -1
- package/dist/adapter/fetch/utils.d.ts +13 -3
- package/dist/adapter/nextjs/index.js +1 -1
- package/dist/{checkpoint-C5AFBYE-.js → checkpoint-Due32543.js} +71 -41
- package/dist/checkpoint-Due32543.js.map +1 -0
- package/dist/{createEndpoint-C2KsYHDx.js → createEndpoint-CTPbz_D8.js} +20 -5
- package/dist/createEndpoint-CTPbz_D8.js.map +1 -0
- package/dist/createEndpoint.d.ts +2 -1
- package/dist/index.js +2 -2
- package/dist/queue/stream_queue.d.ts +3 -1
- package/dist/{queue-DySatFkr.js → queue-DPHwOl26.js} +120 -16
- package/dist/queue-DPHwOl26.js.map +1 -0
- package/dist/remote/index.js +1 -1
- package/dist/{shallow-checkpoint-BEhTdp7z.js → shallow-checkpoint-CHYRdSct.js} +79 -49
- package/dist/shallow-checkpoint-CHYRdSct.js.map +1 -0
- package/dist/{sqlite-adapter-oBA95xba.js → sqlite-adapter-CJXgit1j.js} +7 -12
- package/dist/sqlite-adapter-CJXgit1j.js.map +1 -0
- package/dist/storage/memory/queue.d.ts +6 -0
- package/dist/storage/redis/queue.d.ts +14 -1
- package/dist/{stream-pZfO6Y-p.js → stream-Zt8tbgEj.js} +188 -63
- package/dist/stream-Zt8tbgEj.js.map +1 -0
- package/package.json +2 -1
- package/dist/checkpoint-C5AFBYE-.js.map +0 -1
- package/dist/createEndpoint-C2KsYHDx.js.map +0 -1
- package/dist/queue-DySatFkr.js.map +0 -1
- package/dist/shallow-checkpoint-BEhTdp7z.js.map +0 -1
- package/dist/sqlite-adapter-oBA95xba.js.map +0 -1
- package/dist/stream-pZfO6Y-p.js.map +0 -1
|
@@ -1,8 +1,63 @@
|
|
|
1
|
-
import { B as BaseStreamQueue, C as CancelEventMessage } from './stream-
|
|
1
|
+
import { B as BaseStreamQueue, C as CancelEventMessage } from './stream-Zt8tbgEj.js';
|
|
2
2
|
import { createClient } from 'redis';
|
|
3
3
|
|
|
4
|
+
let sharedRedisClient = null;
|
|
5
|
+
let connectionRefCount = 0;
|
|
6
|
+
let connectionPromise = null;
|
|
7
|
+
let releaseTimeoutId = null;
|
|
8
|
+
async function getSharedRedisClient() {
|
|
9
|
+
if (sharedRedisClient && sharedRedisClient.isOpen) {
|
|
10
|
+
connectionRefCount++;
|
|
11
|
+
return sharedRedisClient;
|
|
12
|
+
}
|
|
13
|
+
if (connectionPromise) {
|
|
14
|
+
await connectionPromise;
|
|
15
|
+
if (sharedRedisClient) {
|
|
16
|
+
connectionRefCount++;
|
|
17
|
+
return sharedRedisClient;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
connectionPromise = (async () => {
|
|
21
|
+
const client = createClient({
|
|
22
|
+
url: process.env.REDIS_URL
|
|
23
|
+
});
|
|
24
|
+
await client.connect();
|
|
25
|
+
sharedRedisClient = client;
|
|
26
|
+
})();
|
|
27
|
+
try {
|
|
28
|
+
await connectionPromise;
|
|
29
|
+
connectionRefCount++;
|
|
30
|
+
return sharedRedisClient;
|
|
31
|
+
} catch (error) {
|
|
32
|
+
connectionPromise = null;
|
|
33
|
+
throw error;
|
|
34
|
+
} finally {
|
|
35
|
+
connectionPromise = null;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
async function releaseRedisClient() {
|
|
39
|
+
if (connectionRefCount > 0) {
|
|
40
|
+
connectionRefCount--;
|
|
41
|
+
}
|
|
42
|
+
if (connectionRefCount <= 0 && sharedRedisClient) {
|
|
43
|
+
if (releaseTimeoutId) {
|
|
44
|
+
clearTimeout(releaseTimeoutId);
|
|
45
|
+
releaseTimeoutId = null;
|
|
46
|
+
}
|
|
47
|
+
releaseTimeoutId = setTimeout(async () => {
|
|
48
|
+
if (connectionRefCount <= 0 && sharedRedisClient) {
|
|
49
|
+
try {
|
|
50
|
+
await sharedRedisClient.quit();
|
|
51
|
+
} catch (e) {
|
|
52
|
+
}
|
|
53
|
+
sharedRedisClient = null;
|
|
54
|
+
connectionRefCount = 0;
|
|
55
|
+
releaseTimeoutId = null;
|
|
56
|
+
}
|
|
57
|
+
}, 5e3);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
4
60
|
class RedisStreamQueue extends BaseStreamQueue {
|
|
5
|
-
// 轮询间隔(毫秒)
|
|
6
61
|
constructor(id, compressMessages = true, ttl = 300) {
|
|
7
62
|
super(id, true, ttl);
|
|
8
63
|
this.id = id;
|
|
@@ -10,16 +65,10 @@ class RedisStreamQueue extends BaseStreamQueue {
|
|
|
10
65
|
this.ttl = ttl;
|
|
11
66
|
this.streamKey = `stream:${this.id}`;
|
|
12
67
|
this.listKey = `queue:${this.id}`;
|
|
13
|
-
this.redis = createClient({
|
|
14
|
-
url: process.env.REDIS_URL
|
|
15
|
-
});
|
|
16
68
|
this.cancelSignal = new AbortController();
|
|
17
|
-
|
|
18
|
-
this.redis.connect();
|
|
19
|
-
}
|
|
20
|
-
this.isConnected = true;
|
|
69
|
+
this.connectionReady = this.initConnection();
|
|
21
70
|
}
|
|
22
|
-
redis;
|
|
71
|
+
redis = null;
|
|
23
72
|
streamKey;
|
|
24
73
|
listKey;
|
|
25
74
|
isConnected = false;
|
|
@@ -27,12 +76,36 @@ class RedisStreamQueue extends BaseStreamQueue {
|
|
|
27
76
|
lastStreamId = "0";
|
|
28
77
|
// 最后读取的 Stream ID
|
|
29
78
|
pollInterval = 100;
|
|
79
|
+
// 轮询间隔(毫秒)
|
|
80
|
+
connectionReady;
|
|
81
|
+
/**
|
|
82
|
+
* 初始化 Redis 连接(使用共享连接池)
|
|
83
|
+
*/
|
|
84
|
+
async initConnection() {
|
|
85
|
+
try {
|
|
86
|
+
this.redis = await getSharedRedisClient();
|
|
87
|
+
this.isConnected = true;
|
|
88
|
+
} catch (error) {
|
|
89
|
+
console.error("Failed to connect to Redis:", error);
|
|
90
|
+
throw error;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* 确保连接已建立
|
|
95
|
+
*/
|
|
96
|
+
async ensureConnected() {
|
|
97
|
+
if (!this.isConnected || !this.redis) {
|
|
98
|
+
await this.connectionReady;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
30
101
|
/**
|
|
31
102
|
* 推送消息到 Redis Stream 和 List
|
|
32
103
|
* - Stream: 用于实时推送(集群友好)
|
|
33
104
|
* - List: 用于 getAll() 批量获取历史数据
|
|
34
105
|
*/
|
|
35
106
|
async push(item) {
|
|
107
|
+
await this.ensureConnected();
|
|
108
|
+
if (!this.redis) throw new Error("Redis connection not available");
|
|
36
109
|
const encodedData = await this.encodeData(item);
|
|
37
110
|
const dataString = Buffer.from(encodedData).toString("base64");
|
|
38
111
|
const serializedData = Buffer.from(encodedData);
|
|
@@ -47,6 +120,11 @@ class RedisStreamQueue extends BaseStreamQueue {
|
|
|
47
120
|
*/
|
|
48
121
|
async *onDataReceive() {
|
|
49
122
|
let isStreamEnded = false;
|
|
123
|
+
let isCleanupDone = false;
|
|
124
|
+
await this.ensureConnected();
|
|
125
|
+
if (!this.redis) {
|
|
126
|
+
throw new Error("Redis connection not available");
|
|
127
|
+
}
|
|
50
128
|
if (this.cancelSignal.signal.aborted) {
|
|
51
129
|
return;
|
|
52
130
|
}
|
|
@@ -54,8 +132,20 @@ class RedisStreamQueue extends BaseStreamQueue {
|
|
|
54
132
|
isStreamEnded = true;
|
|
55
133
|
};
|
|
56
134
|
this.cancelSignal.signal.addEventListener("abort", abortHandler);
|
|
135
|
+
const cleanup = () => {
|
|
136
|
+
if (isCleanupDone) return;
|
|
137
|
+
isCleanupDone = true;
|
|
138
|
+
try {
|
|
139
|
+
this.cancelSignal.signal.removeEventListener("abort", abortHandler);
|
|
140
|
+
} catch (e) {
|
|
141
|
+
console.error("Error removing abort listener:", e);
|
|
142
|
+
}
|
|
143
|
+
};
|
|
57
144
|
try {
|
|
58
145
|
while (!isStreamEnded && !this.cancelSignal.signal.aborted) {
|
|
146
|
+
if (!this.redis || !this.isConnected) {
|
|
147
|
+
break;
|
|
148
|
+
}
|
|
59
149
|
const streams = await this.redis.xRead([{ key: this.streamKey, id: this.lastStreamId }], {
|
|
60
150
|
BLOCK: this.pollInterval,
|
|
61
151
|
COUNT: 10
|
|
@@ -89,13 +179,15 @@ class RedisStreamQueue extends BaseStreamQueue {
|
|
|
89
179
|
}
|
|
90
180
|
}
|
|
91
181
|
} finally {
|
|
92
|
-
|
|
182
|
+
cleanup();
|
|
93
183
|
}
|
|
94
184
|
}
|
|
95
185
|
/**
|
|
96
186
|
* 获取队列中的所有数据(从 List 获取历史数据)
|
|
97
187
|
*/
|
|
98
188
|
async getAll() {
|
|
189
|
+
await this.ensureConnected();
|
|
190
|
+
if (!this.redis) return [];
|
|
99
191
|
const data = await this.redis.lRange(this.listKey, 0, -1);
|
|
100
192
|
if (!data || data.length === 0) {
|
|
101
193
|
return [];
|
|
@@ -114,12 +206,20 @@ class RedisStreamQueue extends BaseStreamQueue {
|
|
|
114
206
|
/**
|
|
115
207
|
* 清空队列
|
|
116
208
|
*/
|
|
117
|
-
clear() {
|
|
118
|
-
if (this.isConnected) {
|
|
119
|
-
this.redis.del(this.streamKey);
|
|
120
|
-
this.redis.del(this.listKey);
|
|
209
|
+
async clear() {
|
|
210
|
+
if (this.isConnected && this.redis) {
|
|
211
|
+
await Promise.all([this.redis.del(this.streamKey), this.redis.del(this.listKey)]);
|
|
121
212
|
}
|
|
122
213
|
}
|
|
214
|
+
/**
|
|
215
|
+
* 销毁队列实例,释放 Redis 连接引用
|
|
216
|
+
*/
|
|
217
|
+
async destroy() {
|
|
218
|
+
await this.clear();
|
|
219
|
+
this.isConnected = false;
|
|
220
|
+
this.redis = null;
|
|
221
|
+
await releaseRedisClient();
|
|
222
|
+
}
|
|
123
223
|
/**
|
|
124
224
|
* 取消操作
|
|
125
225
|
*/
|
|
@@ -131,7 +231,11 @@ class RedisStreamQueue extends BaseStreamQueue {
|
|
|
131
231
|
* 复制队列到另一个队列
|
|
132
232
|
*/
|
|
133
233
|
async copyToQueue(toId, ttl) {
|
|
234
|
+
await this.ensureConnected();
|
|
235
|
+
if (!this.redis) throw new Error("Redis connection not available");
|
|
134
236
|
const queue = new RedisStreamQueue(toId, this.compressMessages, ttl ?? this.ttl);
|
|
237
|
+
await queue.ensureConnected();
|
|
238
|
+
if (!queue.redis) throw new Error("Target Redis connection not available");
|
|
135
239
|
await this.redis.copy(this.listKey, queue.listKey);
|
|
136
240
|
await this.redis.expire(queue.listKey, ttl ?? this.ttl);
|
|
137
241
|
const allStreamData = await this.redis.xRange(this.streamKey, "-", "+");
|
|
@@ -150,4 +254,4 @@ class RedisStreamQueue extends BaseStreamQueue {
|
|
|
150
254
|
}
|
|
151
255
|
|
|
152
256
|
export { RedisStreamQueue };
|
|
153
|
-
//# sourceMappingURL=queue-
|
|
257
|
+
//# sourceMappingURL=queue-DPHwOl26.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queue-DPHwOl26.js","sources":["../src/storage/redis/queue.ts"],"sourcesContent":["import { CancelEventMessage, EventMessage } from '../../queue/event_message.js';\nimport { BaseStreamQueue } from '../../queue/stream_queue.js';\nimport { BaseStreamQueueInterface } from '../../queue/stream_queue.js';\nimport { createClient, RedisClientType } from 'redis';\n\n// 全局共享的 Redis 连接池\nlet sharedRedisClient: RedisClientType | null = null;\nlet connectionRefCount = 0;\nlet connectionPromise: Promise<void> | null = null;\nlet releaseTimeoutId: ReturnType<typeof setTimeout> | null = null;\n\n/**\n * 获取共享的 Redis 连接(线程安全)\n */\nasync function getSharedRedisClient(): Promise<RedisClientType> {\n // 如果已有连接,直接返回\n if (sharedRedisClient && sharedRedisClient.isOpen) {\n connectionRefCount++;\n return sharedRedisClient;\n }\n\n // 如果正在建立连接,等待现有 Promise\n if (connectionPromise) {\n await connectionPromise;\n if (sharedRedisClient) {\n connectionRefCount++;\n return sharedRedisClient;\n }\n // 连接失败,继续尝试创建\n }\n\n // 创建新的连接 Promise,防止并发创建多个连接\n connectionPromise = (async () => {\n const client = createClient({\n url: process.env.REDIS_URL,\n });\n await client.connect();\n sharedRedisClient = client as RedisClientType;\n })();\n\n try {\n await connectionPromise;\n connectionRefCount++;\n return sharedRedisClient!;\n } catch (error) {\n // 连接失败时清理状态\n connectionPromise = null;\n throw error;\n } finally {\n connectionPromise = null;\n }\n}\n\n/**\n * 释放 Redis 连接引用\n */\nasync function releaseRedisClient(): Promise<void> {\n if (connectionRefCount > 0) {\n connectionRefCount--;\n }\n\n // 引用计数为 0 且超过一定时间没有新连接时才关闭\n // 避免频繁开关连接\n if (connectionRefCount <= 0 && sharedRedisClient) {\n // 清理之前的延迟关闭定时器\n if (releaseTimeoutId) {\n clearTimeout(releaseTimeoutId);\n releaseTimeoutId = null;\n }\n\n // 延迟关闭,给其他队列复用连接的机会\n releaseTimeoutId = setTimeout(async () => {\n if (connectionRefCount <= 0 && sharedRedisClient) {\n try {\n await sharedRedisClient.quit();\n } catch (e) {\n // 忽略关闭错误\n }\n sharedRedisClient = null;\n connectionRefCount = 0;\n releaseTimeoutId = null;\n }\n }, 5000);\n }\n}\n\n/**\n * Redis Stream 实现的消息队列,用于存储消息\n * 使用 Redis Streams 替代 pub/sub,支持集群模式\n */\nexport class RedisStreamQueue extends BaseStreamQueue implements BaseStreamQueueInterface {\n private redis: RedisClientType | null = null;\n private streamKey: string;\n private listKey: string;\n private isConnected = false;\n public cancelSignal: AbortController;\n private lastStreamId: string = '0'; // 最后读取的 Stream ID\n private pollInterval: number = 100; // 轮询间隔(毫秒)\n private connectionReady: Promise<void>;\n\n constructor(readonly id: string, readonly compressMessages: boolean = true, readonly ttl: number = 300) {\n super(id, true, ttl);\n this.streamKey = `stream:${this.id}`;\n this.listKey = `queue:${this.id}`;\n this.cancelSignal = new AbortController();\n\n // 异步初始化 Redis 连接\n this.connectionReady = this.initConnection();\n }\n\n /**\n * 初始化 Redis 连接(使用共享连接池)\n */\n private async initConnection(): Promise<void> {\n try {\n this.redis = await getSharedRedisClient();\n this.isConnected = true;\n } catch (error) {\n console.error('Failed to connect to Redis:', error);\n throw error;\n }\n }\n\n /**\n * 确保连接已建立\n */\n private async ensureConnected(): Promise<void> {\n if (!this.isConnected || !this.redis) {\n await this.connectionReady;\n }\n }\n\n /**\n * 推送消息到 Redis Stream 和 List\n * - Stream: 用于实时推送(集群友好)\n * - List: 用于 getAll() 批量获取历史数据\n */\n async push(item: EventMessage): Promise<void> {\n await this.ensureConnected();\n if (!this.redis) throw new Error('Redis connection not available');\n\n const encodedData = await this.encodeData(item);\n // 将 Uint8Array 转换为 base64 字符串,以便存储到 Redis Stream\n const dataString = Buffer.from(encodedData).toString('base64');\n const serializedData = Buffer.from(encodedData);\n\n // 推送到 Stream(实时推送)\n // 注意:xAdd 的第三个参数必须是简单的键值对对象,值必须是字符串\n await this.redis.xAdd(this.streamKey, '*', { data: dataString });\n\n // 设置 Stream TTL\n await this.redis.expire(this.streamKey, this.ttl);\n\n // 同时推送到 List(用于 getAll)\n await this.redis.rPush(this.listKey, serializedData);\n await this.redis.expire(this.listKey, this.ttl);\n\n this.emit('dataChange', dataString);\n }\n\n /**\n * 异步生成器:使用 Redis Streams XREAD 轮询消费队列数据\n */\n async *onDataReceive(): AsyncGenerator<EventMessage, void, unknown> {\n let isStreamEnded = false;\n let isCleanupDone = false;\n\n // 等待连接建立\n await this.ensureConnected();\n if (!this.redis) {\n throw new Error('Redis connection not available');\n }\n\n // 检查是否已取消\n if (this.cancelSignal.signal.aborted) {\n return;\n }\n\n // 监听取消信号\n const abortHandler = () => {\n isStreamEnded = true;\n };\n this.cancelSignal.signal.addEventListener('abort', abortHandler);\n\n const cleanup = () => {\n if (isCleanupDone) return;\n isCleanupDone = true;\n try {\n this.cancelSignal.signal.removeEventListener('abort', abortHandler);\n } catch (e) {\n console.error('Error removing abort listener:', e);\n }\n };\n\n try {\n while (!isStreamEnded && !this.cancelSignal.signal.aborted) {\n // 检查 Redis 连接是否仍然有效\n if (!this.redis || !this.isConnected) {\n break;\n }\n\n // 从 Stream 读取新消息(XREAD 阻塞读取)\n const streams = await this.redis.xRead([{ key: this.streamKey, id: this.lastStreamId }], {\n BLOCK: this.pollInterval,\n COUNT: 10,\n });\n\n if (streams && streams.length > 0) {\n for (const stream of streams) {\n for (const message of stream.messages) {\n // 更新最后读取的 ID\n this.lastStreamId = message.id;\n\n // 解析消息:从 base64 字符串转换回 Uint8Array\n const dataString = message.message.data as string;\n const data = Buffer.from(dataString, 'base64');\n const item = (await this.decodeData(data)) as EventMessage;\n\n // 检查是否为流结束或错误信号\n if (\n item.event === '__stream_end__' ||\n item.event === '__stream_error__' ||\n item.event === '__stream_cancel__'\n ) {\n // 延迟 300ms 后结束,确保消息被消费\n await new Promise((resolve) => setTimeout(resolve, 300));\n isStreamEnded = true;\n\n if (item.event === '__stream_cancel__') {\n await this.cancel();\n }\n }\n\n yield item;\n\n if (isStreamEnded) {\n break;\n }\n }\n if (isStreamEnded) {\n break;\n }\n }\n }\n\n // 轮询间隔\n if (!isStreamEnded && !this.cancelSignal.signal.aborted) {\n await new Promise((resolve) => setTimeout(resolve, this.pollInterval));\n }\n }\n } finally {\n cleanup();\n }\n }\n\n /**\n * 获取队列中的所有数据(从 List 获取历史数据)\n */\n async getAll(): Promise<EventMessage[]> {\n await this.ensureConnected();\n if (!this.redis) return [];\n\n const data = await this.redis.lRange(this.listKey, 0, -1);\n\n if (!data || data.length === 0) {\n return [];\n }\n\n if (this.compressMessages) {\n return (await Promise.all(\n data.map((item: Buffer | string) => {\n // 处理 Buffer 或字符串类型\n const buffer = typeof item === 'string' ? Buffer.from(item, 'binary') : item;\n return this.decodeData(buffer);\n }),\n )) as EventMessage[];\n } else {\n return data.map((item: string) => JSON.parse(item) as EventMessage);\n }\n }\n\n /**\n * 清空队列\n */\n async clear(): Promise<void> {\n if (this.isConnected && this.redis) {\n // 同时清空 Stream 和 List\n await Promise.all([this.redis.del(this.streamKey), this.redis.del(this.listKey)]);\n }\n }\n\n /**\n * 销毁队列实例,释放 Redis 连接引用\n */\n async destroy(): Promise<void> {\n await this.clear();\n this.isConnected = false;\n this.redis = null;\n await releaseRedisClient();\n }\n\n /**\n * 取消操作\n */\n async cancel(): Promise<void> {\n // First abort to stop any waiting generators\n this.cancelSignal.abort('user cancel this run');\n // Then push the cancel message to signal other consumers\n await this.push(new CancelEventMessage());\n }\n\n /**\n * 复制队列到另一个队列\n */\n async copyToQueue(toId: string, ttl?: number): Promise<RedisStreamQueue> {\n await this.ensureConnected();\n if (!this.redis) throw new Error('Redis connection not available');\n\n const queue = new RedisStreamQueue(toId, this.compressMessages, ttl ?? this.ttl);\n await queue.ensureConnected();\n\n if (!queue.redis) throw new Error('Target Redis connection not available');\n\n // 复制 List\n await this.redis.copy(this.listKey, queue.listKey);\n await this.redis.expire(queue.listKey, ttl ?? this.ttl);\n\n // 复制 Stream(需要遍历并重新添加)\n const allStreamData = await this.redis.xRange(this.streamKey, '-', '+');\n if (allStreamData && allStreamData.length > 0) {\n for (const message of allStreamData) {\n // 确保所有值都是字符串,Redis Streams 只支持 string 值\n const fields: Record<string, string> = {};\n for (const [key, value] of Object.entries(message.message)) {\n fields[key] = String(value);\n }\n await this.redis.xAdd(queue.streamKey, '*', fields);\n }\n await this.redis.expire(queue.streamKey, ttl ?? this.ttl);\n }\n\n return queue;\n }\n}\n"],"names":[],"mappings":"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"}
|
package/dist/remote/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Hono } from 'hono';
|
|
2
2
|
import { PostgresAdapter } from '../pg-adapter-BFtir1GE.js';
|
|
3
|
-
import { K as KyselyThreadsManager } from '../stream-
|
|
3
|
+
import { K as KyselyThreadsManager } from '../stream-Zt8tbgEj.js';
|
|
4
4
|
import { Pool } from 'pg';
|
|
5
5
|
|
|
6
6
|
class RemoteServer {
|
|
@@ -4,9 +4,27 @@ import { BaseCheckpointSaver, getCheckpointId, TASKS, uuid6, copyCheckpoint, WRI
|
|
|
4
4
|
const SQLITE_RETRY_CONFIG = {
|
|
5
5
|
maxRetries: 3,
|
|
6
6
|
baseDelayMs: 100,
|
|
7
|
+
// 不可重试的错误模式
|
|
8
|
+
nonRetryablePatterns: [
|
|
9
|
+
"database disk image is malformed",
|
|
10
|
+
// 数据库文件损坏
|
|
11
|
+
"database is malformed",
|
|
12
|
+
// 数据库损坏(短形式)
|
|
13
|
+
"cannot rollback",
|
|
14
|
+
// 事务状态错误
|
|
15
|
+
"no transaction is active",
|
|
16
|
+
// 无活动事务
|
|
17
|
+
"database or disk is full"
|
|
18
|
+
// 磁盘空间不足
|
|
19
|
+
],
|
|
7
20
|
isRetryableError: (error) => {
|
|
8
21
|
const msg = error?.message?.toLowerCase() || "";
|
|
9
|
-
|
|
22
|
+
for (const pattern of SQLITE_RETRY_CONFIG.nonRetryablePatterns) {
|
|
23
|
+
if (msg.includes(pattern.toLowerCase())) {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return msg.includes("sqlite_busy") || msg.includes("database is locked") || msg === "sqlite_busy" || msg === "database is locked";
|
|
10
28
|
}
|
|
11
29
|
};
|
|
12
30
|
async function withRetry(operation, context) {
|
|
@@ -16,7 +34,28 @@ async function withRetry(operation, context) {
|
|
|
16
34
|
return await operation();
|
|
17
35
|
} catch (error) {
|
|
18
36
|
lastError = error;
|
|
37
|
+
const msg = error?.message?.toLowerCase() || "";
|
|
19
38
|
if (!SQLITE_RETRY_CONFIG.isRetryableError(error)) {
|
|
39
|
+
if (msg.includes("malformed")) {
|
|
40
|
+
const enhancedError = new Error(
|
|
41
|
+
`SQLite database is corrupted: ${error.message}
|
|
42
|
+
|
|
43
|
+
Context: ${context || "unknown"}
|
|
44
|
+
|
|
45
|
+
Possible causes:
|
|
46
|
+
1. Database file was manually deleted or modified
|
|
47
|
+
2. Disk I/O errors during write operations
|
|
48
|
+
3. Concurrent access without proper locking
|
|
49
|
+
|
|
50
|
+
Recovery options:
|
|
51
|
+
- Delete the database file to start fresh (data will be lost)
|
|
52
|
+
- Use SQLite recovery tools: sqlite3 <db> ".recover" > recover.sql
|
|
53
|
+
- Switch to PostgreSQL/Redis for production use`
|
|
54
|
+
);
|
|
55
|
+
enhancedError.name = "SQLiteCorruptError";
|
|
56
|
+
enhancedError.cause = error;
|
|
57
|
+
throw enhancedError;
|
|
58
|
+
}
|
|
20
59
|
throw error;
|
|
21
60
|
}
|
|
22
61
|
if (attempt < SQLITE_RETRY_CONFIG.maxRetries - 1) {
|
|
@@ -67,9 +106,9 @@ class SqliteShallowSaver extends BaseCheckpointSaver {
|
|
|
67
106
|
static async fromConnStringAsync(connStringOrLocalPath) {
|
|
68
107
|
let saver;
|
|
69
108
|
if (globalThis.Bun) {
|
|
70
|
-
console.log("LG | Using
|
|
71
|
-
const {
|
|
72
|
-
saver = new SqliteShallowSaver(new
|
|
109
|
+
console.log("LG | Using BunSqliteDialect " + connStringOrLocalPath);
|
|
110
|
+
const { BunSqliteDialect } = await import('kysely-bun-worker/normal');
|
|
111
|
+
saver = new SqliteShallowSaver(new BunSqliteDialect({ url: connStringOrLocalPath }));
|
|
73
112
|
} else {
|
|
74
113
|
console.log("LG | Using NodeWasmDialect");
|
|
75
114
|
const { default: SqliteDatabase } = await import('node-sqlite3-wasm');
|
|
@@ -355,36 +394,33 @@ CREATE TABLE IF NOT EXISTS writes (
|
|
|
355
394
|
const threadId_ = threadId;
|
|
356
395
|
const checkpointNs_ = checkpointNs;
|
|
357
396
|
const checkpointId_ = checkpointId;
|
|
358
|
-
await withRetry(
|
|
359
|
-
async () => {
|
|
360
|
-
await
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
397
|
+
await withRetry(async () => {
|
|
398
|
+
await this.db.transaction().execute(async (trx) => {
|
|
399
|
+
const oldCheckpoint = await trx.selectFrom("shallow_checkpoints").select(["checkpoint_id"]).where("thread_id", "=", threadId_).where("checkpoint_ns", "=", checkpointNs_).executeTakeFirst();
|
|
400
|
+
if (oldCheckpoint && oldCheckpoint.checkpoint_id !== checkpointId_) {
|
|
401
|
+
await trx.deleteFrom("writes").where("thread_id", "=", threadId_).where("checkpoint_ns", "=", checkpointNs_).where("checkpoint_id", "=", oldCheckpoint.checkpoint_id).execute();
|
|
402
|
+
}
|
|
403
|
+
await trx.insertInto("shallow_checkpoints").values({
|
|
404
|
+
thread_id: threadId_,
|
|
405
|
+
checkpoint_ns: checkpointNs_,
|
|
406
|
+
checkpoint_id: checkpointId_,
|
|
407
|
+
parent_checkpoint_id: parentCheckpointId ?? null,
|
|
408
|
+
type: type1,
|
|
409
|
+
checkpoint: new Uint8Array(Buffer.from(serializedCheckpoint)),
|
|
410
|
+
metadata: new Uint8Array(Buffer.from(serializedMetadata)),
|
|
411
|
+
checkpoint_ts: Date.now()
|
|
412
|
+
}).onConflict(
|
|
413
|
+
(oc) => oc.columns(["thread_id", "checkpoint_ns"]).doUpdateSet({
|
|
368
414
|
checkpoint_id: checkpointId_,
|
|
369
415
|
parent_checkpoint_id: parentCheckpointId ?? null,
|
|
370
416
|
type: type1,
|
|
371
417
|
checkpoint: new Uint8Array(Buffer.from(serializedCheckpoint)),
|
|
372
418
|
metadata: new Uint8Array(Buffer.from(serializedMetadata)),
|
|
373
419
|
checkpoint_ts: Date.now()
|
|
374
|
-
})
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
type: type1,
|
|
379
|
-
checkpoint: new Uint8Array(Buffer.from(serializedCheckpoint)),
|
|
380
|
-
metadata: new Uint8Array(Buffer.from(serializedMetadata)),
|
|
381
|
-
checkpoint_ts: Date.now()
|
|
382
|
-
})
|
|
383
|
-
).execute();
|
|
384
|
-
});
|
|
385
|
-
},
|
|
386
|
-
`put(${threadId}/${checkpointId})`
|
|
387
|
-
);
|
|
420
|
+
})
|
|
421
|
+
).execute();
|
|
422
|
+
});
|
|
423
|
+
}, `put(${threadId}/${checkpointId})`);
|
|
388
424
|
return {
|
|
389
425
|
configurable: {
|
|
390
426
|
thread_id: threadId,
|
|
@@ -417,28 +453,22 @@ CREATE TABLE IF NOT EXISTS writes (
|
|
|
417
453
|
})
|
|
418
454
|
);
|
|
419
455
|
if (values.length === 0) return;
|
|
420
|
-
await withRetry(
|
|
421
|
-
async () => {
|
|
422
|
-
await
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
},
|
|
429
|
-
`putWrites(${threadId}/${checkpointId}/${taskId})`
|
|
430
|
-
);
|
|
456
|
+
await withRetry(async () => {
|
|
457
|
+
await this.db.transaction().execute(async (trx) => {
|
|
458
|
+
await trx.deleteFrom("writes").where("thread_id", "=", threadId).where("checkpoint_ns", "=", checkpointNs).where("checkpoint_id", "=", checkpointId).where("task_id", "=", taskId).execute();
|
|
459
|
+
for (const value of values) {
|
|
460
|
+
await trx.insertInto("writes").values(value).execute();
|
|
461
|
+
}
|
|
462
|
+
});
|
|
463
|
+
}, `putWrites(${threadId}/${checkpointId}/${taskId})`);
|
|
431
464
|
}
|
|
432
465
|
async deleteThread(threadId) {
|
|
433
|
-
await withRetry(
|
|
434
|
-
async () => {
|
|
435
|
-
await
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
},
|
|
440
|
-
`deleteThread(${threadId})`
|
|
441
|
-
);
|
|
466
|
+
await withRetry(async () => {
|
|
467
|
+
await this.db.transaction().execute(async (trx) => {
|
|
468
|
+
await trx.deleteFrom("shallow_checkpoints").where("thread_id", "=", threadId).execute();
|
|
469
|
+
await trx.deleteFrom("writes").where("thread_id", "=", threadId).execute();
|
|
470
|
+
});
|
|
471
|
+
}, `deleteThread(${threadId})`);
|
|
442
472
|
}
|
|
443
473
|
async migratePendingSends(checkpoint, threadId, parentCheckpointId) {
|
|
444
474
|
const result = await this.db.selectFrom("writes as ps").select([
|
|
@@ -463,4 +493,4 @@ CREATE TABLE IF NOT EXISTS writes (
|
|
|
463
493
|
}
|
|
464
494
|
|
|
465
495
|
export { SqliteShallowSaver };
|
|
466
|
-
//# sourceMappingURL=shallow-checkpoint-
|
|
496
|
+
//# sourceMappingURL=shallow-checkpoint-CHYRdSct.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shallow-checkpoint-CHYRdSct.js","sources":["../src/storage/sqlite/shallow-checkpoint.ts"],"sourcesContent":["import { Dialect, Kysely, sql } from 'kysely';\nimport type { RunnableConfig } from '@langchain/core/runnables';\n\nimport {\n BaseCheckpointSaver,\n type Checkpoint,\n type CheckpointListOptions,\n type CheckpointTuple,\n type SerializerProtocol,\n type PendingWrite,\n type CheckpointMetadata,\n TASKS,\n copyCheckpoint,\n maxChannelVersion,\n getCheckpointId,\n WRITES_IDX_MAP,\n uuid6,\n} from '@langchain/langgraph-checkpoint';\n\n/**\n * SQLite 重试配置\n */\nconst SQLITE_RETRY_CONFIG = {\n maxRetries: 3,\n baseDelayMs: 100,\n // 不可重试的错误模式\n nonRetryablePatterns: [\n 'database disk image is malformed', // 数据库文件损坏\n 'database is malformed', // 数据库损坏(短形式)\n 'cannot rollback', // 事务状态错误\n 'no transaction is active', // 无活动事务\n 'database or disk is full', // 磁盘空间不足\n ],\n isRetryableError: (error: any): boolean => {\n const msg = error?.message?.toLowerCase() || '';\n\n // 检查是否为不可重试的错误\n for (const pattern of SQLITE_RETRY_CONFIG.nonRetryablePatterns) {\n if (msg.includes(pattern.toLowerCase())) {\n return false;\n }\n }\n\n // 只重试锁相关的错误\n return (\n msg.includes('sqlite_busy') ||\n msg.includes('database is locked') ||\n msg === 'sqlite_busy' ||\n msg === 'database is locked'\n );\n },\n};\n\n/**\n * 带重试的数据库操作包装器\n */\nasync function withRetry<T>(operation: () => Promise<T>, context?: string): Promise<T> {\n let lastError: Error | null = null;\n\n for (let attempt = 0; attempt < SQLITE_RETRY_CONFIG.maxRetries; attempt++) {\n try {\n return await operation();\n } catch (error: any) {\n lastError = error;\n const msg = error?.message?.toLowerCase() || '';\n\n // 检查是否为严重错误(不可重试)\n if (!SQLITE_RETRY_CONFIG.isRetryableError(error)) {\n // 为数据库损坏错误提供额外的诊断信息\n if (msg.includes('malformed')) {\n const enhancedError = new Error(\n `SQLite database is corrupted: ${error.message}\\n\\n` +\n `Context: ${context || 'unknown'}\\n\\n` +\n `Possible causes:\\n` +\n `1. Database file was manually deleted or modified\\n` +\n `2. Disk I/O errors during write operations\\n` +\n `3. Concurrent access without proper locking\\n\\n` +\n `Recovery options:\\n` +\n `- Delete the database file to start fresh (data will be lost)\\n` +\n `- Use SQLite recovery tools: sqlite3 <db> \".recover\" > recover.sql\\n` +\n `- Switch to PostgreSQL/Redis for production use`,\n );\n enhancedError.name = 'SQLiteCorruptError';\n /** @ts-ignore */\n enhancedError.cause = error;\n throw enhancedError;\n }\n\n // 其他不可重试错误直接抛出\n throw error;\n }\n\n if (attempt < SQLITE_RETRY_CONFIG.maxRetries - 1) {\n const delay = SQLITE_RETRY_CONFIG.baseDelayMs * Math.pow(2, attempt);\n console.warn(\n `SQLite lock detected${context ? ` (${context})` : ''}, retrying in ${delay}ms (attempt ${\n attempt + 1\n }/${SQLITE_RETRY_CONFIG.maxRetries})`,\n );\n await new Promise((resolve) => setTimeout(resolve, delay));\n }\n }\n }\n\n throw lastError;\n}\n\n/**\n * Helper function for deterministic object comparison\n * Used for deep metadata filtering\n */\nfunction deterministicStringify(obj: any): string {\n if (obj === null || typeof obj !== 'object') {\n return JSON.stringify(obj);\n }\n if (Array.isArray(obj)) {\n return JSON.stringify(obj.map((item) => deterministicStringify(item)));\n }\n const sortedObj: Record<string, any> = {};\n const sortedKeys = Object.keys(obj).sort();\n for (const key of sortedKeys) {\n sortedObj[key] = obj[key];\n }\n return JSON.stringify(sortedObj, (_, value) => {\n if (value !== null && typeof value === 'object' && !Array.isArray(value)) {\n const sorted: Record<string, any> = {};\n const keys = Object.keys(value).sort();\n for (const k of keys) {\n sorted[k] = value[k];\n }\n return sorted;\n }\n return value;\n });\n}\n\n// Kysely 数据库表类型定义 - 浅层模式\ninterface ShallowCheckpointsTable {\n thread_id: string;\n checkpoint_ns: string;\n checkpoint_id: string;\n parent_checkpoint_id: string | null;\n type: string | null;\n checkpoint: Uint8Array;\n metadata: Uint8Array;\n checkpoint_ts: number; // 时间戳用于排序\n}\n\ninterface WritesTable {\n thread_id: string;\n checkpoint_ns: string;\n checkpoint_id: string;\n task_id: string;\n idx: number;\n channel: string;\n type: string | null;\n value: Uint8Array | null;\n}\n\ninterface ShallowCheckpointDatabase {\n shallow_checkpoints: ShallowCheckpointsTable;\n writes: WritesTable;\n}\n\n// Valid metadata keys for filtering\nconst checkpointMetadataKeys = ['source', 'step', 'parents'] as const;\n\ntype CheckKeys<T, K extends readonly (keyof T)[]> = [K[number]] extends [keyof T]\n ? [keyof T] extends [K[number]]\n ? K\n : never\n : never;\n\nfunction validateKeys<T, K extends readonly (keyof T)[]>(keys: CheckKeys<T, K>): K {\n return keys;\n}\n\nconst validCheckpointMetadataKeys = validateKeys<CheckpointMetadata, typeof checkpointMetadataKeys>(\n checkpointMetadataKeys,\n);\n\n/**\n * SqliteShallowSaver - SQLite 浅层检查点存储器\n *\n * 特性:\n * - 每个 thread_id + checkpoint_ns 组合只保留最新的 checkpoint\n * - 新 checkpoint 写入时自动清理旧 checkpoint 的 writes\n * - 使用 checkpoint_ts 时间戳排序\n * - 大幅减少存储数据量\n */\nexport class SqliteShallowSaver extends BaseCheckpointSaver {\n db: Kysely<ShallowCheckpointDatabase>;\n protected isSetup: boolean;\n\n constructor(dialect: Dialect, serde?: SerializerProtocol) {\n super(serde);\n this.db = new Kysely<ShallowCheckpointDatabase>({\n dialect,\n });\n this.isSetup = false;\n }\n\n static async fromConnStringAsync(connStringOrLocalPath: string): Promise<SqliteShallowSaver> {\n let saver: SqliteShallowSaver;\n /** @ts-ignore */\n if (globalThis.Bun) {\n console.log('LG | Using BunSqliteDialect ' + connStringOrLocalPath);\n const { BunSqliteDialect } = await import('kysely-bun-worker/normal');\n // 使用 BunSqliteDialect(非 Worker 模式)避免 Worker 事务状态同步问题\n // BunWorkerDialect 在高并发下可能出现 \"cannot rollback - no transaction is active\"\n saver = new SqliteShallowSaver(new BunSqliteDialect({ url: connStringOrLocalPath }));\n } else {\n /** @ts-ignore */\n console.log('LG | Using NodeWasmDialect');\n const { default: SqliteDatabase } = await import('node-sqlite3-wasm');\n const { NodeWasmDialect } = await import('kysely-wasm');\n console.log(connStringOrLocalPath);\n const wasm = new NodeWasmDialect({\n database: new SqliteDatabase.Database(connStringOrLocalPath),\n });\n saver = new SqliteShallowSaver(wasm);\n }\n await saver.setup();\n return saver;\n }\n\n protected async setup(): Promise<void> {\n if (this.isSetup) {\n return;\n }\n\n // 锁等待超时 5 秒\n await sql`PRAGMA busy_timeout = 5000`.execute(this.db as any);\n\n // WAL 模式 - 允许读写并发\n await sql`PRAGMA journal_mode = WAL`.execute(this.db as any);\n\n // NORMAL 模式 - 平衡数据安全与性能\n await sql`PRAGMA synchronous = NORMAL`.execute(this.db as any);\n\n // WAL 自动检查点\n await sql`PRAGMA wal_autocheckpoint = 1000`.execute(this.db as any);\n\n // 创建浅层 checkpoints 表 - 使用复合主键 (thread_id, checkpoint_ns)\n await sql`\nCREATE TABLE IF NOT EXISTS shallow_checkpoints (\n thread_id TEXT NOT NULL,\n checkpoint_ns TEXT NOT NULL DEFAULT '',\n checkpoint_id TEXT NOT NULL,\n parent_checkpoint_id TEXT,\n type TEXT,\n checkpoint BLOB,\n metadata BLOB,\n checkpoint_ts INTEGER NOT NULL DEFAULT 0,\n PRIMARY KEY (thread_id, checkpoint_ns)\n)`.execute(this.db as any);\n\n // 创建 checkpoint_ts 索引用于排序\n await sql`\nCREATE INDEX IF NOT EXISTS idx_shallow_checkpoints_ts \nON shallow_checkpoints(checkpoint_ts DESC)`.execute(this.db as any);\n\n // 创建 writes 表\n await sql`\nCREATE TABLE IF NOT EXISTS writes (\n thread_id TEXT NOT NULL,\n checkpoint_ns TEXT NOT NULL DEFAULT '',\n checkpoint_id TEXT NOT NULL,\n task_id TEXT NOT NULL,\n idx INTEGER NOT NULL,\n channel TEXT NOT NULL,\n type TEXT,\n value BLOB,\n PRIMARY KEY (thread_id, checkpoint_ns, checkpoint_id, task_id, idx)\n)`.execute(this.db as any);\n\n this.isSetup = true;\n }\n\n /**\n * 获取 checkpoint(便捷方法)\n */\n async get(config: RunnableConfig): Promise<Checkpoint | undefined> {\n const tuple = await this.getTuple(config);\n return tuple?.checkpoint;\n }\n\n async getTuple(config: RunnableConfig): Promise<CheckpointTuple | undefined> {\n await this.setup();\n const threadId = config.configurable?.thread_id;\n const checkpointNs = config.configurable?.checkpoint_ns ?? '';\n const requestedCheckpointId = getCheckpointId(config);\n\n if (threadId === undefined) {\n return undefined;\n }\n\n let query = this.db\n .selectFrom('shallow_checkpoints')\n .select([\n 'thread_id',\n 'checkpoint_ns',\n 'checkpoint_id',\n 'parent_checkpoint_id',\n 'type',\n 'checkpoint',\n 'metadata',\n 'checkpoint_ts',\n sql<string>`(\n SELECT json_group_array(\n json_object(\n 'task_id', pw.task_id,\n 'channel', pw.channel,\n 'type', pw.type,\n 'value', CAST(pw.value AS TEXT)\n )\n )\n FROM writes as pw\n WHERE pw.thread_id = shallow_checkpoints.thread_id\n AND pw.checkpoint_ns = shallow_checkpoints.checkpoint_ns\n AND pw.checkpoint_id = shallow_checkpoints.checkpoint_id\n )`.as('pending_writes'),\n sql<string>`(\n SELECT json_group_array(\n json_object(\n 'type', ps.type,\n 'value', CAST(ps.value AS TEXT)\n )\n )\n FROM writes as ps\n WHERE ps.thread_id = shallow_checkpoints.thread_id\n AND ps.checkpoint_ns = shallow_checkpoints.checkpoint_ns\n AND ps.checkpoint_id = shallow_checkpoints.parent_checkpoint_id\n AND ps.channel = ${TASKS}\n ORDER BY ps.idx\n )`.as('pending_sends'),\n ])\n .where('thread_id', '=', threadId)\n .where('checkpoint_ns', '=', checkpointNs);\n\n // 如果指定了 checkpoint_id,需要验证是否匹配\n if (requestedCheckpointId) {\n query = query.where('checkpoint_id', '=', requestedCheckpointId);\n }\n\n const row = await query.executeTakeFirst();\n if (!row) return undefined;\n\n // 如果请求了特定的 checkpoint_id 但不匹配(在 shallow 模式下只有一条记录)\n if (requestedCheckpointId && row.checkpoint_id !== requestedCheckpointId) {\n return undefined;\n }\n\n // 反序列化 pending writes\n const pendingWrites = await Promise.all(\n (\n JSON.parse(row.pending_writes || '[]') as Array<{\n task_id: string;\n channel: string;\n type: string;\n value: string;\n }>\n ).map(async (write) => {\n return [\n write.task_id,\n write.channel,\n await this.serde.loadsTyped(write.type ?? 'json', write.value ?? ''),\n ] as [string, string, unknown];\n }),\n );\n\n // 反序列化 checkpoint\n const checkpoint = (await this.serde.loadsTyped(\n row.type ?? 'json',\n new TextDecoder().decode(row.checkpoint),\n )) as Checkpoint;\n\n // 处理 v < 4 迁移\n if (checkpoint.v < 4 && row.parent_checkpoint_id != null) {\n await this.migratePendingSends(checkpoint, row.thread_id, row.parent_checkpoint_id);\n }\n\n const finalConfig: RunnableConfig = {\n configurable: {\n thread_id: row.thread_id,\n checkpoint_ns: row.checkpoint_ns,\n checkpoint_id: row.checkpoint_id,\n },\n };\n\n return {\n checkpoint,\n config: finalConfig,\n metadata: (await this.serde.loadsTyped(\n row.type ?? 'json',\n new TextDecoder().decode(row.metadata),\n )) as CheckpointMetadata,\n parentConfig: row.parent_checkpoint_id\n ? {\n configurable: {\n thread_id: row.thread_id,\n checkpoint_ns: row.checkpoint_ns,\n checkpoint_id: row.parent_checkpoint_id,\n },\n }\n : undefined,\n pendingWrites,\n };\n }\n\n async *list(config: RunnableConfig, options?: CheckpointListOptions): AsyncGenerator<CheckpointTuple> {\n await this.setup();\n const { limit, before, filter } = options ?? {};\n const threadId = config.configurable?.thread_id;\n const checkpointNs = config.configurable?.checkpoint_ns;\n\n let query = this.db.selectFrom('shallow_checkpoints').select([\n 'thread_id',\n 'checkpoint_ns',\n 'checkpoint_id',\n 'parent_checkpoint_id',\n 'type',\n 'checkpoint',\n 'metadata',\n 'checkpoint_ts',\n sql<string>`(\n SELECT json_group_array(\n json_object(\n 'task_id', pw.task_id,\n 'channel', pw.channel,\n 'type', pw.type,\n 'value', CAST(pw.value AS TEXT)\n )\n )\n FROM writes as pw\n WHERE pw.thread_id = shallow_checkpoints.thread_id\n AND pw.checkpoint_ns = shallow_checkpoints.checkpoint_ns\n AND pw.checkpoint_id = shallow_checkpoints.checkpoint_id\n )`.as('pending_writes'),\n ]);\n\n if (threadId) {\n query = query.where('thread_id', '=', threadId);\n }\n\n if (checkpointNs !== undefined && checkpointNs !== null) {\n query = query.where('checkpoint_ns', '=', checkpointNs);\n }\n\n if (before?.configurable?.checkpoint_id !== undefined) {\n query = query.where('checkpoint_id', '<', before.configurable.checkpoint_id);\n }\n\n // 按时间戳降序(最新的在前)\n query = query.orderBy('checkpoint_ts', 'desc');\n\n const rows = await query.execute();\n let count = 0;\n\n for (const row of rows) {\n // 先反序列化 metadata 以便在应用层过滤\n const metadata = (await this.serde.loadsTyped(\n row.type ?? 'json',\n new TextDecoder().decode(row.metadata),\n )) as CheckpointMetadata;\n\n // 应用层 metadata 过滤(与 ShallowMemorySaver 保持一致)\n if (filter && !this._checkMetadataFilterMatch(metadata, filter as CheckpointMetadata)) {\n continue;\n }\n\n // 应用 limit\n if (limit !== undefined && count >= limit) {\n return;\n }\n\n const pendingWrites = await Promise.all(\n (\n JSON.parse(row.pending_writes || '[]') as Array<{\n task_id: string;\n channel: string;\n type: string;\n value: string;\n }>\n ).map(async (write) => {\n return [\n write.task_id,\n write.channel,\n await this.serde.loadsTyped(write.type ?? 'json', write.value ?? ''),\n ] as [string, string, unknown];\n }),\n );\n\n const checkpoint = (await this.serde.loadsTyped(\n row.type ?? 'json',\n new TextDecoder().decode(row.checkpoint),\n )) as Checkpoint;\n\n if (checkpoint.v < 4 && row.parent_checkpoint_id != null) {\n await this.migratePendingSends(checkpoint, row.thread_id, row.parent_checkpoint_id);\n }\n\n count++;\n yield {\n config: {\n configurable: {\n thread_id: row.thread_id,\n checkpoint_ns: row.checkpoint_ns,\n checkpoint_id: row.checkpoint_id,\n },\n },\n checkpoint,\n metadata,\n parentConfig: row.parent_checkpoint_id\n ? {\n configurable: {\n thread_id: row.thread_id,\n checkpoint_ns: row.checkpoint_ns,\n checkpoint_id: row.parent_checkpoint_id,\n },\n }\n : undefined,\n pendingWrites,\n };\n }\n }\n\n /**\n * Check metadata filter matches (with deep comparison support)\n * Matches ShallowMemorySaver behavior\n */\n private _checkMetadataFilterMatch(metadata: any, filter: CheckpointMetadata): boolean {\n for (const [key, value] of Object.entries(filter)) {\n const metadataValue = metadata?.[key];\n\n if (value === null) {\n // For null filter value, check if key doesn't exist or value is null\n if (!(key in (metadata || {})) || metadataValue !== null) {\n return false;\n }\n } else if (typeof value === 'object' && !Array.isArray(value)) {\n // Deep comparison for objects with deterministic key ordering\n if (typeof metadataValue !== 'object' || metadataValue === null) {\n return false;\n }\n if (deterministicStringify(value) !== deterministicStringify(metadataValue)) {\n return false;\n }\n } else if (metadataValue !== value) {\n return false;\n }\n }\n return true;\n }\n\n async put(\n config: RunnableConfig,\n checkpoint: Checkpoint,\n metadata: CheckpointMetadata,\n _newVersions?: Record<string, string | number>,\n ): Promise<RunnableConfig> {\n await this.setup();\n\n const threadId = config.configurable?.thread_id;\n const checkpointNs = config.configurable?.checkpoint_ns ?? '';\n const parentCheckpointId = config.configurable?.checkpoint_id;\n\n if (!threadId) {\n throw new Error('thread_id is required');\n }\n\n // 使用 checkpoint.id 或生成新的\n const checkpointId = checkpoint.id || uuid6(0);\n\n const preparedCheckpoint: Partial<Checkpoint> = copyCheckpoint(checkpoint);\n\n // 序列化\n const [[type1, serializedCheckpoint], [type2, serializedMetadata]] = await Promise.all([\n this.serde.dumpsTyped(preparedCheckpoint),\n this.serde.dumpsTyped(metadata),\n ]);\n\n if (type1 !== type2) {\n throw new Error('Failed to serialize checkpoint and metadata to the same type.');\n }\n\n const threadId_ = threadId;\n const checkpointNs_ = checkpointNs;\n const checkpointId_ = checkpointId;\n\n // 带重试的数据库操作\n await withRetry(async () => {\n await this.db.transaction().execute(async (trx) => {\n // 获取旧的 checkpoint_id 用于清理 writes\n const oldCheckpoint = await trx\n .selectFrom('shallow_checkpoints')\n .select(['checkpoint_id'])\n .where('thread_id', '=', threadId_)\n .where('checkpoint_ns', '=', checkpointNs_)\n .executeTakeFirst();\n\n // 如果存在旧的 checkpoint 且 id 不同,清理旧的 writes\n if (oldCheckpoint && oldCheckpoint.checkpoint_id !== checkpointId_) {\n await trx\n .deleteFrom('writes')\n .where('thread_id', '=', threadId_)\n .where('checkpoint_ns', '=', checkpointNs_)\n .where('checkpoint_id', '=', oldCheckpoint.checkpoint_id)\n .execute();\n }\n\n // 使用 INSERT OR REPLACE (UPSERT) 覆盖写入\n await trx\n .insertInto('shallow_checkpoints')\n .values({\n thread_id: threadId_,\n checkpoint_ns: checkpointNs_,\n checkpoint_id: checkpointId_,\n parent_checkpoint_id: parentCheckpointId ?? null,\n type: type1,\n checkpoint: new Uint8Array(Buffer.from(serializedCheckpoint)),\n metadata: new Uint8Array(Buffer.from(serializedMetadata)),\n checkpoint_ts: Date.now(),\n })\n .onConflict((oc) =>\n oc.columns(['thread_id', 'checkpoint_ns']).doUpdateSet({\n checkpoint_id: checkpointId_,\n parent_checkpoint_id: parentCheckpointId ?? null,\n type: type1,\n checkpoint: new Uint8Array(Buffer.from(serializedCheckpoint)),\n metadata: new Uint8Array(Buffer.from(serializedMetadata)),\n checkpoint_ts: Date.now(),\n }),\n )\n .execute();\n });\n }, `put(${threadId}/${checkpointId})`);\n\n return {\n configurable: {\n thread_id: threadId,\n checkpoint_ns: checkpointNs,\n checkpoint_id: checkpointId,\n },\n };\n }\n\n async putWrites(config: RunnableConfig, writes: PendingWrite[], taskId: string): Promise<void> {\n await this.setup();\n\n const threadId = config.configurable?.thread_id;\n const checkpointNs = config.configurable?.checkpoint_ns ?? '';\n const checkpointId = config.configurable?.checkpoint_id;\n\n if (!threadId || !checkpointId) {\n throw new Error('thread_id and checkpoint_id are required');\n }\n\n // 预先序列化\n const values = await Promise.all(\n writes.map(async (write, idx) => {\n const [type, serializedWrite] = await this.serde.dumpsTyped(write[1]);\n return {\n thread_id: threadId,\n checkpoint_ns: checkpointNs,\n checkpoint_id: checkpointId,\n task_id: taskId,\n idx: WRITES_IDX_MAP[write[0]] ?? idx,\n channel: write[0],\n type,\n value: new Uint8Array(Buffer.from(serializedWrite)),\n };\n }),\n );\n\n if (values.length === 0) return;\n\n // 带重试的批量插入\n await withRetry(async () => {\n await this.db.transaction().execute(async (trx) => {\n // 先删除已存在的记录\n await trx\n .deleteFrom('writes')\n .where('thread_id', '=', threadId)\n .where('checkpoint_ns', '=', checkpointNs)\n .where('checkpoint_id', '=', checkpointId)\n .where('task_id', '=', taskId)\n .execute();\n\n // 批量插入\n for (const value of values) {\n await trx.insertInto('writes').values(value).execute();\n }\n });\n }, `putWrites(${threadId}/${checkpointId}/${taskId})`);\n }\n\n async deleteThread(threadId: string): Promise<void> {\n await withRetry(async () => {\n await this.db.transaction().execute(async (trx) => {\n await trx.deleteFrom('shallow_checkpoints').where('thread_id', '=', threadId).execute();\n await trx.deleteFrom('writes').where('thread_id', '=', threadId).execute();\n });\n }, `deleteThread(${threadId})`);\n }\n\n protected async migratePendingSends(checkpoint: Checkpoint, threadId: string, parentCheckpointId: string) {\n const result = await this.db\n .selectFrom('writes as ps')\n .select([\n 'ps.checkpoint_id',\n sql<string>`json_group_array(\n json_object(\n 'type', ps.type,\n 'value', CAST(ps.value AS TEXT)\n )\n )`.as('pending_sends'),\n ])\n .where('ps.thread_id', '=', threadId)\n .where('ps.checkpoint_id', '=', parentCheckpointId)\n .where('ps.channel', '=', TASKS)\n .orderBy('ps.idx')\n .executeTakeFirst();\n\n if (!result) return;\n\n const mutableCheckpoint = checkpoint;\n\n mutableCheckpoint.channel_values ??= {};\n mutableCheckpoint.channel_values[TASKS] = await Promise.all(\n JSON.parse(result.pending_sends || '[]').map(({ type, value }: { type: string; value: string }) =>\n this.serde.loadsTyped(type, value),\n ),\n );\n\n mutableCheckpoint.channel_versions[TASKS] =\n Object.keys(checkpoint.channel_versions).length > 0\n ? maxChannelVersion(...Object.values(checkpoint.channel_versions))\n : this.getNextVersion(undefined);\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -42,8 +42,12 @@ class SQLiteAdapter {
|
|
|
42
42
|
return dbValue;
|
|
43
43
|
}
|
|
44
44
|
buildJsonQuery(db, field, key, value) {
|
|
45
|
-
const
|
|
46
|
-
|
|
45
|
+
const jsonPath = `$.${JSON.stringify(key)}`;
|
|
46
|
+
if (value === null) {
|
|
47
|
+
return sql`
|
|
48
|
+
json_type(${sql.ref(field)}, ${sql.lit(jsonPath)}) = 'null'
|
|
49
|
+
`;
|
|
50
|
+
}
|
|
47
51
|
let compareValue;
|
|
48
52
|
if (typeof value === "string") {
|
|
49
53
|
compareValue = value;
|
|
@@ -51,18 +55,9 @@ class SQLiteAdapter {
|
|
|
51
55
|
compareValue = value;
|
|
52
56
|
} else if (typeof value === "boolean") {
|
|
53
57
|
compareValue = value ? 1 : 0;
|
|
54
|
-
} else if (value === null) {
|
|
55
|
-
compareValue = null;
|
|
56
58
|
} else {
|
|
57
59
|
compareValue = JSON.stringify(value);
|
|
58
60
|
}
|
|
59
|
-
if (value === null) {
|
|
60
|
-
const keyPattern = `"${key}":null`;
|
|
61
|
-
return sql`
|
|
62
|
-
json_extract(${sql.ref(field)}, ${sql.lit(jsonPath)}) IS NULL
|
|
63
|
-
AND ${sql.ref(field)} LIKE ${sql.lit(`%${keyPattern}%`)}
|
|
64
|
-
`;
|
|
65
|
-
}
|
|
66
61
|
return sql`json_extract(${sql.ref(field)}, ${sql.lit(jsonPath)}) = ${sql.lit(compareValue)}`;
|
|
67
62
|
}
|
|
68
63
|
now() {
|
|
@@ -105,4 +100,4 @@ class SQLiteAdapter {
|
|
|
105
100
|
}
|
|
106
101
|
|
|
107
102
|
export { SQLiteAdapter };
|
|
108
|
-
//# sourceMappingURL=sqlite-adapter-
|
|
103
|
+
//# sourceMappingURL=sqlite-adapter-CJXgit1j.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqlite-adapter-CJXgit1j.js","sources":["../src/storage/kysely/sqlite-adapter.ts"],"sourcesContent":["import { Kysely, sql, SqlBool, Expression, SqliteDialect } from 'kysely';\nimport { DatabaseAdapter } from './adapter';\nimport { Database } from './types';\n\n/**\n * SQLite 适配器\n * - 时间类型:TEXT (ISO 8601 字符串)\n * - JSON 类型:TEXT\n * - JSON 查询:使用 json_extract 函数\n */\nexport class SQLiteAdapter implements DatabaseAdapter {\n db: Kysely<Database>;\n private pragmaPromise: Promise<void> | null = null;\n\n constructor(database: Kysely<any>) {\n this.db = database;\n }\n\n /**\n * 设置 SQLite PRAGMA 配置,解决锁问题\n * 使用 Promise 缓存确保并发安全且只执行一次\n */\n private ensurePragma(): Promise<void> {\n if (!this.pragmaPromise) {\n this.pragmaPromise = this.doEnsurePragma();\n }\n return this.pragmaPromise;\n }\n\n private async doEnsurePragma(): Promise<void> {\n // 锁等待超时 5 秒\n await sql`PRAGMA busy_timeout = 5000`.execute(this.db);\n // WAL 模式 - 读写并发\n await sql`PRAGMA journal_mode = WAL`.execute(this.db);\n // 平衡安全与性能\n await sql`PRAGMA synchronous = NORMAL`.execute(this.db);\n // 自动清理 WAL\n await sql`PRAGMA wal_autocheckpoint = 1000`.execute(this.db);\n }\n dateToDb(date: Date): string {\n // SQLite 存储为 ISO 8601 字符串\n return date.toISOString();\n }\n\n dbToDate(dbValue: any): Date {\n // SQLite 返回字符串,需要转换为 Date\n return new Date(dbValue);\n }\n\n jsonToDb(obj: any): string {\n // SQLite 存储为 JSON 字符串\n return JSON.stringify(obj);\n }\n\n dbToJson(dbValue: any): any {\n // SQLite 返回字符串,需要解析\n if (typeof dbValue === 'string') {\n try {\n return JSON.parse(dbValue);\n } catch {\n return dbValue;\n }\n }\n return dbValue;\n }\n\n buildJsonQuery(\n db: Kysely<Database>,\n field: 'metadata' | 'interrupts',\n key: string,\n value: any,\n ): Expression<SqlBool> {\n // SQLite json_extract 的行为:\n // - 字符串: 返回不带引号的文本\n // - 数字: 返回数字\n // - 布尔值: 返回 1/0\n // - null: 返回 NULL\n // 所以比较时需要根据类型处理\n\n // 构建 JSON 路径\n // 在 SQLite 中,所有键都需要用双引号括起来以确保正确解析\n // json_extract 语法: $.key 或 $.\"key-with-special\"\n const jsonPath = `$.${JSON.stringify(key)}`;\n\n // 构建 NULL 处理条件\n // 注意:SQLite 中 json_extract 对键不存在和值为 null 都返回 NULL\n // 使用 json_type 函数区分:\n // - 键不存在:json_type 返回 NULL\n // - 值为 null:json_type 返回 'null' 字符串\n if (value === null) {\n return sql<boolean>`\n json_type(${sql.ref(field)}, ${sql.lit(jsonPath)}) = 'null'\n `;\n }\n\n // 处理其他类型的值\n let compareValue: any;\n if (typeof value === 'string') {\n // 字符串: json_extract 返回不带引号的字符串,直接比较\n compareValue = value;\n } else if (typeof value === 'number') {\n // 数字: json_extract 返回数字\n compareValue = value;\n } else if (typeof value === 'boolean') {\n // 布尔值: json_extract 返回 1/0\n compareValue = value ? 1 : 0;\n } else {\n // 其他类型(对象、数组): 使用 JSON 字符串\n compareValue = JSON.stringify(value);\n }\n\n // 构建普通比较条件\n return sql<boolean>`json_extract(${sql.ref(field)}, ${sql.lit(jsonPath)}) = ${sql.lit(compareValue)}`;\n }\n\n now(): string {\n return new Date().toISOString();\n }\n\n async createTables(db: Kysely<Database>): Promise<void> {\n // 先设置 PRAGMA\n await this.ensurePragma();\n\n // 创建 threads 表\n await sql`\n CREATE TABLE IF NOT EXISTS threads (\n thread_id TEXT PRIMARY KEY,\n created_at TEXT NOT NULL,\n updated_at TEXT NOT NULL,\n metadata TEXT NOT NULL DEFAULT '{}',\n status TEXT NOT NULL DEFAULT 'idle',\n \"values\" TEXT,\n interrupts TEXT NOT NULL DEFAULT '{}'\n )\n `.execute(db);\n\n // 创建 runs 表\n await sql`\n CREATE TABLE IF NOT EXISTS runs (\n run_id TEXT PRIMARY KEY,\n thread_id TEXT NOT NULL,\n assistant_id TEXT NOT NULL,\n created_at TEXT NOT NULL,\n updated_at TEXT NOT NULL,\n status TEXT NOT NULL DEFAULT 'pending',\n metadata TEXT NOT NULL DEFAULT '{}',\n multitask_strategy TEXT NOT NULL DEFAULT 'reject',\n FOREIGN KEY (thread_id) REFERENCES threads(thread_id) ON DELETE CASCADE\n )\n `.execute(db);\n }\n\n async createIndexes(db: Kysely<Database>): Promise<void> {\n await sql`CREATE INDEX IF NOT EXISTS idx_threads_status ON threads(status)`.execute(db);\n await sql`CREATE INDEX IF NOT EXISTS idx_threads_created_at ON threads(created_at)`.execute(db);\n await sql`CREATE INDEX IF NOT EXISTS idx_threads_updated_at ON threads(updated_at)`.execute(db);\n await sql`CREATE INDEX IF NOT EXISTS idx_runs_thread_id ON runs(thread_id)`.execute(db);\n await sql`CREATE INDEX IF NOT EXISTS idx_runs_status ON runs(status)`.execute(db);\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -4,6 +4,8 @@ import { BaseStreamQueueInterface } from '../../queue/stream_queue.js';
|
|
|
4
4
|
/** 内存实现的消息队列,用于存储消息 */
|
|
5
5
|
export declare class MemoryStreamQueue extends BaseStreamQueue implements BaseStreamQueueInterface {
|
|
6
6
|
private data;
|
|
7
|
+
private activeGenerators;
|
|
8
|
+
private isDestroyed;
|
|
7
9
|
push(item: EventMessage): Promise<void>;
|
|
8
10
|
onDataChange(listener: (data: EventMessage) => void): () => void;
|
|
9
11
|
/**
|
|
@@ -15,4 +17,8 @@ export declare class MemoryStreamQueue extends BaseStreamQueue implements BaseSt
|
|
|
15
17
|
cancelSignal: AbortController;
|
|
16
18
|
cancel(): Promise<void>;
|
|
17
19
|
copyToQueue(toId: string, ttl?: number): Promise<MemoryStreamQueue>;
|
|
20
|
+
/**
|
|
21
|
+
* 销毁队列,释放所有资源
|
|
22
|
+
*/
|
|
23
|
+
destroy(): Promise<void>;
|
|
18
24
|
}
|