@voltagent/libsql 1.0.14 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/edge.d.mts +111 -0
- package/dist/edge.d.ts +111 -0
- package/dist/edge.js +2183 -0
- package/dist/edge.js.map +1 -0
- package/dist/edge.mjs +2155 -0
- package/dist/edge.mjs.map +1 -0
- package/dist/index.d.mts +24 -391
- package/dist/index.d.ts +24 -391
- package/dist/index.js +209 -340
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +208 -343
- package/dist/index.mjs.map +1 -1
- package/dist/vector-core-CKn8FNVK.d.mts +274 -0
- package/dist/vector-core-CKn8FNVK.d.ts +274 -0
- package/package.json +12 -2
package/dist/edge.mjs
ADDED
|
@@ -0,0 +1,2155 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
3
|
+
|
|
4
|
+
// src/memory-v2-adapter-edge.ts
|
|
5
|
+
import { createClient } from "@libsql/client/web";
|
|
6
|
+
import { AgentRegistry } from "@voltagent/core";
|
|
7
|
+
import { createPinoLogger } from "@voltagent/logger";
|
|
8
|
+
|
|
9
|
+
// src/memory-core.ts
|
|
10
|
+
import { ConversationAlreadyExistsError, ConversationNotFoundError } from "@voltagent/core";
|
|
11
|
+
import { safeStringify } from "@voltagent/internal";
|
|
12
|
+
var LibSQLMemoryCore = class {
|
|
13
|
+
static {
|
|
14
|
+
__name(this, "LibSQLMemoryCore");
|
|
15
|
+
}
|
|
16
|
+
client;
|
|
17
|
+
tablePrefix;
|
|
18
|
+
initialized = false;
|
|
19
|
+
logger;
|
|
20
|
+
maxRetries;
|
|
21
|
+
retryDelayMs;
|
|
22
|
+
url;
|
|
23
|
+
constructor(client, url, options, logger) {
|
|
24
|
+
this.client = client;
|
|
25
|
+
this.url = url;
|
|
26
|
+
this.tablePrefix = options.tablePrefix ?? "voltagent_memory";
|
|
27
|
+
this.maxRetries = options.maxRetries ?? 3;
|
|
28
|
+
this.retryDelayMs = options.retryDelayMs ?? 100;
|
|
29
|
+
this.logger = logger;
|
|
30
|
+
this.logger.debug("LibSQL Memory adapter core initialized", { url: this.url });
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Execute a database operation with retry logic
|
|
34
|
+
*/
|
|
35
|
+
async executeWithRetry(operation, operationName) {
|
|
36
|
+
let lastError;
|
|
37
|
+
for (let attempt = 0; attempt < this.maxRetries; attempt++) {
|
|
38
|
+
try {
|
|
39
|
+
return await operation();
|
|
40
|
+
} catch (error) {
|
|
41
|
+
lastError = error;
|
|
42
|
+
if (error?.code === "SQLITE_BUSY" || error?.message?.includes("SQLITE_BUSY") || error?.message?.includes("database is locked")) {
|
|
43
|
+
const delay = this.retryDelayMs * 2 ** attempt;
|
|
44
|
+
this.logger.debug(
|
|
45
|
+
`Database busy, retrying ${operationName} (attempt ${attempt + 1}/${this.maxRetries}) after ${delay}ms`
|
|
46
|
+
);
|
|
47
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
48
|
+
} else {
|
|
49
|
+
throw error;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
this.logger.error(
|
|
54
|
+
`Failed to execute ${operationName} after ${this.maxRetries} attempts`,
|
|
55
|
+
lastError
|
|
56
|
+
);
|
|
57
|
+
throw lastError;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Initialize database schema
|
|
61
|
+
*/
|
|
62
|
+
async initialize() {
|
|
63
|
+
if (this.initialized) return;
|
|
64
|
+
const conversationsTable = `${this.tablePrefix}_conversations`;
|
|
65
|
+
const messagesTable = `${this.tablePrefix}_messages`;
|
|
66
|
+
const usersTable = `${this.tablePrefix}_users`;
|
|
67
|
+
const workflowStatesTable = `${this.tablePrefix}_workflow_states`;
|
|
68
|
+
const stepsTable = `${this.tablePrefix}_steps`;
|
|
69
|
+
const isMemoryDb = this.url === ":memory:" || this.url.includes("mode=memory");
|
|
70
|
+
if (!isMemoryDb && (this.url.startsWith("file:") || this.url.startsWith("libsql:"))) {
|
|
71
|
+
try {
|
|
72
|
+
await this.client.execute("PRAGMA journal_mode=WAL");
|
|
73
|
+
this.logger.debug("Set PRAGMA journal_mode=WAL");
|
|
74
|
+
} catch (err) {
|
|
75
|
+
this.logger.debug("Failed to set PRAGMA journal_mode=WAL (non-critical)", { err });
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
try {
|
|
79
|
+
await this.client.execute("PRAGMA busy_timeout=5000");
|
|
80
|
+
this.logger.debug("Set PRAGMA busy_timeout=5000");
|
|
81
|
+
} catch (err) {
|
|
82
|
+
this.logger.debug("Failed to set PRAGMA busy_timeout (non-critical)", { err });
|
|
83
|
+
}
|
|
84
|
+
try {
|
|
85
|
+
await this.client.execute("PRAGMA foreign_keys=ON");
|
|
86
|
+
this.logger.debug("Set PRAGMA foreign_keys=ON");
|
|
87
|
+
} catch (err) {
|
|
88
|
+
this.logger.debug("Failed to set PRAGMA foreign_keys (non-critical)", { err });
|
|
89
|
+
}
|
|
90
|
+
this.logger.debug("Applied PRAGMA settings for better concurrency");
|
|
91
|
+
await this.executeWithRetry(async () => {
|
|
92
|
+
await this.client.batch([
|
|
93
|
+
`CREATE TABLE IF NOT EXISTS ${usersTable} (
|
|
94
|
+
id TEXT PRIMARY KEY,
|
|
95
|
+
metadata TEXT,
|
|
96
|
+
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
|
|
97
|
+
updated_at TEXT DEFAULT CURRENT_TIMESTAMP
|
|
98
|
+
)`,
|
|
99
|
+
`CREATE TABLE IF NOT EXISTS ${conversationsTable} (
|
|
100
|
+
id TEXT PRIMARY KEY,
|
|
101
|
+
resource_id TEXT NOT NULL,
|
|
102
|
+
user_id TEXT NOT NULL,
|
|
103
|
+
title TEXT NOT NULL,
|
|
104
|
+
metadata TEXT NOT NULL,
|
|
105
|
+
created_at TEXT NOT NULL,
|
|
106
|
+
updated_at TEXT NOT NULL
|
|
107
|
+
)`,
|
|
108
|
+
`CREATE TABLE IF NOT EXISTS ${messagesTable} (
|
|
109
|
+
conversation_id TEXT NOT NULL,
|
|
110
|
+
message_id TEXT NOT NULL,
|
|
111
|
+
user_id TEXT NOT NULL,
|
|
112
|
+
role TEXT NOT NULL,
|
|
113
|
+
parts TEXT NOT NULL,
|
|
114
|
+
metadata TEXT,
|
|
115
|
+
format_version INTEGER DEFAULT 2,
|
|
116
|
+
created_at TEXT NOT NULL,
|
|
117
|
+
PRIMARY KEY (conversation_id, message_id)
|
|
118
|
+
)`,
|
|
119
|
+
`CREATE TABLE IF NOT EXISTS ${workflowStatesTable} (
|
|
120
|
+
id TEXT PRIMARY KEY,
|
|
121
|
+
workflow_id TEXT NOT NULL,
|
|
122
|
+
workflow_name TEXT NOT NULL,
|
|
123
|
+
status TEXT NOT NULL,
|
|
124
|
+
suspension TEXT,
|
|
125
|
+
events TEXT,
|
|
126
|
+
output TEXT,
|
|
127
|
+
cancellation TEXT,
|
|
128
|
+
user_id TEXT,
|
|
129
|
+
conversation_id TEXT,
|
|
130
|
+
metadata TEXT,
|
|
131
|
+
created_at TEXT NOT NULL,
|
|
132
|
+
updated_at TEXT NOT NULL
|
|
133
|
+
)`,
|
|
134
|
+
`CREATE TABLE IF NOT EXISTS ${stepsTable} (
|
|
135
|
+
id TEXT PRIMARY KEY,
|
|
136
|
+
conversation_id TEXT NOT NULL,
|
|
137
|
+
user_id TEXT NOT NULL,
|
|
138
|
+
agent_id TEXT NOT NULL,
|
|
139
|
+
agent_name TEXT,
|
|
140
|
+
operation_id TEXT,
|
|
141
|
+
step_index INTEGER NOT NULL,
|
|
142
|
+
type TEXT NOT NULL,
|
|
143
|
+
role TEXT NOT NULL,
|
|
144
|
+
content TEXT,
|
|
145
|
+
arguments TEXT,
|
|
146
|
+
result TEXT,
|
|
147
|
+
usage TEXT,
|
|
148
|
+
sub_agent_id TEXT,
|
|
149
|
+
sub_agent_name TEXT,
|
|
150
|
+
created_at TEXT NOT NULL,
|
|
151
|
+
FOREIGN KEY (conversation_id) REFERENCES ${conversationsTable}(id) ON DELETE CASCADE
|
|
152
|
+
)`,
|
|
153
|
+
`CREATE INDEX IF NOT EXISTS idx_${conversationsTable}_user_id ON ${conversationsTable}(user_id)`,
|
|
154
|
+
`CREATE INDEX IF NOT EXISTS idx_${conversationsTable}_resource_id ON ${conversationsTable}(resource_id)`,
|
|
155
|
+
`CREATE INDEX IF NOT EXISTS idx_${messagesTable}_conversation_id ON ${messagesTable}(conversation_id)`,
|
|
156
|
+
`CREATE INDEX IF NOT EXISTS idx_${messagesTable}_created_at ON ${messagesTable}(created_at)`,
|
|
157
|
+
`CREATE INDEX IF NOT EXISTS idx_${workflowStatesTable}_workflow_id ON ${workflowStatesTable}(workflow_id)`,
|
|
158
|
+
`CREATE INDEX IF NOT EXISTS idx_${workflowStatesTable}_status ON ${workflowStatesTable}(status)`,
|
|
159
|
+
`CREATE INDEX IF NOT EXISTS idx_${stepsTable}_conversation ON ${stepsTable}(conversation_id, step_index)`,
|
|
160
|
+
`CREATE INDEX IF NOT EXISTS idx_${stepsTable}_operation ON ${stepsTable}(conversation_id, operation_id)`
|
|
161
|
+
]);
|
|
162
|
+
}, "initialize database schema");
|
|
163
|
+
await this.addV2ColumnsToMessagesTable();
|
|
164
|
+
await this.migrateDefaultUserIds();
|
|
165
|
+
await this.addWorkflowStateColumns();
|
|
166
|
+
this.initialized = true;
|
|
167
|
+
this.logger.debug("Database schema initialized");
|
|
168
|
+
}
|
|
169
|
+
async addV2ColumnsToMessagesTable() {
|
|
170
|
+
const messagesTableName = `${this.tablePrefix}_messages`;
|
|
171
|
+
try {
|
|
172
|
+
const tableInfo = await this.client.execute(`PRAGMA table_info(${messagesTableName})`);
|
|
173
|
+
const columns = tableInfo.rows.map((row) => row.name);
|
|
174
|
+
if (!columns.includes("parts")) {
|
|
175
|
+
try {
|
|
176
|
+
await this.client.execute(`ALTER TABLE ${messagesTableName} ADD COLUMN parts TEXT`);
|
|
177
|
+
} catch (_e) {
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
if (!columns.includes("metadata")) {
|
|
181
|
+
try {
|
|
182
|
+
await this.client.execute(`ALTER TABLE ${messagesTableName} ADD COLUMN metadata TEXT`);
|
|
183
|
+
} catch (_e) {
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
if (!columns.includes("format_version")) {
|
|
187
|
+
try {
|
|
188
|
+
await this.client.execute(
|
|
189
|
+
`ALTER TABLE ${messagesTableName} ADD COLUMN format_version INTEGER DEFAULT 2`
|
|
190
|
+
);
|
|
191
|
+
} catch (_e) {
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
if (!columns.includes("user_id")) {
|
|
195
|
+
try {
|
|
196
|
+
await this.client.execute(
|
|
197
|
+
`ALTER TABLE ${messagesTableName} ADD COLUMN user_id TEXT NOT NULL DEFAULT 'default'`
|
|
198
|
+
);
|
|
199
|
+
} catch (_e) {
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
const contentInfo = tableInfo.rows.find((row) => row.name === "content");
|
|
203
|
+
if (contentInfo && contentInfo.notnull === 1) {
|
|
204
|
+
try {
|
|
205
|
+
await this.client.execute(
|
|
206
|
+
`ALTER TABLE ${messagesTableName} ADD COLUMN content_temp TEXT`
|
|
207
|
+
);
|
|
208
|
+
await this.client.execute(
|
|
209
|
+
`UPDATE ${messagesTableName} SET content_temp = content WHERE content IS NOT NULL`
|
|
210
|
+
);
|
|
211
|
+
try {
|
|
212
|
+
await this.client.execute(`ALTER TABLE ${messagesTableName} DROP COLUMN content`);
|
|
213
|
+
await this.client.execute(
|
|
214
|
+
`ALTER TABLE ${messagesTableName} RENAME COLUMN content_temp TO content`
|
|
215
|
+
);
|
|
216
|
+
} catch (_) {
|
|
217
|
+
}
|
|
218
|
+
} catch (_) {
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
const typeInfo = tableInfo.rows.find((row) => row.name === "type");
|
|
222
|
+
if (typeInfo && typeInfo.notnull === 1) {
|
|
223
|
+
try {
|
|
224
|
+
await this.client.execute(`ALTER TABLE ${messagesTableName} ADD COLUMN type_temp TEXT`);
|
|
225
|
+
await this.client.execute(
|
|
226
|
+
`UPDATE ${messagesTableName} SET type_temp = type WHERE type IS NOT NULL`
|
|
227
|
+
);
|
|
228
|
+
try {
|
|
229
|
+
await this.client.execute(`ALTER TABLE ${messagesTableName} DROP COLUMN type`);
|
|
230
|
+
await this.client.execute(
|
|
231
|
+
`ALTER TABLE ${messagesTableName} RENAME COLUMN type_temp TO type`
|
|
232
|
+
);
|
|
233
|
+
} catch (_) {
|
|
234
|
+
}
|
|
235
|
+
} catch (_) {
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
} catch (_) {
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
async migrateDefaultUserIds() {
|
|
242
|
+
const messagesTableName = `${this.tablePrefix}_messages`;
|
|
243
|
+
const conversationsTableName = `${this.tablePrefix}_conversations`;
|
|
244
|
+
try {
|
|
245
|
+
const checkResult = await this.client.execute({
|
|
246
|
+
sql: `SELECT COUNT(*) as count FROM ${messagesTableName} WHERE user_id = 'default'`,
|
|
247
|
+
args: []
|
|
248
|
+
});
|
|
249
|
+
const defaultCount = checkResult.rows[0]?.count || 0;
|
|
250
|
+
if (defaultCount === 0) {
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
this.logger.debug(`Found ${defaultCount} messages with default user_id, starting migration`);
|
|
254
|
+
await this.executeWithRetry(async () => {
|
|
255
|
+
const result = await this.client.execute({
|
|
256
|
+
sql: `UPDATE ${messagesTableName}
|
|
257
|
+
SET user_id = (
|
|
258
|
+
SELECT c.user_id
|
|
259
|
+
FROM ${conversationsTableName} c
|
|
260
|
+
WHERE c.id = ${messagesTableName}.conversation_id
|
|
261
|
+
)
|
|
262
|
+
WHERE user_id = 'default'
|
|
263
|
+
AND EXISTS (
|
|
264
|
+
SELECT 1
|
|
265
|
+
FROM ${conversationsTableName} c
|
|
266
|
+
WHERE c.id = ${messagesTableName}.conversation_id
|
|
267
|
+
)`,
|
|
268
|
+
args: []
|
|
269
|
+
});
|
|
270
|
+
const updatedCount = result.rowsAffected || 0;
|
|
271
|
+
this.logger.info(
|
|
272
|
+
`Successfully migrated ${updatedCount} messages from default user_id to actual user_ids`
|
|
273
|
+
);
|
|
274
|
+
const remainingResult = await this.client.execute({
|
|
275
|
+
sql: `SELECT COUNT(*) as count FROM ${messagesTableName} WHERE user_id = 'default'`,
|
|
276
|
+
args: []
|
|
277
|
+
});
|
|
278
|
+
const remainingCount = remainingResult.rows[0]?.count || 0;
|
|
279
|
+
if (remainingCount > 0) {
|
|
280
|
+
this.logger.warn(
|
|
281
|
+
`${remainingCount} messages still have default user_id (possibly orphaned messages without valid conversations)`
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
}, "migrate default user_ids");
|
|
285
|
+
} catch (error) {
|
|
286
|
+
this.logger.error("Failed to migrate default user_ids", error);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
async addWorkflowStateColumns() {
|
|
290
|
+
const workflowStatesTable = `${this.tablePrefix}_workflow_states`;
|
|
291
|
+
try {
|
|
292
|
+
const tableInfo = await this.client.execute(`PRAGMA table_info(${workflowStatesTable})`);
|
|
293
|
+
const columns = tableInfo.rows.map((row) => row.name);
|
|
294
|
+
if (!columns.includes("events")) {
|
|
295
|
+
try {
|
|
296
|
+
await this.client.execute(`ALTER TABLE ${workflowStatesTable} ADD COLUMN events TEXT`);
|
|
297
|
+
this.logger.debug("Added 'events' column to workflow_states table");
|
|
298
|
+
} catch (_e) {
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
if (!columns.includes("output")) {
|
|
302
|
+
try {
|
|
303
|
+
await this.client.execute(`ALTER TABLE ${workflowStatesTable} ADD COLUMN output TEXT`);
|
|
304
|
+
this.logger.debug("Added 'output' column to workflow_states table");
|
|
305
|
+
} catch (_e) {
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
if (!columns.includes("cancellation")) {
|
|
309
|
+
try {
|
|
310
|
+
await this.client.execute(
|
|
311
|
+
`ALTER TABLE ${workflowStatesTable} ADD COLUMN cancellation TEXT`
|
|
312
|
+
);
|
|
313
|
+
this.logger.debug("Added 'cancellation' column to workflow_states table");
|
|
314
|
+
} catch (_e) {
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
} catch (error) {
|
|
318
|
+
this.logger.warn("Failed to add workflow state columns (non-critical)", error);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
// ============================================================================
|
|
322
|
+
// Message Operations
|
|
323
|
+
// ============================================================================
|
|
324
|
+
async addMessage(message, userId, conversationId) {
|
|
325
|
+
await this.initialize();
|
|
326
|
+
const messagesTable = `${this.tablePrefix}_messages`;
|
|
327
|
+
const conversation = await this.getConversation(conversationId);
|
|
328
|
+
if (!conversation) {
|
|
329
|
+
throw new ConversationNotFoundError(conversationId);
|
|
330
|
+
}
|
|
331
|
+
await this.executeWithRetry(async () => {
|
|
332
|
+
await this.client.execute({
|
|
333
|
+
sql: `INSERT INTO ${messagesTable} (conversation_id, message_id, user_id, role, parts, metadata, format_version, created_at)
|
|
334
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
335
|
+
args: [
|
|
336
|
+
conversationId,
|
|
337
|
+
message.id,
|
|
338
|
+
userId,
|
|
339
|
+
message.role,
|
|
340
|
+
safeStringify(message.parts),
|
|
341
|
+
message.metadata ? safeStringify(message.metadata) : null,
|
|
342
|
+
2,
|
|
343
|
+
(/* @__PURE__ */ new Date()).toISOString()
|
|
344
|
+
]
|
|
345
|
+
});
|
|
346
|
+
}, "add message");
|
|
347
|
+
}
|
|
348
|
+
async addMessages(messages, userId, conversationId) {
|
|
349
|
+
await this.initialize();
|
|
350
|
+
const messagesTable = `${this.tablePrefix}_messages`;
|
|
351
|
+
const conversation = await this.getConversation(conversationId);
|
|
352
|
+
if (!conversation) {
|
|
353
|
+
throw new ConversationNotFoundError(conversationId);
|
|
354
|
+
}
|
|
355
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
356
|
+
await this.executeWithRetry(async () => {
|
|
357
|
+
await this.client.batch(
|
|
358
|
+
messages.map((message) => ({
|
|
359
|
+
sql: `INSERT INTO ${messagesTable} (conversation_id, message_id, user_id, role, parts, metadata, format_version, created_at)
|
|
360
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
361
|
+
args: [
|
|
362
|
+
conversationId,
|
|
363
|
+
message.id,
|
|
364
|
+
userId,
|
|
365
|
+
message.role,
|
|
366
|
+
safeStringify(message.parts),
|
|
367
|
+
message.metadata ? safeStringify(message.metadata) : null,
|
|
368
|
+
2,
|
|
369
|
+
now
|
|
370
|
+
]
|
|
371
|
+
}))
|
|
372
|
+
);
|
|
373
|
+
}, "add batch messages");
|
|
374
|
+
}
|
|
375
|
+
async saveConversationSteps(steps) {
|
|
376
|
+
if (steps.length === 0) return;
|
|
377
|
+
await this.initialize();
|
|
378
|
+
const stepsTable = `${this.tablePrefix}_steps`;
|
|
379
|
+
await this.executeWithRetry(async () => {
|
|
380
|
+
await this.client.batch(
|
|
381
|
+
steps.map((step) => {
|
|
382
|
+
const createdAt = step.createdAt ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
383
|
+
return {
|
|
384
|
+
sql: `INSERT INTO ${stepsTable} (
|
|
385
|
+
id,
|
|
386
|
+
conversation_id,
|
|
387
|
+
user_id,
|
|
388
|
+
agent_id,
|
|
389
|
+
agent_name,
|
|
390
|
+
operation_id,
|
|
391
|
+
step_index,
|
|
392
|
+
type,
|
|
393
|
+
role,
|
|
394
|
+
content,
|
|
395
|
+
arguments,
|
|
396
|
+
result,
|
|
397
|
+
usage,
|
|
398
|
+
sub_agent_id,
|
|
399
|
+
sub_agent_name,
|
|
400
|
+
created_at
|
|
401
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
402
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
403
|
+
conversation_id = excluded.conversation_id,
|
|
404
|
+
user_id = excluded.user_id,
|
|
405
|
+
agent_id = excluded.agent_id,
|
|
406
|
+
agent_name = excluded.agent_name,
|
|
407
|
+
operation_id = excluded.operation_id,
|
|
408
|
+
step_index = excluded.step_index,
|
|
409
|
+
type = excluded.type,
|
|
410
|
+
role = excluded.role,
|
|
411
|
+
content = excluded.content,
|
|
412
|
+
arguments = excluded.arguments,
|
|
413
|
+
result = excluded.result,
|
|
414
|
+
usage = excluded.usage,
|
|
415
|
+
sub_agent_id = excluded.sub_agent_id,
|
|
416
|
+
sub_agent_name = excluded.sub_agent_name,
|
|
417
|
+
created_at = excluded.created_at`,
|
|
418
|
+
args: [
|
|
419
|
+
step.id,
|
|
420
|
+
step.conversationId,
|
|
421
|
+
step.userId,
|
|
422
|
+
step.agentId,
|
|
423
|
+
step.agentName ?? null,
|
|
424
|
+
step.operationId ?? null,
|
|
425
|
+
step.stepIndex,
|
|
426
|
+
step.type,
|
|
427
|
+
step.role,
|
|
428
|
+
step.content ?? null,
|
|
429
|
+
step.arguments ? safeStringify(step.arguments) : null,
|
|
430
|
+
step.result ? safeStringify(step.result) : null,
|
|
431
|
+
step.usage ? safeStringify(step.usage) : null,
|
|
432
|
+
step.subAgentId ?? null,
|
|
433
|
+
step.subAgentName ?? null,
|
|
434
|
+
createdAt
|
|
435
|
+
]
|
|
436
|
+
};
|
|
437
|
+
})
|
|
438
|
+
);
|
|
439
|
+
}, "save conversation steps");
|
|
440
|
+
}
|
|
441
|
+
async getMessages(userId, conversationId, options) {
|
|
442
|
+
await this.initialize();
|
|
443
|
+
const messagesTable = `${this.tablePrefix}_messages`;
|
|
444
|
+
const { limit, before, after, roles } = options || {};
|
|
445
|
+
let sql = `SELECT * FROM ${messagesTable}
|
|
446
|
+
WHERE conversation_id = ? AND user_id = ?`;
|
|
447
|
+
const args = [conversationId, userId];
|
|
448
|
+
if (roles && roles.length > 0) {
|
|
449
|
+
const placeholders = roles.map(() => "?").join(",");
|
|
450
|
+
sql += ` AND role IN (${placeholders})`;
|
|
451
|
+
args.push(...roles);
|
|
452
|
+
}
|
|
453
|
+
if (before) {
|
|
454
|
+
sql += " AND created_at < ?";
|
|
455
|
+
args.push(before.toISOString());
|
|
456
|
+
}
|
|
457
|
+
if (after) {
|
|
458
|
+
sql += " AND created_at > ?";
|
|
459
|
+
args.push(after.toISOString());
|
|
460
|
+
}
|
|
461
|
+
sql += " ORDER BY created_at ASC";
|
|
462
|
+
if (limit && limit > 0) {
|
|
463
|
+
sql += " LIMIT ?";
|
|
464
|
+
args.push(limit);
|
|
465
|
+
}
|
|
466
|
+
const result = await this.client.execute({ sql, args });
|
|
467
|
+
return result.rows.map((row) => {
|
|
468
|
+
let parts;
|
|
469
|
+
if (row.parts !== void 0 && row.parts !== null) {
|
|
470
|
+
try {
|
|
471
|
+
parts = JSON.parse(row.parts);
|
|
472
|
+
} catch {
|
|
473
|
+
parts = [];
|
|
474
|
+
}
|
|
475
|
+
} else if (row.content !== void 0 && row.content !== null) {
|
|
476
|
+
try {
|
|
477
|
+
const content = JSON.parse(row.content);
|
|
478
|
+
if (typeof content === "string") {
|
|
479
|
+
parts = [{ type: "text", text: content }];
|
|
480
|
+
} else if (Array.isArray(content)) {
|
|
481
|
+
parts = content;
|
|
482
|
+
} else {
|
|
483
|
+
parts = [];
|
|
484
|
+
}
|
|
485
|
+
} catch {
|
|
486
|
+
parts = [{ type: "text", text: row.content }];
|
|
487
|
+
}
|
|
488
|
+
} else {
|
|
489
|
+
parts = [];
|
|
490
|
+
}
|
|
491
|
+
const metadata = row.metadata ? JSON.parse(row.metadata) : {};
|
|
492
|
+
return {
|
|
493
|
+
id: row.message_id,
|
|
494
|
+
role: row.role,
|
|
495
|
+
parts,
|
|
496
|
+
metadata: {
|
|
497
|
+
...metadata,
|
|
498
|
+
createdAt: row.created_at ? new Date(row.created_at) : void 0
|
|
499
|
+
}
|
|
500
|
+
};
|
|
501
|
+
});
|
|
502
|
+
}
|
|
503
|
+
async getConversationSteps(userId, conversationId, options) {
|
|
504
|
+
await this.initialize();
|
|
505
|
+
const stepsTable = `${this.tablePrefix}_steps`;
|
|
506
|
+
const limit = options?.limit && options.limit > 0 ? options.limit : void 0;
|
|
507
|
+
let sql = `SELECT * FROM ${stepsTable} WHERE conversation_id = ? AND user_id = ?`;
|
|
508
|
+
const args = [conversationId, userId];
|
|
509
|
+
if (options?.operationId) {
|
|
510
|
+
sql += " AND operation_id = ?";
|
|
511
|
+
args.push(options.operationId);
|
|
512
|
+
}
|
|
513
|
+
sql += " ORDER BY step_index ASC";
|
|
514
|
+
if (limit !== void 0) {
|
|
515
|
+
sql += " LIMIT ?";
|
|
516
|
+
args.push(limit);
|
|
517
|
+
}
|
|
518
|
+
const result = await this.client.execute({ sql, args });
|
|
519
|
+
const parseJsonField = /* @__PURE__ */ __name((value) => {
|
|
520
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
521
|
+
return void 0;
|
|
522
|
+
}
|
|
523
|
+
try {
|
|
524
|
+
return JSON.parse(value);
|
|
525
|
+
} catch {
|
|
526
|
+
return void 0;
|
|
527
|
+
}
|
|
528
|
+
}, "parseJsonField");
|
|
529
|
+
return result.rows.map((row) => ({
|
|
530
|
+
id: row.id,
|
|
531
|
+
conversationId: row.conversation_id,
|
|
532
|
+
userId: row.user_id,
|
|
533
|
+
agentId: row.agent_id,
|
|
534
|
+
agentName: row.agent_name ?? void 0,
|
|
535
|
+
operationId: row.operation_id ?? void 0,
|
|
536
|
+
stepIndex: typeof row.step_index === "number" ? row.step_index : Number(row.step_index ?? 0),
|
|
537
|
+
type: row.type,
|
|
538
|
+
role: row.role,
|
|
539
|
+
content: row.content ?? void 0,
|
|
540
|
+
arguments: parseJsonField(row.arguments),
|
|
541
|
+
result: parseJsonField(row.result),
|
|
542
|
+
usage: parseJsonField(row.usage),
|
|
543
|
+
subAgentId: row.sub_agent_id ?? void 0,
|
|
544
|
+
subAgentName: row.sub_agent_name ?? void 0,
|
|
545
|
+
createdAt: row.created_at ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
546
|
+
}));
|
|
547
|
+
}
|
|
548
|
+
async clearMessages(userId, conversationId) {
|
|
549
|
+
await this.initialize();
|
|
550
|
+
const messagesTable = `${this.tablePrefix}_messages`;
|
|
551
|
+
const conversationsTable = `${this.tablePrefix}_conversations`;
|
|
552
|
+
const stepsTable = `${this.tablePrefix}_steps`;
|
|
553
|
+
if (conversationId) {
|
|
554
|
+
await this.client.execute({
|
|
555
|
+
sql: `DELETE FROM ${messagesTable} WHERE conversation_id = ? AND user_id = ?`,
|
|
556
|
+
args: [conversationId, userId]
|
|
557
|
+
});
|
|
558
|
+
await this.client.execute({
|
|
559
|
+
sql: `DELETE FROM ${stepsTable} WHERE conversation_id = ? AND user_id = ?`,
|
|
560
|
+
args: [conversationId, userId]
|
|
561
|
+
});
|
|
562
|
+
} else {
|
|
563
|
+
await this.client.execute({
|
|
564
|
+
sql: `DELETE FROM ${messagesTable}
|
|
565
|
+
WHERE conversation_id IN (
|
|
566
|
+
SELECT id FROM ${conversationsTable} WHERE user_id = ?
|
|
567
|
+
)`,
|
|
568
|
+
args: [userId]
|
|
569
|
+
});
|
|
570
|
+
await this.client.execute({
|
|
571
|
+
sql: `DELETE FROM ${stepsTable}
|
|
572
|
+
WHERE conversation_id IN (
|
|
573
|
+
SELECT id FROM ${conversationsTable} WHERE user_id = ?
|
|
574
|
+
)`,
|
|
575
|
+
args: [userId]
|
|
576
|
+
});
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
// ============================================================================
|
|
580
|
+
// Conversation Operations
|
|
581
|
+
// ============================================================================
|
|
582
|
+
async createConversation(input) {
|
|
583
|
+
await this.initialize();
|
|
584
|
+
const conversationsTable = `${this.tablePrefix}_conversations`;
|
|
585
|
+
const existing = await this.getConversation(input.id);
|
|
586
|
+
if (existing) {
|
|
587
|
+
throw new ConversationAlreadyExistsError(input.id);
|
|
588
|
+
}
|
|
589
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
590
|
+
await this.executeWithRetry(async () => {
|
|
591
|
+
await this.client.execute({
|
|
592
|
+
sql: `INSERT INTO ${conversationsTable} (id, resource_id, user_id, title, metadata, created_at, updated_at)
|
|
593
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)`,
|
|
594
|
+
args: [
|
|
595
|
+
input.id,
|
|
596
|
+
input.resourceId,
|
|
597
|
+
input.userId,
|
|
598
|
+
input.title,
|
|
599
|
+
safeStringify(input.metadata || {}),
|
|
600
|
+
now,
|
|
601
|
+
now
|
|
602
|
+
]
|
|
603
|
+
});
|
|
604
|
+
}, "create conversation");
|
|
605
|
+
return {
|
|
606
|
+
id: input.id,
|
|
607
|
+
userId: input.userId,
|
|
608
|
+
resourceId: input.resourceId,
|
|
609
|
+
title: input.title,
|
|
610
|
+
metadata: input.metadata || {},
|
|
611
|
+
createdAt: now,
|
|
612
|
+
updatedAt: now
|
|
613
|
+
};
|
|
614
|
+
}
|
|
615
|
+
async getConversation(id) {
|
|
616
|
+
await this.initialize();
|
|
617
|
+
const conversationsTable = `${this.tablePrefix}_conversations`;
|
|
618
|
+
const result = await this.client.execute({
|
|
619
|
+
sql: `SELECT * FROM ${conversationsTable} WHERE id = ?`,
|
|
620
|
+
args: [id]
|
|
621
|
+
});
|
|
622
|
+
if (result.rows.length === 0) {
|
|
623
|
+
return null;
|
|
624
|
+
}
|
|
625
|
+
const row = result.rows[0];
|
|
626
|
+
return {
|
|
627
|
+
id: row.id,
|
|
628
|
+
userId: row.user_id,
|
|
629
|
+
resourceId: row.resource_id,
|
|
630
|
+
title: row.title,
|
|
631
|
+
metadata: row.metadata ? JSON.parse(row.metadata) : {},
|
|
632
|
+
createdAt: row.created_at,
|
|
633
|
+
updatedAt: row.updated_at
|
|
634
|
+
};
|
|
635
|
+
}
|
|
636
|
+
async getConversations(resourceId) {
|
|
637
|
+
await this.initialize();
|
|
638
|
+
const conversationsTable = `${this.tablePrefix}_conversations`;
|
|
639
|
+
const result = await this.client.execute({
|
|
640
|
+
sql: `SELECT * FROM ${conversationsTable} WHERE resource_id = ? ORDER BY updated_at DESC`,
|
|
641
|
+
args: [resourceId]
|
|
642
|
+
});
|
|
643
|
+
return result.rows.map((row) => ({
|
|
644
|
+
id: row.id,
|
|
645
|
+
userId: row.user_id,
|
|
646
|
+
resourceId: row.resource_id,
|
|
647
|
+
title: row.title,
|
|
648
|
+
metadata: row.metadata ? JSON.parse(row.metadata) : {},
|
|
649
|
+
createdAt: row.created_at,
|
|
650
|
+
updatedAt: row.updated_at
|
|
651
|
+
}));
|
|
652
|
+
}
|
|
653
|
+
async getConversationsByUserId(userId, options) {
|
|
654
|
+
return this.queryConversations({ ...options, userId });
|
|
655
|
+
}
|
|
656
|
+
async queryConversations(options) {
|
|
657
|
+
await this.initialize();
|
|
658
|
+
const conversationsTable = `${this.tablePrefix}_conversations`;
|
|
659
|
+
let sql = `SELECT * FROM ${conversationsTable} WHERE 1=1`;
|
|
660
|
+
const args = [];
|
|
661
|
+
if (options.userId) {
|
|
662
|
+
sql += " AND user_id = ?";
|
|
663
|
+
args.push(options.userId);
|
|
664
|
+
}
|
|
665
|
+
if (options.resourceId) {
|
|
666
|
+
sql += " AND resource_id = ?";
|
|
667
|
+
args.push(options.resourceId);
|
|
668
|
+
}
|
|
669
|
+
const orderBy = options.orderBy || "updated_at";
|
|
670
|
+
const orderDirection = options.orderDirection || "DESC";
|
|
671
|
+
sql += ` ORDER BY ${orderBy} ${orderDirection}`;
|
|
672
|
+
if (options.limit) {
|
|
673
|
+
sql += " LIMIT ?";
|
|
674
|
+
args.push(options.limit);
|
|
675
|
+
}
|
|
676
|
+
if (options.offset) {
|
|
677
|
+
sql += " OFFSET ?";
|
|
678
|
+
args.push(options.offset);
|
|
679
|
+
}
|
|
680
|
+
const result = await this.client.execute({ sql, args });
|
|
681
|
+
return result.rows.map((row) => ({
|
|
682
|
+
id: row.id,
|
|
683
|
+
userId: row.user_id,
|
|
684
|
+
resourceId: row.resource_id,
|
|
685
|
+
title: row.title,
|
|
686
|
+
metadata: row.metadata ? JSON.parse(row.metadata) : {},
|
|
687
|
+
createdAt: row.created_at,
|
|
688
|
+
updatedAt: row.updated_at
|
|
689
|
+
}));
|
|
690
|
+
}
|
|
691
|
+
async updateConversation(id, updates) {
|
|
692
|
+
await this.initialize();
|
|
693
|
+
const conversationsTable = `${this.tablePrefix}_conversations`;
|
|
694
|
+
const conversation = await this.getConversation(id);
|
|
695
|
+
if (!conversation) {
|
|
696
|
+
throw new ConversationNotFoundError(id);
|
|
697
|
+
}
|
|
698
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
699
|
+
const fieldsToUpdate = ["updated_at = ?"];
|
|
700
|
+
const args = [now];
|
|
701
|
+
if (updates.title !== void 0) {
|
|
702
|
+
fieldsToUpdate.push("title = ?");
|
|
703
|
+
args.push(updates.title);
|
|
704
|
+
}
|
|
705
|
+
if (updates.resourceId !== void 0) {
|
|
706
|
+
fieldsToUpdate.push("resource_id = ?");
|
|
707
|
+
args.push(updates.resourceId);
|
|
708
|
+
}
|
|
709
|
+
if (updates.metadata !== void 0) {
|
|
710
|
+
fieldsToUpdate.push("metadata = ?");
|
|
711
|
+
args.push(safeStringify(updates.metadata));
|
|
712
|
+
}
|
|
713
|
+
args.push(id);
|
|
714
|
+
await this.client.execute({
|
|
715
|
+
sql: `UPDATE ${conversationsTable} SET ${fieldsToUpdate.join(", ")} WHERE id = ?`,
|
|
716
|
+
args
|
|
717
|
+
});
|
|
718
|
+
const updated = await this.getConversation(id);
|
|
719
|
+
if (!updated) {
|
|
720
|
+
throw new Error(`Conversation not found after update: ${id}`);
|
|
721
|
+
}
|
|
722
|
+
return updated;
|
|
723
|
+
}
|
|
724
|
+
async deleteConversation(id) {
|
|
725
|
+
await this.initialize();
|
|
726
|
+
const conversationsTable = `${this.tablePrefix}_conversations`;
|
|
727
|
+
await this.client.execute({
|
|
728
|
+
sql: `DELETE FROM ${conversationsTable} WHERE id = ?`,
|
|
729
|
+
args: [id]
|
|
730
|
+
});
|
|
731
|
+
}
|
|
732
|
+
// ============================================================================
|
|
733
|
+
// Working Memory Operations
|
|
734
|
+
// ============================================================================
|
|
735
|
+
async getWorkingMemory(params) {
|
|
736
|
+
await this.initialize();
|
|
737
|
+
if (params.scope === "conversation" && params.conversationId) {
|
|
738
|
+
const conversation = await this.getConversation(params.conversationId);
|
|
739
|
+
return conversation?.metadata?.workingMemory || null;
|
|
740
|
+
}
|
|
741
|
+
if (params.scope === "user" && params.userId) {
|
|
742
|
+
const usersTable = `${this.tablePrefix}_users`;
|
|
743
|
+
const result = await this.client.execute({
|
|
744
|
+
sql: `SELECT metadata FROM ${usersTable} WHERE id = ?`,
|
|
745
|
+
args: [params.userId]
|
|
746
|
+
});
|
|
747
|
+
if (result.rows.length > 0) {
|
|
748
|
+
const metadata = result.rows[0].metadata ? JSON.parse(result.rows[0].metadata) : {};
|
|
749
|
+
return metadata.workingMemory || null;
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
return null;
|
|
753
|
+
}
|
|
754
|
+
async setWorkingMemory(params) {
|
|
755
|
+
await this.initialize();
|
|
756
|
+
if (params.scope === "conversation" && params.conversationId) {
|
|
757
|
+
const conversation = await this.getConversation(params.conversationId);
|
|
758
|
+
if (!conversation) {
|
|
759
|
+
throw new ConversationNotFoundError(params.conversationId);
|
|
760
|
+
}
|
|
761
|
+
const metadata = conversation.metadata || {};
|
|
762
|
+
metadata.workingMemory = params.content;
|
|
763
|
+
await this.updateConversation(params.conversationId, { metadata });
|
|
764
|
+
}
|
|
765
|
+
if (params.scope === "user" && params.userId) {
|
|
766
|
+
const usersTable = `${this.tablePrefix}_users`;
|
|
767
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
768
|
+
const result = await this.client.execute({
|
|
769
|
+
sql: `SELECT metadata FROM ${usersTable} WHERE id = ?`,
|
|
770
|
+
args: [params.userId]
|
|
771
|
+
});
|
|
772
|
+
if (result.rows.length > 0) {
|
|
773
|
+
const metadata = result.rows[0].metadata ? JSON.parse(result.rows[0].metadata) : {};
|
|
774
|
+
metadata.workingMemory = params.content;
|
|
775
|
+
await this.client.execute({
|
|
776
|
+
sql: `UPDATE ${usersTable} SET metadata = ?, updated_at = ? WHERE id = ?`,
|
|
777
|
+
args: [safeStringify(metadata), now, params.userId]
|
|
778
|
+
});
|
|
779
|
+
} else {
|
|
780
|
+
await this.client.execute({
|
|
781
|
+
sql: `INSERT INTO ${usersTable} (id, metadata, created_at, updated_at) VALUES (?, ?, ?, ?)`,
|
|
782
|
+
args: [params.userId, safeStringify({ workingMemory: params.content }), now, now]
|
|
783
|
+
});
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
async deleteWorkingMemory(params) {
|
|
788
|
+
await this.initialize();
|
|
789
|
+
if (params.scope === "conversation" && params.conversationId) {
|
|
790
|
+
const conversation = await this.getConversation(params.conversationId);
|
|
791
|
+
if (conversation?.metadata?.workingMemory) {
|
|
792
|
+
const metadata = { ...conversation.metadata };
|
|
793
|
+
delete metadata.workingMemory;
|
|
794
|
+
await this.updateConversation(params.conversationId, { metadata });
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
if (params.scope === "user" && params.userId) {
|
|
798
|
+
const usersTable = `${this.tablePrefix}_users`;
|
|
799
|
+
const result = await this.client.execute({
|
|
800
|
+
sql: `SELECT metadata FROM ${usersTable} WHERE id = ?`,
|
|
801
|
+
args: [params.userId]
|
|
802
|
+
});
|
|
803
|
+
if (result.rows.length > 0 && result.rows[0].metadata) {
|
|
804
|
+
const metadata = JSON.parse(result.rows[0].metadata);
|
|
805
|
+
if (metadata.workingMemory) {
|
|
806
|
+
delete metadata.workingMemory;
|
|
807
|
+
await this.client.execute({
|
|
808
|
+
sql: `UPDATE ${usersTable} SET metadata = ?, updated_at = ? WHERE id = ?`,
|
|
809
|
+
args: [safeStringify(metadata), (/* @__PURE__ */ new Date()).toISOString(), params.userId]
|
|
810
|
+
});
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
// ============================================================================
|
|
816
|
+
// Workflow State Operations
|
|
817
|
+
// ============================================================================
|
|
818
|
+
async getWorkflowState(executionId) {
|
|
819
|
+
await this.initialize();
|
|
820
|
+
const workflowStatesTable = `${this.tablePrefix}_workflow_states`;
|
|
821
|
+
const result = await this.client.execute({
|
|
822
|
+
sql: `SELECT * FROM ${workflowStatesTable} WHERE id = ?`,
|
|
823
|
+
args: [executionId]
|
|
824
|
+
});
|
|
825
|
+
if (result.rows.length === 0) {
|
|
826
|
+
return null;
|
|
827
|
+
}
|
|
828
|
+
const row = result.rows[0];
|
|
829
|
+
return {
|
|
830
|
+
id: row.id,
|
|
831
|
+
workflowId: row.workflow_id,
|
|
832
|
+
workflowName: row.workflow_name,
|
|
833
|
+
status: row.status,
|
|
834
|
+
suspension: row.suspension ? JSON.parse(row.suspension) : void 0,
|
|
835
|
+
events: row.events ? JSON.parse(row.events) : void 0,
|
|
836
|
+
output: row.output ? JSON.parse(row.output) : void 0,
|
|
837
|
+
cancellation: row.cancellation ? JSON.parse(row.cancellation) : void 0,
|
|
838
|
+
userId: row.user_id,
|
|
839
|
+
conversationId: row.conversation_id,
|
|
840
|
+
metadata: row.metadata ? JSON.parse(row.metadata) : void 0,
|
|
841
|
+
createdAt: new Date(row.created_at),
|
|
842
|
+
updatedAt: new Date(row.updated_at)
|
|
843
|
+
};
|
|
844
|
+
}
|
|
845
|
+
async queryWorkflowRuns(query) {
|
|
846
|
+
await this.initialize();
|
|
847
|
+
const workflowStatesTable = `${this.tablePrefix}_workflow_states`;
|
|
848
|
+
const conditions = [];
|
|
849
|
+
const args = [];
|
|
850
|
+
if (query.workflowId) {
|
|
851
|
+
conditions.push("workflow_id = ?");
|
|
852
|
+
args.push(query.workflowId);
|
|
853
|
+
}
|
|
854
|
+
if (query.status) {
|
|
855
|
+
conditions.push("status = ?");
|
|
856
|
+
args.push(query.status);
|
|
857
|
+
}
|
|
858
|
+
if (query.from) {
|
|
859
|
+
conditions.push("created_at >= ?");
|
|
860
|
+
args.push(query.from.toISOString());
|
|
861
|
+
}
|
|
862
|
+
if (query.to) {
|
|
863
|
+
conditions.push("created_at <= ?");
|
|
864
|
+
args.push(query.to.toISOString());
|
|
865
|
+
}
|
|
866
|
+
let sql = `SELECT * FROM ${workflowStatesTable}`;
|
|
867
|
+
if (conditions.length > 0) {
|
|
868
|
+
sql += ` WHERE ${conditions.join(" AND ")}`;
|
|
869
|
+
}
|
|
870
|
+
sql += " ORDER BY created_at DESC";
|
|
871
|
+
if (query.limit !== void 0) {
|
|
872
|
+
sql += " LIMIT ?";
|
|
873
|
+
args.push(query.limit);
|
|
874
|
+
}
|
|
875
|
+
if (query.offset !== void 0) {
|
|
876
|
+
sql += " OFFSET ?";
|
|
877
|
+
args.push(query.offset);
|
|
878
|
+
}
|
|
879
|
+
const result = await this.client.execute({
|
|
880
|
+
sql,
|
|
881
|
+
args
|
|
882
|
+
});
|
|
883
|
+
return result.rows.map((row) => ({
|
|
884
|
+
id: row.id,
|
|
885
|
+
workflowId: row.workflow_id,
|
|
886
|
+
workflowName: row.workflow_name,
|
|
887
|
+
status: row.status,
|
|
888
|
+
suspension: row.suspension ? JSON.parse(row.suspension) : void 0,
|
|
889
|
+
events: row.events ? JSON.parse(row.events) : void 0,
|
|
890
|
+
output: row.output ? JSON.parse(row.output) : void 0,
|
|
891
|
+
cancellation: row.cancellation ? JSON.parse(row.cancellation) : void 0,
|
|
892
|
+
userId: row.user_id,
|
|
893
|
+
conversationId: row.conversation_id,
|
|
894
|
+
metadata: row.metadata ? JSON.parse(row.metadata) : void 0,
|
|
895
|
+
createdAt: new Date(row.created_at),
|
|
896
|
+
updatedAt: new Date(row.updated_at)
|
|
897
|
+
}));
|
|
898
|
+
}
|
|
899
|
+
async setWorkflowState(executionId, state) {
|
|
900
|
+
await this.initialize();
|
|
901
|
+
const workflowStatesTable = `${this.tablePrefix}_workflow_states`;
|
|
902
|
+
await this.client.execute({
|
|
903
|
+
sql: `INSERT OR REPLACE INTO ${workflowStatesTable}
|
|
904
|
+
(id, workflow_id, workflow_name, status, suspension, events, output, cancellation, user_id, conversation_id, metadata, created_at, updated_at)
|
|
905
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
906
|
+
args: [
|
|
907
|
+
executionId,
|
|
908
|
+
state.workflowId,
|
|
909
|
+
state.workflowName,
|
|
910
|
+
state.status,
|
|
911
|
+
state.suspension ? safeStringify(state.suspension) : null,
|
|
912
|
+
state.events ? safeStringify(state.events) : null,
|
|
913
|
+
state.output ? safeStringify(state.output) : null,
|
|
914
|
+
state.cancellation ? safeStringify(state.cancellation) : null,
|
|
915
|
+
state.userId || null,
|
|
916
|
+
state.conversationId || null,
|
|
917
|
+
state.metadata ? safeStringify(state.metadata) : null,
|
|
918
|
+
state.createdAt.toISOString(),
|
|
919
|
+
state.updatedAt.toISOString()
|
|
920
|
+
]
|
|
921
|
+
});
|
|
922
|
+
}
|
|
923
|
+
async updateWorkflowState(executionId, updates) {
|
|
924
|
+
await this.initialize();
|
|
925
|
+
const existing = await this.getWorkflowState(executionId);
|
|
926
|
+
if (!existing) {
|
|
927
|
+
throw new Error(`Workflow state ${executionId} not found`);
|
|
928
|
+
}
|
|
929
|
+
const updated = {
|
|
930
|
+
...existing,
|
|
931
|
+
...updates,
|
|
932
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
933
|
+
};
|
|
934
|
+
await this.setWorkflowState(executionId, updated);
|
|
935
|
+
}
|
|
936
|
+
async getSuspendedWorkflowStates(workflowId) {
|
|
937
|
+
await this.initialize();
|
|
938
|
+
const workflowStatesTable = `${this.tablePrefix}_workflow_states`;
|
|
939
|
+
const result = await this.client.execute({
|
|
940
|
+
sql: `SELECT * FROM ${workflowStatesTable} WHERE workflow_id = ? AND status = 'suspended' ORDER BY created_at DESC`,
|
|
941
|
+
args: [workflowId]
|
|
942
|
+
});
|
|
943
|
+
return result.rows.map((row) => ({
|
|
944
|
+
id: row.id,
|
|
945
|
+
workflowId: row.workflow_id,
|
|
946
|
+
workflowName: row.workflow_name,
|
|
947
|
+
status: "suspended",
|
|
948
|
+
suspension: row.suspension ? JSON.parse(row.suspension) : void 0,
|
|
949
|
+
events: row.events ? JSON.parse(row.events) : void 0,
|
|
950
|
+
output: row.output ? JSON.parse(row.output) : void 0,
|
|
951
|
+
cancellation: row.cancellation ? JSON.parse(row.cancellation) : void 0,
|
|
952
|
+
userId: row.user_id,
|
|
953
|
+
conversationId: row.conversation_id,
|
|
954
|
+
metadata: row.metadata ? JSON.parse(row.metadata) : void 0,
|
|
955
|
+
createdAt: new Date(row.created_at),
|
|
956
|
+
updatedAt: new Date(row.updated_at)
|
|
957
|
+
}));
|
|
958
|
+
}
|
|
959
|
+
async close() {
|
|
960
|
+
this.logger.debug("Closing LibSQL Memory adapter");
|
|
961
|
+
}
|
|
962
|
+
};
|
|
963
|
+
|
|
964
|
+
// src/memory-v2-adapter-edge.ts
|
|
965
|
+
var LibSQLMemoryAdapterEdge = class extends LibSQLMemoryCore {
|
|
966
|
+
static {
|
|
967
|
+
__name(this, "LibSQLMemoryAdapterEdge");
|
|
968
|
+
}
|
|
969
|
+
constructor(options) {
|
|
970
|
+
if (!options.url) {
|
|
971
|
+
throw new Error("LibSQLMemoryAdapterEdge requires a url option");
|
|
972
|
+
}
|
|
973
|
+
if (options.url.startsWith("file:") || options.url === ":memory:" || !options.url.startsWith("libsql://")) {
|
|
974
|
+
throw new Error(
|
|
975
|
+
"LibSQLMemoryAdapterEdge only supports remote Turso URLs (libsql://). File-based databases are not supported in edge environments. Use LibSQLMemoryAdapter from '@voltagent/libsql' for Node.js environments."
|
|
976
|
+
);
|
|
977
|
+
}
|
|
978
|
+
if (!options.authToken) {
|
|
979
|
+
throw new Error("LibSQLMemoryAdapterEdge requires an authToken for remote connections");
|
|
980
|
+
}
|
|
981
|
+
const logger = options.logger || AgentRegistry.getInstance().getGlobalLogger() || createPinoLogger({ name: "libsql-memory-edge", level: options.debug ? "debug" : "info" });
|
|
982
|
+
const client = createClient({
|
|
983
|
+
url: options.url,
|
|
984
|
+
authToken: options.authToken
|
|
985
|
+
});
|
|
986
|
+
super(client, options.url, options, logger);
|
|
987
|
+
}
|
|
988
|
+
};
|
|
989
|
+
|
|
990
|
+
// src/observability-adapter-edge.ts
|
|
991
|
+
import { createClient as createClient2 } from "@libsql/client/web";
|
|
992
|
+
import { createPinoLogger as createPinoLogger2 } from "@voltagent/logger";
|
|
993
|
+
|
|
994
|
+
// src/observability-core.ts
|
|
995
|
+
import { safeStringify as safeStringify2 } from "@voltagent/internal/utils";
|
|
996
|
+
var LibSQLObservabilityCore = class {
|
|
997
|
+
static {
|
|
998
|
+
__name(this, "LibSQLObservabilityCore");
|
|
999
|
+
}
|
|
1000
|
+
client;
|
|
1001
|
+
tablePrefix;
|
|
1002
|
+
debug;
|
|
1003
|
+
logger;
|
|
1004
|
+
initialized;
|
|
1005
|
+
maxSpansPerQuery;
|
|
1006
|
+
constructor(client, options, logger) {
|
|
1007
|
+
this.client = client;
|
|
1008
|
+
this.logger = logger;
|
|
1009
|
+
this.tablePrefix = options.tablePrefix || "observability";
|
|
1010
|
+
this.debug = options.debug || false;
|
|
1011
|
+
this.maxSpansPerQuery = options.maxSpansPerQuery || 1e3;
|
|
1012
|
+
this.debugLog("LibSQL observability adapter core initialized", {
|
|
1013
|
+
tablePrefix: this.tablePrefix,
|
|
1014
|
+
debug: this.debug,
|
|
1015
|
+
maxSpansPerQuery: this.maxSpansPerQuery
|
|
1016
|
+
});
|
|
1017
|
+
this.initialized = this.initializeDatabase();
|
|
1018
|
+
}
|
|
1019
|
+
debugLog(message, data) {
|
|
1020
|
+
if (this.debug) {
|
|
1021
|
+
this.logger.debug(`${message}`, data || "");
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
async initializeDatabase() {
|
|
1025
|
+
try {
|
|
1026
|
+
await this.client.execute(`
|
|
1027
|
+
CREATE TABLE IF NOT EXISTS ${this.tablePrefix}_spans (
|
|
1028
|
+
span_id TEXT PRIMARY KEY,
|
|
1029
|
+
trace_id TEXT NOT NULL,
|
|
1030
|
+
parent_span_id TEXT,
|
|
1031
|
+
entity_id TEXT,
|
|
1032
|
+
entity_type TEXT,
|
|
1033
|
+
name TEXT NOT NULL,
|
|
1034
|
+
kind INTEGER DEFAULT 0,
|
|
1035
|
+
start_time TEXT NOT NULL,
|
|
1036
|
+
end_time TEXT,
|
|
1037
|
+
duration REAL,
|
|
1038
|
+
status_code INTEGER DEFAULT 0,
|
|
1039
|
+
status_message TEXT,
|
|
1040
|
+
attributes TEXT,
|
|
1041
|
+
events TEXT,
|
|
1042
|
+
links TEXT,
|
|
1043
|
+
resource TEXT,
|
|
1044
|
+
instrumentation_scope TEXT,
|
|
1045
|
+
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
|
|
1046
|
+
updated_at TEXT DEFAULT CURRENT_TIMESTAMP
|
|
1047
|
+
)
|
|
1048
|
+
`);
|
|
1049
|
+
await this.client.execute(`
|
|
1050
|
+
CREATE INDEX IF NOT EXISTS idx_${this.tablePrefix}_spans_trace_id
|
|
1051
|
+
ON ${this.tablePrefix}_spans(trace_id)
|
|
1052
|
+
`);
|
|
1053
|
+
await this.client.execute(`
|
|
1054
|
+
CREATE INDEX IF NOT EXISTS idx_${this.tablePrefix}_spans_parent_span_id
|
|
1055
|
+
ON ${this.tablePrefix}_spans(parent_span_id)
|
|
1056
|
+
`);
|
|
1057
|
+
await this.client.execute(`
|
|
1058
|
+
CREATE INDEX IF NOT EXISTS idx_${this.tablePrefix}_spans_start_time
|
|
1059
|
+
ON ${this.tablePrefix}_spans(start_time)
|
|
1060
|
+
`);
|
|
1061
|
+
await this.client.execute(`
|
|
1062
|
+
CREATE INDEX IF NOT EXISTS idx_${this.tablePrefix}_spans_name
|
|
1063
|
+
ON ${this.tablePrefix}_spans(name)
|
|
1064
|
+
`);
|
|
1065
|
+
await this.client.execute(`
|
|
1066
|
+
CREATE INDEX IF NOT EXISTS idx_${this.tablePrefix}_spans_entity_id
|
|
1067
|
+
ON ${this.tablePrefix}_spans(entity_id)
|
|
1068
|
+
`);
|
|
1069
|
+
await this.client.execute(`
|
|
1070
|
+
CREATE INDEX IF NOT EXISTS idx_${this.tablePrefix}_spans_entity_type
|
|
1071
|
+
ON ${this.tablePrefix}_spans(entity_type)
|
|
1072
|
+
`);
|
|
1073
|
+
await this.client.execute(`
|
|
1074
|
+
CREATE TABLE IF NOT EXISTS ${this.tablePrefix}_traces (
|
|
1075
|
+
trace_id TEXT PRIMARY KEY,
|
|
1076
|
+
root_span_id TEXT,
|
|
1077
|
+
entity_id TEXT,
|
|
1078
|
+
entity_type TEXT,
|
|
1079
|
+
start_time TEXT NOT NULL,
|
|
1080
|
+
end_time TEXT,
|
|
1081
|
+
span_count INTEGER DEFAULT 1,
|
|
1082
|
+
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
|
|
1083
|
+
updated_at TEXT DEFAULT CURRENT_TIMESTAMP
|
|
1084
|
+
)
|
|
1085
|
+
`);
|
|
1086
|
+
await this.client.execute(`
|
|
1087
|
+
CREATE INDEX IF NOT EXISTS idx_${this.tablePrefix}_traces_start_time
|
|
1088
|
+
ON ${this.tablePrefix}_traces(start_time DESC)
|
|
1089
|
+
`);
|
|
1090
|
+
await this.client.execute(`
|
|
1091
|
+
CREATE INDEX IF NOT EXISTS idx_${this.tablePrefix}_traces_entity_id
|
|
1092
|
+
ON ${this.tablePrefix}_traces(entity_id)
|
|
1093
|
+
`);
|
|
1094
|
+
await this.client.execute(`
|
|
1095
|
+
CREATE INDEX IF NOT EXISTS idx_${this.tablePrefix}_traces_entity_type
|
|
1096
|
+
ON ${this.tablePrefix}_traces(entity_type)
|
|
1097
|
+
`);
|
|
1098
|
+
await this.client.execute(`
|
|
1099
|
+
CREATE TABLE IF NOT EXISTS ${this.tablePrefix}_logs (
|
|
1100
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
1101
|
+
timestamp TEXT NOT NULL,
|
|
1102
|
+
trace_id TEXT,
|
|
1103
|
+
span_id TEXT,
|
|
1104
|
+
trace_flags INTEGER,
|
|
1105
|
+
severity_number INTEGER,
|
|
1106
|
+
severity_text TEXT,
|
|
1107
|
+
body TEXT NOT NULL,
|
|
1108
|
+
attributes TEXT,
|
|
1109
|
+
resource TEXT,
|
|
1110
|
+
instrumentation_scope TEXT,
|
|
1111
|
+
created_at TEXT DEFAULT CURRENT_TIMESTAMP
|
|
1112
|
+
)
|
|
1113
|
+
`);
|
|
1114
|
+
await this.client.execute(`
|
|
1115
|
+
CREATE INDEX IF NOT EXISTS idx_${this.tablePrefix}_logs_trace_id
|
|
1116
|
+
ON ${this.tablePrefix}_logs(trace_id)
|
|
1117
|
+
`);
|
|
1118
|
+
await this.client.execute(`
|
|
1119
|
+
CREATE INDEX IF NOT EXISTS idx_${this.tablePrefix}_logs_span_id
|
|
1120
|
+
ON ${this.tablePrefix}_logs(span_id)
|
|
1121
|
+
`);
|
|
1122
|
+
await this.client.execute(`
|
|
1123
|
+
CREATE INDEX IF NOT EXISTS idx_${this.tablePrefix}_logs_timestamp
|
|
1124
|
+
ON ${this.tablePrefix}_logs(timestamp DESC)
|
|
1125
|
+
`);
|
|
1126
|
+
await this.client.execute(`
|
|
1127
|
+
CREATE INDEX IF NOT EXISTS idx_${this.tablePrefix}_logs_severity
|
|
1128
|
+
ON ${this.tablePrefix}_logs(severity_number)
|
|
1129
|
+
`);
|
|
1130
|
+
this.debugLog("Database tables initialized successfully");
|
|
1131
|
+
} catch (error) {
|
|
1132
|
+
this.logger.error("Failed to initialize database tables", { error });
|
|
1133
|
+
throw error;
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
async ensureInitialized() {
|
|
1137
|
+
await this.initialized;
|
|
1138
|
+
}
|
|
1139
|
+
async addSpan(span) {
|
|
1140
|
+
await this.ensureInitialized();
|
|
1141
|
+
try {
|
|
1142
|
+
const entityId = span.attributes?.["entity.id"] || null;
|
|
1143
|
+
const entityType = span.attributes?.["entity.type"] || null;
|
|
1144
|
+
await this.client.batch([
|
|
1145
|
+
{
|
|
1146
|
+
sql: `
|
|
1147
|
+
INSERT INTO ${this.tablePrefix}_spans (
|
|
1148
|
+
span_id, trace_id, parent_span_id, entity_id, entity_type, name, kind,
|
|
1149
|
+
start_time, end_time, duration,
|
|
1150
|
+
status_code, status_message,
|
|
1151
|
+
attributes, events, links,
|
|
1152
|
+
resource, instrumentation_scope
|
|
1153
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
1154
|
+
`,
|
|
1155
|
+
args: [
|
|
1156
|
+
span.spanId,
|
|
1157
|
+
span.traceId,
|
|
1158
|
+
span.parentSpanId || null,
|
|
1159
|
+
entityId,
|
|
1160
|
+
entityType,
|
|
1161
|
+
span.name,
|
|
1162
|
+
span.kind,
|
|
1163
|
+
span.startTime,
|
|
1164
|
+
span.endTime || null,
|
|
1165
|
+
span.duration || null,
|
|
1166
|
+
span.status.code,
|
|
1167
|
+
span.status.message || null,
|
|
1168
|
+
safeStringify2(span.attributes),
|
|
1169
|
+
safeStringify2(span.events),
|
|
1170
|
+
span.links ? safeStringify2(span.links) : null,
|
|
1171
|
+
span.resource ? safeStringify2(span.resource) : null,
|
|
1172
|
+
span.instrumentationScope ? safeStringify2(span.instrumentationScope) : null
|
|
1173
|
+
]
|
|
1174
|
+
},
|
|
1175
|
+
{
|
|
1176
|
+
sql: `
|
|
1177
|
+
INSERT INTO ${this.tablePrefix}_traces (
|
|
1178
|
+
trace_id, root_span_id, entity_id, entity_type, start_time, end_time, span_count
|
|
1179
|
+
) VALUES (?, ?, ?, ?, ?, ?, 1)
|
|
1180
|
+
ON CONFLICT(trace_id) DO UPDATE SET
|
|
1181
|
+
span_count = span_count + 1,
|
|
1182
|
+
entity_id = COALESCE(excluded.entity_id, entity_id),
|
|
1183
|
+
entity_type = COALESCE(excluded.entity_type, entity_type),
|
|
1184
|
+
start_time = MIN(start_time, excluded.start_time),
|
|
1185
|
+
end_time = MAX(COALESCE(end_time, excluded.end_time), excluded.end_time),
|
|
1186
|
+
updated_at = CURRENT_TIMESTAMP
|
|
1187
|
+
`,
|
|
1188
|
+
args: [
|
|
1189
|
+
span.traceId,
|
|
1190
|
+
span.parentSpanId ? null : span.spanId,
|
|
1191
|
+
entityId,
|
|
1192
|
+
entityType,
|
|
1193
|
+
span.startTime,
|
|
1194
|
+
span.endTime || null
|
|
1195
|
+
]
|
|
1196
|
+
}
|
|
1197
|
+
]);
|
|
1198
|
+
this.debugLog("Span added successfully", {
|
|
1199
|
+
spanId: span.spanId,
|
|
1200
|
+
traceId: span.traceId
|
|
1201
|
+
});
|
|
1202
|
+
} catch (error) {
|
|
1203
|
+
this.logger.error("Failed to add span", { error, span });
|
|
1204
|
+
throw error;
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
1207
|
+
async updateSpan(spanId, updates) {
|
|
1208
|
+
await this.ensureInitialized();
|
|
1209
|
+
try {
|
|
1210
|
+
const setClauses = [];
|
|
1211
|
+
const args = [];
|
|
1212
|
+
if (updates.endTime !== void 0) {
|
|
1213
|
+
setClauses.push("end_time = ?");
|
|
1214
|
+
args.push(updates.endTime);
|
|
1215
|
+
}
|
|
1216
|
+
if (updates.duration !== void 0) {
|
|
1217
|
+
setClauses.push("duration = ?");
|
|
1218
|
+
args.push(updates.duration);
|
|
1219
|
+
}
|
|
1220
|
+
if (updates.status !== void 0) {
|
|
1221
|
+
setClauses.push("status_code = ?, status_message = ?");
|
|
1222
|
+
args.push(updates.status.code, updates.status.message || null);
|
|
1223
|
+
}
|
|
1224
|
+
if (updates.attributes !== void 0) {
|
|
1225
|
+
setClauses.push("attributes = ?");
|
|
1226
|
+
args.push(safeStringify2(updates.attributes));
|
|
1227
|
+
}
|
|
1228
|
+
if (updates.events !== void 0) {
|
|
1229
|
+
setClauses.push("events = ?");
|
|
1230
|
+
args.push(safeStringify2(updates.events));
|
|
1231
|
+
}
|
|
1232
|
+
if (updates.links !== void 0) {
|
|
1233
|
+
setClauses.push("links = ?");
|
|
1234
|
+
args.push(safeStringify2(updates.links));
|
|
1235
|
+
}
|
|
1236
|
+
if (setClauses.length === 0) {
|
|
1237
|
+
return;
|
|
1238
|
+
}
|
|
1239
|
+
setClauses.push("updated_at = CURRENT_TIMESTAMP");
|
|
1240
|
+
args.push(spanId);
|
|
1241
|
+
await this.client.execute({
|
|
1242
|
+
sql: `
|
|
1243
|
+
UPDATE ${this.tablePrefix}_spans
|
|
1244
|
+
SET ${setClauses.join(", ")}
|
|
1245
|
+
WHERE span_id = ?
|
|
1246
|
+
`,
|
|
1247
|
+
args
|
|
1248
|
+
});
|
|
1249
|
+
if (updates.endTime) {
|
|
1250
|
+
const span = await this.getSpan(spanId);
|
|
1251
|
+
if (span) {
|
|
1252
|
+
await this.client.execute({
|
|
1253
|
+
sql: `
|
|
1254
|
+
UPDATE ${this.tablePrefix}_traces
|
|
1255
|
+
SET end_time = MAX(COALESCE(end_time, ?), ?),
|
|
1256
|
+
updated_at = CURRENT_TIMESTAMP
|
|
1257
|
+
WHERE trace_id = ?
|
|
1258
|
+
`,
|
|
1259
|
+
args: [updates.endTime, updates.endTime, span.traceId]
|
|
1260
|
+
});
|
|
1261
|
+
}
|
|
1262
|
+
}
|
|
1263
|
+
this.debugLog("Span updated successfully", { spanId, updates });
|
|
1264
|
+
} catch (error) {
|
|
1265
|
+
this.logger.error("Failed to update span", { error, spanId, updates });
|
|
1266
|
+
throw error;
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
async getSpan(spanId) {
|
|
1270
|
+
await this.ensureInitialized();
|
|
1271
|
+
try {
|
|
1272
|
+
const result = await this.client.execute({
|
|
1273
|
+
sql: `SELECT * FROM ${this.tablePrefix}_spans WHERE span_id = ?`,
|
|
1274
|
+
args: [spanId]
|
|
1275
|
+
});
|
|
1276
|
+
if (result.rows.length === 0) {
|
|
1277
|
+
return null;
|
|
1278
|
+
}
|
|
1279
|
+
return this.rowToSpan(result.rows[0]);
|
|
1280
|
+
} catch (error) {
|
|
1281
|
+
this.logger.error("Failed to get span", { error, spanId });
|
|
1282
|
+
throw error;
|
|
1283
|
+
}
|
|
1284
|
+
}
|
|
1285
|
+
async getTrace(traceId) {
|
|
1286
|
+
await this.ensureInitialized();
|
|
1287
|
+
try {
|
|
1288
|
+
const result = await this.client.execute({
|
|
1289
|
+
sql: `
|
|
1290
|
+
SELECT * FROM ${this.tablePrefix}_spans
|
|
1291
|
+
WHERE trace_id = ?
|
|
1292
|
+
ORDER BY start_time ASC
|
|
1293
|
+
LIMIT ?
|
|
1294
|
+
`,
|
|
1295
|
+
args: [traceId, this.maxSpansPerQuery]
|
|
1296
|
+
});
|
|
1297
|
+
return result.rows.map((row) => this.rowToSpan(row));
|
|
1298
|
+
} catch (error) {
|
|
1299
|
+
this.logger.error("Failed to get trace", { error, traceId });
|
|
1300
|
+
throw error;
|
|
1301
|
+
}
|
|
1302
|
+
}
|
|
1303
|
+
async listTraces(limit = 100, offset = 0, filter) {
|
|
1304
|
+
await this.ensureInitialized();
|
|
1305
|
+
try {
|
|
1306
|
+
let sql;
|
|
1307
|
+
let args = [];
|
|
1308
|
+
const conditions = [];
|
|
1309
|
+
if (filter?.entityId) {
|
|
1310
|
+
conditions.push("entity_id = ?");
|
|
1311
|
+
args.push(filter.entityId);
|
|
1312
|
+
}
|
|
1313
|
+
if (filter?.entityType) {
|
|
1314
|
+
conditions.push("entity_type = ?");
|
|
1315
|
+
args.push(filter.entityType);
|
|
1316
|
+
}
|
|
1317
|
+
if (conditions.length > 0) {
|
|
1318
|
+
sql = `
|
|
1319
|
+
SELECT trace_id FROM ${this.tablePrefix}_traces
|
|
1320
|
+
WHERE ${conditions.join(" AND ")}
|
|
1321
|
+
ORDER BY start_time DESC
|
|
1322
|
+
LIMIT ? OFFSET ?
|
|
1323
|
+
`;
|
|
1324
|
+
args.push(limit, offset);
|
|
1325
|
+
} else {
|
|
1326
|
+
sql = `
|
|
1327
|
+
SELECT trace_id FROM ${this.tablePrefix}_traces
|
|
1328
|
+
ORDER BY start_time DESC
|
|
1329
|
+
LIMIT ? OFFSET ?
|
|
1330
|
+
`;
|
|
1331
|
+
args = [limit, offset];
|
|
1332
|
+
}
|
|
1333
|
+
const result = await this.client.execute({ sql, args });
|
|
1334
|
+
return result.rows.map((row) => row.trace_id);
|
|
1335
|
+
} catch (error) {
|
|
1336
|
+
this.logger.error("Failed to list traces", { error, limit, offset, filter });
|
|
1337
|
+
throw error;
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
async deleteOldSpans(beforeTimestamp) {
|
|
1341
|
+
await this.ensureInitialized();
|
|
1342
|
+
try {
|
|
1343
|
+
const beforeDate = new Date(beforeTimestamp).toISOString();
|
|
1344
|
+
const tracesResult = await this.client.execute({
|
|
1345
|
+
sql: `SELECT DISTINCT trace_id FROM ${this.tablePrefix}_spans WHERE start_time < ?`,
|
|
1346
|
+
args: [beforeDate]
|
|
1347
|
+
});
|
|
1348
|
+
const affectedTraceIds = tracesResult.rows.map((row) => row.trace_id);
|
|
1349
|
+
const deleteResult = await this.client.execute({
|
|
1350
|
+
sql: `DELETE FROM ${this.tablePrefix}_spans WHERE start_time < ?`,
|
|
1351
|
+
args: [beforeDate]
|
|
1352
|
+
});
|
|
1353
|
+
if (affectedTraceIds.length > 0) {
|
|
1354
|
+
for (const traceId of affectedTraceIds) {
|
|
1355
|
+
const countResult = await this.client.execute({
|
|
1356
|
+
sql: `SELECT COUNT(*) as count FROM ${this.tablePrefix}_spans WHERE trace_id = ?`,
|
|
1357
|
+
args: [traceId]
|
|
1358
|
+
});
|
|
1359
|
+
const count = countResult.rows[0].count;
|
|
1360
|
+
if (count === 0) {
|
|
1361
|
+
await this.client.execute({
|
|
1362
|
+
sql: `DELETE FROM ${this.tablePrefix}_traces WHERE trace_id = ?`,
|
|
1363
|
+
args: [traceId]
|
|
1364
|
+
});
|
|
1365
|
+
} else {
|
|
1366
|
+
await this.client.execute({
|
|
1367
|
+
sql: `
|
|
1368
|
+
UPDATE ${this.tablePrefix}_traces
|
|
1369
|
+
SET span_count = ?, updated_at = CURRENT_TIMESTAMP
|
|
1370
|
+
WHERE trace_id = ?
|
|
1371
|
+
`,
|
|
1372
|
+
args: [count, traceId]
|
|
1373
|
+
});
|
|
1374
|
+
}
|
|
1375
|
+
}
|
|
1376
|
+
}
|
|
1377
|
+
const deletedCount = deleteResult.rowsAffected || 0;
|
|
1378
|
+
this.debugLog("Old spans deleted", { deletedCount, beforeDate });
|
|
1379
|
+
return deletedCount;
|
|
1380
|
+
} catch (error) {
|
|
1381
|
+
this.logger.error("Failed to delete old spans", { error, beforeTimestamp });
|
|
1382
|
+
throw error;
|
|
1383
|
+
}
|
|
1384
|
+
}
|
|
1385
|
+
async clear() {
|
|
1386
|
+
await this.ensureInitialized();
|
|
1387
|
+
try {
|
|
1388
|
+
await this.client.batch([
|
|
1389
|
+
{ sql: `DELETE FROM ${this.tablePrefix}_spans`, args: [] },
|
|
1390
|
+
{ sql: `DELETE FROM ${this.tablePrefix}_traces`, args: [] },
|
|
1391
|
+
{ sql: `DELETE FROM ${this.tablePrefix}_logs`, args: [] }
|
|
1392
|
+
]);
|
|
1393
|
+
this.debugLog("All spans, traces, and logs cleared");
|
|
1394
|
+
} catch (error) {
|
|
1395
|
+
this.logger.error("Failed to clear data", { error });
|
|
1396
|
+
throw error;
|
|
1397
|
+
}
|
|
1398
|
+
}
|
|
1399
|
+
rowToSpan(row) {
|
|
1400
|
+
const span = {
|
|
1401
|
+
traceId: row.trace_id,
|
|
1402
|
+
spanId: row.span_id,
|
|
1403
|
+
name: row.name,
|
|
1404
|
+
kind: row.kind,
|
|
1405
|
+
startTime: row.start_time,
|
|
1406
|
+
status: {
|
|
1407
|
+
code: row.status_code
|
|
1408
|
+
},
|
|
1409
|
+
attributes: row.attributes ? JSON.parse(row.attributes) : {},
|
|
1410
|
+
events: row.events ? JSON.parse(row.events) : []
|
|
1411
|
+
};
|
|
1412
|
+
if (row.parent_span_id !== null) {
|
|
1413
|
+
span.parentSpanId = row.parent_span_id;
|
|
1414
|
+
}
|
|
1415
|
+
if (row.end_time !== null) {
|
|
1416
|
+
span.endTime = row.end_time;
|
|
1417
|
+
}
|
|
1418
|
+
if (row.duration !== null) {
|
|
1419
|
+
span.duration = row.duration;
|
|
1420
|
+
}
|
|
1421
|
+
if (row.status_message !== null) {
|
|
1422
|
+
span.status.message = row.status_message;
|
|
1423
|
+
}
|
|
1424
|
+
if (row.links && row.links !== "null") {
|
|
1425
|
+
const links = JSON.parse(row.links);
|
|
1426
|
+
if (links && links.length > 0) {
|
|
1427
|
+
span.links = links;
|
|
1428
|
+
}
|
|
1429
|
+
}
|
|
1430
|
+
if (row.resource && row.resource !== "null") {
|
|
1431
|
+
const resource = JSON.parse(row.resource);
|
|
1432
|
+
if (resource && Object.keys(resource).length > 0) {
|
|
1433
|
+
span.resource = resource;
|
|
1434
|
+
}
|
|
1435
|
+
}
|
|
1436
|
+
if (row.instrumentation_scope && row.instrumentation_scope !== "null") {
|
|
1437
|
+
const scope = JSON.parse(row.instrumentation_scope);
|
|
1438
|
+
if (scope) {
|
|
1439
|
+
span.instrumentationScope = scope;
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1442
|
+
return span;
|
|
1443
|
+
}
|
|
1444
|
+
async getStats() {
|
|
1445
|
+
await this.ensureInitialized();
|
|
1446
|
+
try {
|
|
1447
|
+
const [spanCountResult, traceCountResult, timeRangeResult] = await Promise.all([
|
|
1448
|
+
this.client.execute(`SELECT COUNT(*) as count FROM ${this.tablePrefix}_spans`),
|
|
1449
|
+
this.client.execute(`SELECT COUNT(*) as count FROM ${this.tablePrefix}_traces`),
|
|
1450
|
+
this.client.execute(`
|
|
1451
|
+
SELECT MIN(start_time) as oldest, MAX(start_time) as newest
|
|
1452
|
+
FROM ${this.tablePrefix}_spans
|
|
1453
|
+
`)
|
|
1454
|
+
]);
|
|
1455
|
+
const stats = {
|
|
1456
|
+
spanCount: spanCountResult.rows[0].count,
|
|
1457
|
+
traceCount: traceCountResult.rows[0].count
|
|
1458
|
+
};
|
|
1459
|
+
if (timeRangeResult.rows[0].oldest) {
|
|
1460
|
+
stats.oldestSpan = new Date(timeRangeResult.rows[0].oldest);
|
|
1461
|
+
}
|
|
1462
|
+
if (timeRangeResult.rows[0].newest) {
|
|
1463
|
+
stats.newestSpan = new Date(timeRangeResult.rows[0].newest);
|
|
1464
|
+
}
|
|
1465
|
+
return stats;
|
|
1466
|
+
} catch (error) {
|
|
1467
|
+
this.logger.error("Failed to get stats", { error });
|
|
1468
|
+
throw error;
|
|
1469
|
+
}
|
|
1470
|
+
}
|
|
1471
|
+
async saveLogRecord(logRecord) {
|
|
1472
|
+
await this.ensureInitialized();
|
|
1473
|
+
try {
|
|
1474
|
+
let timestamp;
|
|
1475
|
+
if (Array.isArray(logRecord.hrTime)) {
|
|
1476
|
+
const timeMs = logRecord.hrTime[0] * 1e3 + logRecord.hrTime[1] / 1e6;
|
|
1477
|
+
timestamp = new Date(timeMs).toISOString();
|
|
1478
|
+
} else if (logRecord.timestamp) {
|
|
1479
|
+
timestamp = typeof logRecord.timestamp === "string" ? logRecord.timestamp : new Date(logRecord.timestamp).toISOString();
|
|
1480
|
+
} else {
|
|
1481
|
+
timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
1482
|
+
}
|
|
1483
|
+
const spanContext = logRecord.spanContext || {};
|
|
1484
|
+
const traceId = spanContext.traceId || null;
|
|
1485
|
+
const spanId = spanContext.spanId || null;
|
|
1486
|
+
const traceFlags = spanContext.traceFlags ?? null;
|
|
1487
|
+
const severityNumber = logRecord.severityNumber ?? null;
|
|
1488
|
+
const severityText = logRecord.severityText || null;
|
|
1489
|
+
const body = typeof logRecord.body === "string" ? logRecord.body : safeStringify2(logRecord.body);
|
|
1490
|
+
const attributes = logRecord.attributes ? safeStringify2(logRecord.attributes) : null;
|
|
1491
|
+
const resource = logRecord.resource?.attributes ? safeStringify2(logRecord.resource.attributes) : null;
|
|
1492
|
+
const instrumentationScope = logRecord.instrumentationLibrary || logRecord.instrumentationScope ? safeStringify2(logRecord.instrumentationLibrary || logRecord.instrumentationScope) : null;
|
|
1493
|
+
await this.client.execute({
|
|
1494
|
+
sql: `
|
|
1495
|
+
INSERT INTO ${this.tablePrefix}_logs (
|
|
1496
|
+
timestamp, trace_id, span_id, trace_flags,
|
|
1497
|
+
severity_number, severity_text, body,
|
|
1498
|
+
attributes, resource, instrumentation_scope
|
|
1499
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
1500
|
+
`,
|
|
1501
|
+
args: [
|
|
1502
|
+
timestamp,
|
|
1503
|
+
traceId,
|
|
1504
|
+
spanId,
|
|
1505
|
+
traceFlags,
|
|
1506
|
+
severityNumber,
|
|
1507
|
+
severityText,
|
|
1508
|
+
body,
|
|
1509
|
+
attributes,
|
|
1510
|
+
resource,
|
|
1511
|
+
instrumentationScope
|
|
1512
|
+
]
|
|
1513
|
+
});
|
|
1514
|
+
this.debugLog("Log record saved successfully", {
|
|
1515
|
+
timestamp,
|
|
1516
|
+
traceId,
|
|
1517
|
+
spanId,
|
|
1518
|
+
severityNumber
|
|
1519
|
+
});
|
|
1520
|
+
} catch (error) {
|
|
1521
|
+
this.logger.error("Failed to save log record", { error, logRecord });
|
|
1522
|
+
throw error;
|
|
1523
|
+
}
|
|
1524
|
+
}
|
|
1525
|
+
async getLogsByTraceId(traceId) {
|
|
1526
|
+
await this.ensureInitialized();
|
|
1527
|
+
try {
|
|
1528
|
+
const result = await this.client.execute({
|
|
1529
|
+
sql: `
|
|
1530
|
+
SELECT * FROM ${this.tablePrefix}_logs
|
|
1531
|
+
WHERE trace_id = ?
|
|
1532
|
+
ORDER BY timestamp DESC
|
|
1533
|
+
LIMIT ?
|
|
1534
|
+
`,
|
|
1535
|
+
args: [traceId, this.maxSpansPerQuery]
|
|
1536
|
+
});
|
|
1537
|
+
return result.rows.map((row) => this.rowToLogRecord(row));
|
|
1538
|
+
} catch (error) {
|
|
1539
|
+
this.logger.error("Failed to get logs by trace ID", { error, traceId });
|
|
1540
|
+
throw error;
|
|
1541
|
+
}
|
|
1542
|
+
}
|
|
1543
|
+
async getLogsBySpanId(spanId) {
|
|
1544
|
+
await this.ensureInitialized();
|
|
1545
|
+
try {
|
|
1546
|
+
const result = await this.client.execute({
|
|
1547
|
+
sql: `
|
|
1548
|
+
SELECT * FROM ${this.tablePrefix}_logs
|
|
1549
|
+
WHERE span_id = ?
|
|
1550
|
+
ORDER BY timestamp DESC
|
|
1551
|
+
LIMIT ?
|
|
1552
|
+
`,
|
|
1553
|
+
args: [spanId, this.maxSpansPerQuery]
|
|
1554
|
+
});
|
|
1555
|
+
return result.rows.map((row) => this.rowToLogRecord(row));
|
|
1556
|
+
} catch (error) {
|
|
1557
|
+
this.logger.error("Failed to get logs by span ID", { error, spanId });
|
|
1558
|
+
throw error;
|
|
1559
|
+
}
|
|
1560
|
+
}
|
|
1561
|
+
async queryLogs(filter) {
|
|
1562
|
+
await this.ensureInitialized();
|
|
1563
|
+
try {
|
|
1564
|
+
const whereClauses = [];
|
|
1565
|
+
const args = [];
|
|
1566
|
+
if (filter.traceId) {
|
|
1567
|
+
whereClauses.push("trace_id = ?");
|
|
1568
|
+
args.push(filter.traceId);
|
|
1569
|
+
}
|
|
1570
|
+
if (filter.spanId) {
|
|
1571
|
+
whereClauses.push("span_id = ?");
|
|
1572
|
+
args.push(filter.spanId);
|
|
1573
|
+
}
|
|
1574
|
+
if (filter.severityNumber !== void 0) {
|
|
1575
|
+
whereClauses.push("severity_number >= ?");
|
|
1576
|
+
args.push(filter.severityNumber);
|
|
1577
|
+
}
|
|
1578
|
+
if (filter.severityText) {
|
|
1579
|
+
whereClauses.push("severity_text = ?");
|
|
1580
|
+
args.push(filter.severityText);
|
|
1581
|
+
}
|
|
1582
|
+
if (filter.instrumentationScope) {
|
|
1583
|
+
whereClauses.push("instrumentation_scope LIKE ?");
|
|
1584
|
+
args.push(`%${filter.instrumentationScope}%`);
|
|
1585
|
+
}
|
|
1586
|
+
if (filter.startTimeMin !== void 0) {
|
|
1587
|
+
const minTime = new Date(filter.startTimeMin).toISOString();
|
|
1588
|
+
whereClauses.push("timestamp >= ?");
|
|
1589
|
+
args.push(minTime);
|
|
1590
|
+
}
|
|
1591
|
+
if (filter.startTimeMax !== void 0) {
|
|
1592
|
+
const maxTime = new Date(filter.startTimeMax).toISOString();
|
|
1593
|
+
whereClauses.push("timestamp <= ?");
|
|
1594
|
+
args.push(maxTime);
|
|
1595
|
+
}
|
|
1596
|
+
if (filter.bodyContains) {
|
|
1597
|
+
whereClauses.push("body LIKE ?");
|
|
1598
|
+
args.push(`%${filter.bodyContains}%`);
|
|
1599
|
+
}
|
|
1600
|
+
const whereClause = whereClauses.length > 0 ? `WHERE ${whereClauses.join(" AND ")}` : "";
|
|
1601
|
+
const limit = filter.limit || this.maxSpansPerQuery;
|
|
1602
|
+
args.push(limit);
|
|
1603
|
+
const result = await this.client.execute({
|
|
1604
|
+
sql: `
|
|
1605
|
+
SELECT * FROM ${this.tablePrefix}_logs
|
|
1606
|
+
${whereClause}
|
|
1607
|
+
ORDER BY timestamp DESC
|
|
1608
|
+
LIMIT ?
|
|
1609
|
+
`,
|
|
1610
|
+
args
|
|
1611
|
+
});
|
|
1612
|
+
const logs = result.rows.map((row) => this.rowToLogRecord(row));
|
|
1613
|
+
if (filter.attributeKey) {
|
|
1614
|
+
const key = filter.attributeKey;
|
|
1615
|
+
return logs.filter((log) => {
|
|
1616
|
+
if (!log.attributes) return false;
|
|
1617
|
+
if (filter.attributeValue !== void 0) {
|
|
1618
|
+
return log.attributes[key] === filter.attributeValue;
|
|
1619
|
+
}
|
|
1620
|
+
return key in log.attributes;
|
|
1621
|
+
});
|
|
1622
|
+
}
|
|
1623
|
+
return logs;
|
|
1624
|
+
} catch (error) {
|
|
1625
|
+
this.logger.error("Failed to query logs", { error, filter });
|
|
1626
|
+
throw error;
|
|
1627
|
+
}
|
|
1628
|
+
}
|
|
1629
|
+
async deleteOldLogs(beforeTimestamp) {
|
|
1630
|
+
await this.ensureInitialized();
|
|
1631
|
+
try {
|
|
1632
|
+
const beforeDate = new Date(beforeTimestamp).toISOString();
|
|
1633
|
+
const result = await this.client.execute({
|
|
1634
|
+
sql: `DELETE FROM ${this.tablePrefix}_logs WHERE timestamp < ?`,
|
|
1635
|
+
args: [beforeDate]
|
|
1636
|
+
});
|
|
1637
|
+
const deletedCount = result.rowsAffected || 0;
|
|
1638
|
+
this.debugLog("Old logs deleted", { deletedCount, beforeDate });
|
|
1639
|
+
return deletedCount;
|
|
1640
|
+
} catch (error) {
|
|
1641
|
+
this.logger.error("Failed to delete old logs", { error, beforeTimestamp });
|
|
1642
|
+
throw error;
|
|
1643
|
+
}
|
|
1644
|
+
}
|
|
1645
|
+
rowToLogRecord(row) {
|
|
1646
|
+
const log = {
|
|
1647
|
+
timestamp: row.timestamp,
|
|
1648
|
+
body: (() => {
|
|
1649
|
+
try {
|
|
1650
|
+
const bodyStr = row.body;
|
|
1651
|
+
if (bodyStr.startsWith("{") || bodyStr.startsWith("[")) {
|
|
1652
|
+
return JSON.parse(bodyStr);
|
|
1653
|
+
}
|
|
1654
|
+
} catch {
|
|
1655
|
+
}
|
|
1656
|
+
return row.body;
|
|
1657
|
+
})()
|
|
1658
|
+
};
|
|
1659
|
+
if (row.trace_id !== null) {
|
|
1660
|
+
log.traceId = row.trace_id;
|
|
1661
|
+
}
|
|
1662
|
+
if (row.span_id !== null) {
|
|
1663
|
+
log.spanId = row.span_id;
|
|
1664
|
+
}
|
|
1665
|
+
if (row.trace_flags !== null) {
|
|
1666
|
+
log.traceFlags = row.trace_flags;
|
|
1667
|
+
}
|
|
1668
|
+
if (row.severity_number !== null) {
|
|
1669
|
+
log.severityNumber = row.severity_number;
|
|
1670
|
+
}
|
|
1671
|
+
if (row.severity_text !== null) {
|
|
1672
|
+
log.severityText = row.severity_text;
|
|
1673
|
+
}
|
|
1674
|
+
if (row.attributes && row.attributes !== "null") {
|
|
1675
|
+
try {
|
|
1676
|
+
const attributes = JSON.parse(row.attributes);
|
|
1677
|
+
if (attributes && Object.keys(attributes).length > 0) {
|
|
1678
|
+
log.attributes = attributes;
|
|
1679
|
+
}
|
|
1680
|
+
} catch {
|
|
1681
|
+
}
|
|
1682
|
+
}
|
|
1683
|
+
if (row.resource && row.resource !== "null") {
|
|
1684
|
+
try {
|
|
1685
|
+
const resource = JSON.parse(row.resource);
|
|
1686
|
+
if (resource && Object.keys(resource).length > 0) {
|
|
1687
|
+
log.resource = resource;
|
|
1688
|
+
}
|
|
1689
|
+
} catch {
|
|
1690
|
+
}
|
|
1691
|
+
}
|
|
1692
|
+
if (row.instrumentation_scope && row.instrumentation_scope !== "null") {
|
|
1693
|
+
try {
|
|
1694
|
+
const scope = JSON.parse(row.instrumentation_scope);
|
|
1695
|
+
if (scope) {
|
|
1696
|
+
log.instrumentationScope = scope;
|
|
1697
|
+
}
|
|
1698
|
+
} catch {
|
|
1699
|
+
}
|
|
1700
|
+
}
|
|
1701
|
+
return log;
|
|
1702
|
+
}
|
|
1703
|
+
getInfo() {
|
|
1704
|
+
return {
|
|
1705
|
+
adapter: this.constructor.name,
|
|
1706
|
+
displayName: "LibSQL Observability Storage",
|
|
1707
|
+
persistent: true,
|
|
1708
|
+
description: "Persists spans and logs to a LibSQL/Turso database for long-term retention."
|
|
1709
|
+
};
|
|
1710
|
+
}
|
|
1711
|
+
async close() {
|
|
1712
|
+
this.debugLog("LibSQL observability adapter closed");
|
|
1713
|
+
}
|
|
1714
|
+
};
|
|
1715
|
+
|
|
1716
|
+
// src/observability-adapter-edge.ts
|
|
1717
|
+
var LibSQLObservabilityAdapterEdge = class extends LibSQLObservabilityCore {
|
|
1718
|
+
static {
|
|
1719
|
+
__name(this, "LibSQLObservabilityAdapterEdge");
|
|
1720
|
+
}
|
|
1721
|
+
constructor(options) {
|
|
1722
|
+
if (!options.url) {
|
|
1723
|
+
throw new Error("LibSQLObservabilityAdapterEdge requires a url option");
|
|
1724
|
+
}
|
|
1725
|
+
if (options.url.startsWith("file:") || options.url === ":memory:" || !options.url.startsWith("libsql://")) {
|
|
1726
|
+
throw new Error(
|
|
1727
|
+
"LibSQLObservabilityAdapterEdge only supports remote Turso URLs (libsql://). File-based databases are not supported in edge environments. Use LibSQLObservabilityAdapter from '@voltagent/libsql' for Node.js environments."
|
|
1728
|
+
);
|
|
1729
|
+
}
|
|
1730
|
+
if (!options.authToken) {
|
|
1731
|
+
throw new Error(
|
|
1732
|
+
"LibSQLObservabilityAdapterEdge requires an authToken for remote connections"
|
|
1733
|
+
);
|
|
1734
|
+
}
|
|
1735
|
+
const logger = options.logger || createPinoLogger2({ name: "libsql-observability-edge" });
|
|
1736
|
+
const client = createClient2({
|
|
1737
|
+
url: options.url,
|
|
1738
|
+
authToken: options.authToken
|
|
1739
|
+
});
|
|
1740
|
+
super(client, options, logger);
|
|
1741
|
+
}
|
|
1742
|
+
};
|
|
1743
|
+
|
|
1744
|
+
// src/vector-adapter-edge.ts
|
|
1745
|
+
import { createClient as createClient3 } from "@libsql/client/web";
|
|
1746
|
+
import { createPinoLogger as createPinoLogger3 } from "@voltagent/logger";
|
|
1747
|
+
|
|
1748
|
+
// src/vector-core.ts
|
|
1749
|
+
import {
|
|
1750
|
+
cosineSimilarity
|
|
1751
|
+
} from "@voltagent/core";
|
|
1752
|
+
import { safeStringify as safeStringify3 } from "@voltagent/internal";
|
|
1753
|
+
var LibSQLVectorCore = class {
|
|
1754
|
+
static {
|
|
1755
|
+
__name(this, "LibSQLVectorCore");
|
|
1756
|
+
}
|
|
1757
|
+
client;
|
|
1758
|
+
tablePrefix;
|
|
1759
|
+
maxVectorDimensions;
|
|
1760
|
+
cacheSize;
|
|
1761
|
+
batchSize;
|
|
1762
|
+
debug;
|
|
1763
|
+
logger;
|
|
1764
|
+
maxRetries;
|
|
1765
|
+
retryDelayMs;
|
|
1766
|
+
initialized = false;
|
|
1767
|
+
vectorCache;
|
|
1768
|
+
dimensions = null;
|
|
1769
|
+
constructor(client, options, logger) {
|
|
1770
|
+
this.client = client;
|
|
1771
|
+
this.tablePrefix = options.tablePrefix ?? "voltagent";
|
|
1772
|
+
this.maxVectorDimensions = options.maxVectorDimensions ?? 1536;
|
|
1773
|
+
this.cacheSize = options.cacheSize ?? 100;
|
|
1774
|
+
this.batchSize = options.batchSize ?? 100;
|
|
1775
|
+
this.maxRetries = options.maxRetries ?? 3;
|
|
1776
|
+
this.retryDelayMs = options.retryDelayMs ?? 100;
|
|
1777
|
+
this.debug = options.debug ?? false;
|
|
1778
|
+
this.logger = logger;
|
|
1779
|
+
this.vectorCache = /* @__PURE__ */ new Map();
|
|
1780
|
+
}
|
|
1781
|
+
/**
|
|
1782
|
+
* Serialize a vector to binary format
|
|
1783
|
+
* Uses ArrayBuffer/DataView for cross-platform compatibility
|
|
1784
|
+
*/
|
|
1785
|
+
serializeVector(vector) {
|
|
1786
|
+
const buffer = new ArrayBuffer(vector.length * 4);
|
|
1787
|
+
const view = new DataView(buffer);
|
|
1788
|
+
for (let i = 0; i < vector.length; i++) {
|
|
1789
|
+
view.setFloat32(i * 4, vector[i], true);
|
|
1790
|
+
}
|
|
1791
|
+
return new Uint8Array(buffer);
|
|
1792
|
+
}
|
|
1793
|
+
/**
|
|
1794
|
+
* Deserialize a vector from binary format
|
|
1795
|
+
*/
|
|
1796
|
+
deserializeVector(data) {
|
|
1797
|
+
const bytes = data instanceof ArrayBuffer ? new Uint8Array(data) : data;
|
|
1798
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
1799
|
+
const vector = [];
|
|
1800
|
+
for (let i = 0; i < bytes.length; i += 4) {
|
|
1801
|
+
vector.push(view.getFloat32(i, true));
|
|
1802
|
+
}
|
|
1803
|
+
return vector;
|
|
1804
|
+
}
|
|
1805
|
+
/**
|
|
1806
|
+
* Initialize the database schema
|
|
1807
|
+
*/
|
|
1808
|
+
async initialize() {
|
|
1809
|
+
if (this.initialized) return;
|
|
1810
|
+
const tableName = `${this.tablePrefix}_vectors`;
|
|
1811
|
+
try {
|
|
1812
|
+
await this.client.execute(`
|
|
1813
|
+
CREATE TABLE IF NOT EXISTS ${tableName} (
|
|
1814
|
+
id TEXT PRIMARY KEY,
|
|
1815
|
+
vector BLOB NOT NULL,
|
|
1816
|
+
dimensions INTEGER NOT NULL,
|
|
1817
|
+
metadata TEXT,
|
|
1818
|
+
content TEXT,
|
|
1819
|
+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
1820
|
+
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
1821
|
+
)
|
|
1822
|
+
`);
|
|
1823
|
+
await this.client.execute(
|
|
1824
|
+
`CREATE INDEX IF NOT EXISTS idx_${tableName}_created ON ${tableName}(created_at)`
|
|
1825
|
+
);
|
|
1826
|
+
await this.client.execute(
|
|
1827
|
+
`CREATE INDEX IF NOT EXISTS idx_${tableName}_dimensions ON ${tableName}(dimensions)`
|
|
1828
|
+
);
|
|
1829
|
+
this.initialized = true;
|
|
1830
|
+
this.logger.debug("Vector adapter initialized");
|
|
1831
|
+
} catch (error) {
|
|
1832
|
+
this.logger.error("Failed to initialize vector adapter", error);
|
|
1833
|
+
throw error;
|
|
1834
|
+
}
|
|
1835
|
+
}
|
|
1836
|
+
/**
|
|
1837
|
+
* Execute a database operation with retries
|
|
1838
|
+
*/
|
|
1839
|
+
async executeWithRetry(operation, context) {
|
|
1840
|
+
let lastError;
|
|
1841
|
+
let delay = this.retryDelayMs;
|
|
1842
|
+
for (let attempt = 1; attempt <= this.maxRetries; attempt++) {
|
|
1843
|
+
try {
|
|
1844
|
+
return await operation();
|
|
1845
|
+
} catch (error) {
|
|
1846
|
+
lastError = error;
|
|
1847
|
+
this.logger.warn(`Operation failed (attempt ${attempt}): ${context}`, error);
|
|
1848
|
+
if (attempt < this.maxRetries) {
|
|
1849
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
1850
|
+
delay *= 2;
|
|
1851
|
+
}
|
|
1852
|
+
}
|
|
1853
|
+
}
|
|
1854
|
+
this.logger.error(`Operation failed after ${this.maxRetries} attempts: ${context}`, lastError);
|
|
1855
|
+
throw lastError;
|
|
1856
|
+
}
|
|
1857
|
+
async store(id, vector, metadata) {
|
|
1858
|
+
await this.initialize();
|
|
1859
|
+
if (!Array.isArray(vector) || vector.length === 0) {
|
|
1860
|
+
throw new Error("Vector must be a non-empty array");
|
|
1861
|
+
}
|
|
1862
|
+
if (vector.length > this.maxVectorDimensions) {
|
|
1863
|
+
throw new Error(
|
|
1864
|
+
`Vector dimensions (${vector.length}) exceed maximum (${this.maxVectorDimensions})`
|
|
1865
|
+
);
|
|
1866
|
+
}
|
|
1867
|
+
if (this.dimensions === null) {
|
|
1868
|
+
this.dimensions = vector.length;
|
|
1869
|
+
} else if (vector.length !== this.dimensions) {
|
|
1870
|
+
throw new Error(
|
|
1871
|
+
`Vector dimension mismatch. Expected ${this.dimensions}, got ${vector.length}`
|
|
1872
|
+
);
|
|
1873
|
+
}
|
|
1874
|
+
const tableName = `${this.tablePrefix}_vectors`;
|
|
1875
|
+
const serializedVector = this.serializeVector(vector);
|
|
1876
|
+
const metadataJson = metadata ? safeStringify3(metadata) : null;
|
|
1877
|
+
await this.executeWithRetry(async () => {
|
|
1878
|
+
await this.client.execute({
|
|
1879
|
+
sql: `
|
|
1880
|
+
INSERT OR REPLACE INTO ${tableName}
|
|
1881
|
+
(id, vector, dimensions, metadata, updated_at)
|
|
1882
|
+
VALUES (?, ?, ?, ?, CURRENT_TIMESTAMP)
|
|
1883
|
+
`,
|
|
1884
|
+
args: [id, serializedVector, vector.length, metadataJson]
|
|
1885
|
+
});
|
|
1886
|
+
}, `store vector ${id}`);
|
|
1887
|
+
if (this.vectorCache.size >= this.cacheSize) {
|
|
1888
|
+
const firstKey = this.vectorCache.keys().next().value;
|
|
1889
|
+
if (firstKey) this.vectorCache.delete(firstKey);
|
|
1890
|
+
}
|
|
1891
|
+
this.vectorCache.set(id, { id, vector, metadata });
|
|
1892
|
+
this.logger.debug(`Vector stored: ${id} (${vector.length} dimensions)`);
|
|
1893
|
+
}
|
|
1894
|
+
async storeBatch(items) {
|
|
1895
|
+
await this.initialize();
|
|
1896
|
+
if (items.length === 0) return;
|
|
1897
|
+
const tableName = `${this.tablePrefix}_vectors`;
|
|
1898
|
+
for (let i = 0; i < items.length; i += this.batchSize) {
|
|
1899
|
+
const batch = items.slice(i, i + this.batchSize);
|
|
1900
|
+
await this.executeWithRetry(async () => {
|
|
1901
|
+
const stmts = [];
|
|
1902
|
+
for (const item of batch) {
|
|
1903
|
+
if (!Array.isArray(item.vector) || item.vector.length === 0) {
|
|
1904
|
+
throw new Error("Vector must be a non-empty array");
|
|
1905
|
+
}
|
|
1906
|
+
if (this.dimensions === null) {
|
|
1907
|
+
this.dimensions = item.vector.length;
|
|
1908
|
+
} else if (item.vector.length !== this.dimensions) {
|
|
1909
|
+
throw new Error(
|
|
1910
|
+
`Vector dimension mismatch. Expected ${this.dimensions}, got ${item.vector.length}`
|
|
1911
|
+
);
|
|
1912
|
+
}
|
|
1913
|
+
const serializedVector = this.serializeVector(item.vector);
|
|
1914
|
+
const metadataJson = item.metadata ? safeStringify3(item.metadata) : null;
|
|
1915
|
+
const content = item.content ?? null;
|
|
1916
|
+
stmts.push({
|
|
1917
|
+
sql: `INSERT OR REPLACE INTO ${tableName} (id, vector, dimensions, metadata, content, updated_at) VALUES (?, ?, ?, ?, ?, CURRENT_TIMESTAMP)`,
|
|
1918
|
+
args: [item.id, serializedVector, item.vector.length, metadataJson, content]
|
|
1919
|
+
});
|
|
1920
|
+
}
|
|
1921
|
+
await this.client.batch(stmts, "write");
|
|
1922
|
+
}, `storeBatch ${batch.length} vectors`);
|
|
1923
|
+
this.logger.debug(`Batch of ${batch.length} vectors stored`);
|
|
1924
|
+
}
|
|
1925
|
+
}
|
|
1926
|
+
async search(queryVector, options) {
|
|
1927
|
+
await this.initialize();
|
|
1928
|
+
const { limit = 10, threshold = 0, filter } = options || {};
|
|
1929
|
+
if (this.dimensions !== null && queryVector.length !== this.dimensions) {
|
|
1930
|
+
throw new Error(
|
|
1931
|
+
`Query vector dimension mismatch. Expected ${this.dimensions}, got ${queryVector.length}`
|
|
1932
|
+
);
|
|
1933
|
+
}
|
|
1934
|
+
const tableName = `${this.tablePrefix}_vectors`;
|
|
1935
|
+
let query = `SELECT id, vector, dimensions, metadata, content FROM ${tableName}`;
|
|
1936
|
+
const args = [];
|
|
1937
|
+
if (this.dimensions !== null) {
|
|
1938
|
+
query += " WHERE dimensions = ?";
|
|
1939
|
+
args.push(this.dimensions);
|
|
1940
|
+
}
|
|
1941
|
+
const result = await this.executeWithRetry(
|
|
1942
|
+
async () => await this.client.execute({ sql: query, args }),
|
|
1943
|
+
"search vectors"
|
|
1944
|
+
);
|
|
1945
|
+
const searchResults = [];
|
|
1946
|
+
for (const row of result.rows) {
|
|
1947
|
+
const id = row.id;
|
|
1948
|
+
const vectorBlob = row.vector;
|
|
1949
|
+
const metadataJson = row.metadata;
|
|
1950
|
+
const content = row.content ?? void 0;
|
|
1951
|
+
const metadata = metadataJson ? JSON.parse(metadataJson) : void 0;
|
|
1952
|
+
if (filter && !this.matchesFilter(metadata, filter)) {
|
|
1953
|
+
continue;
|
|
1954
|
+
}
|
|
1955
|
+
const vector = this.deserializeVector(vectorBlob);
|
|
1956
|
+
const similarity = cosineSimilarity(queryVector, vector);
|
|
1957
|
+
const score = (similarity + 1) / 2;
|
|
1958
|
+
if (score >= threshold) {
|
|
1959
|
+
searchResults.push({
|
|
1960
|
+
id,
|
|
1961
|
+
vector,
|
|
1962
|
+
metadata,
|
|
1963
|
+
content,
|
|
1964
|
+
score,
|
|
1965
|
+
distance: 1 - similarity
|
|
1966
|
+
});
|
|
1967
|
+
}
|
|
1968
|
+
}
|
|
1969
|
+
searchResults.sort((a, b) => b.score - a.score);
|
|
1970
|
+
return searchResults.slice(0, limit);
|
|
1971
|
+
}
|
|
1972
|
+
matchesFilter(metadata, filter) {
|
|
1973
|
+
if (!metadata) {
|
|
1974
|
+
return false;
|
|
1975
|
+
}
|
|
1976
|
+
for (const [key, value] of Object.entries(filter)) {
|
|
1977
|
+
if (metadata[key] !== value) {
|
|
1978
|
+
return false;
|
|
1979
|
+
}
|
|
1980
|
+
}
|
|
1981
|
+
return true;
|
|
1982
|
+
}
|
|
1983
|
+
async delete(id) {
|
|
1984
|
+
await this.initialize();
|
|
1985
|
+
const tableName = `${this.tablePrefix}_vectors`;
|
|
1986
|
+
await this.executeWithRetry(async () => {
|
|
1987
|
+
await this.client.execute({
|
|
1988
|
+
sql: `DELETE FROM ${tableName} WHERE id = ?`,
|
|
1989
|
+
args: [id]
|
|
1990
|
+
});
|
|
1991
|
+
}, `delete vector ${id}`);
|
|
1992
|
+
this.vectorCache.delete(id);
|
|
1993
|
+
this.logger.debug(`Vector deleted: ${id}`);
|
|
1994
|
+
}
|
|
1995
|
+
async deleteBatch(ids) {
|
|
1996
|
+
await this.initialize();
|
|
1997
|
+
if (ids.length === 0) return;
|
|
1998
|
+
const tableName = `${this.tablePrefix}_vectors`;
|
|
1999
|
+
for (let i = 0; i < ids.length; i += this.batchSize) {
|
|
2000
|
+
const batch = ids.slice(i, i + this.batchSize);
|
|
2001
|
+
const placeholders = batch.map(() => "?").join(",");
|
|
2002
|
+
await this.executeWithRetry(async () => {
|
|
2003
|
+
await this.client.execute({
|
|
2004
|
+
sql: `DELETE FROM ${tableName} WHERE id IN (${placeholders})`,
|
|
2005
|
+
args: batch
|
|
2006
|
+
});
|
|
2007
|
+
}, `deleteBatch ${batch.length} vectors`);
|
|
2008
|
+
for (const id of batch) {
|
|
2009
|
+
this.vectorCache.delete(id);
|
|
2010
|
+
}
|
|
2011
|
+
this.logger.debug(`Batch of ${batch.length} vectors deleted`);
|
|
2012
|
+
}
|
|
2013
|
+
}
|
|
2014
|
+
async clear() {
|
|
2015
|
+
await this.initialize();
|
|
2016
|
+
const tableName = `${this.tablePrefix}_vectors`;
|
|
2017
|
+
await this.executeWithRetry(async () => {
|
|
2018
|
+
await this.client.execute(`DELETE FROM ${tableName}`);
|
|
2019
|
+
}, "clear all vectors");
|
|
2020
|
+
this.vectorCache.clear();
|
|
2021
|
+
this.dimensions = null;
|
|
2022
|
+
this.logger.debug("All vectors cleared");
|
|
2023
|
+
}
|
|
2024
|
+
async count() {
|
|
2025
|
+
await this.initialize();
|
|
2026
|
+
const tableName = `${this.tablePrefix}_vectors`;
|
|
2027
|
+
const result = await this.executeWithRetry(
|
|
2028
|
+
async () => await this.client.execute(`SELECT COUNT(*) as count FROM ${tableName}`),
|
|
2029
|
+
"count vectors"
|
|
2030
|
+
);
|
|
2031
|
+
const raw = result.rows[0]?.count;
|
|
2032
|
+
if (typeof raw === "bigint") return Number(raw);
|
|
2033
|
+
if (typeof raw === "string") return Number.parseInt(raw, 10) || 0;
|
|
2034
|
+
return raw ?? 0;
|
|
2035
|
+
}
|
|
2036
|
+
async get(id) {
|
|
2037
|
+
await this.initialize();
|
|
2038
|
+
if (this.vectorCache.has(id)) {
|
|
2039
|
+
const cached = this.vectorCache.get(id);
|
|
2040
|
+
if (cached) {
|
|
2041
|
+
return {
|
|
2042
|
+
...cached,
|
|
2043
|
+
vector: [...cached.vector],
|
|
2044
|
+
metadata: cached.metadata ? { ...cached.metadata } : void 0
|
|
2045
|
+
};
|
|
2046
|
+
}
|
|
2047
|
+
}
|
|
2048
|
+
const tableName = `${this.tablePrefix}_vectors`;
|
|
2049
|
+
const result = await this.executeWithRetry(
|
|
2050
|
+
async () => await this.client.execute({
|
|
2051
|
+
sql: `SELECT id, vector, metadata, content FROM ${tableName} WHERE id = ?`,
|
|
2052
|
+
args: [id]
|
|
2053
|
+
}),
|
|
2054
|
+
`get vector ${id}`
|
|
2055
|
+
);
|
|
2056
|
+
if (result.rows.length === 0) {
|
|
2057
|
+
return null;
|
|
2058
|
+
}
|
|
2059
|
+
const row = result.rows[0];
|
|
2060
|
+
const vectorBlob = row.vector;
|
|
2061
|
+
const metadataJson = row.metadata;
|
|
2062
|
+
const content = row.content;
|
|
2063
|
+
const vector = this.deserializeVector(vectorBlob);
|
|
2064
|
+
const metadata = metadataJson ? JSON.parse(metadataJson) : void 0;
|
|
2065
|
+
const item = {
|
|
2066
|
+
id,
|
|
2067
|
+
vector,
|
|
2068
|
+
metadata,
|
|
2069
|
+
content: content ?? void 0
|
|
2070
|
+
};
|
|
2071
|
+
if (this.vectorCache.size >= this.cacheSize) {
|
|
2072
|
+
const firstKey = this.vectorCache.keys().next().value;
|
|
2073
|
+
if (firstKey) this.vectorCache.delete(firstKey);
|
|
2074
|
+
}
|
|
2075
|
+
this.vectorCache.set(id, item);
|
|
2076
|
+
return item;
|
|
2077
|
+
}
|
|
2078
|
+
async close() {
|
|
2079
|
+
this.vectorCache.clear();
|
|
2080
|
+
this.logger.debug("Vector adapter closed");
|
|
2081
|
+
}
|
|
2082
|
+
async getStats() {
|
|
2083
|
+
await this.initialize();
|
|
2084
|
+
const tableName = `${this.tablePrefix}_vectors`;
|
|
2085
|
+
const [countResult, sizeResult] = await Promise.all([
|
|
2086
|
+
this.executeWithRetry(
|
|
2087
|
+
async () => await this.client.execute(
|
|
2088
|
+
`SELECT COUNT(*) as count, MAX(dimensions) as dims FROM ${tableName}`
|
|
2089
|
+
),
|
|
2090
|
+
"getStats count"
|
|
2091
|
+
),
|
|
2092
|
+
this.executeWithRetry(
|
|
2093
|
+
async () => await this.client.execute({
|
|
2094
|
+
sql: `SELECT
|
|
2095
|
+
COALESCE(SUM(LENGTH(id)),0) +
|
|
2096
|
+
COALESCE(SUM(LENGTH(vector)),0) +
|
|
2097
|
+
COALESCE(SUM(LENGTH(metadata)),0) +
|
|
2098
|
+
COALESCE(SUM(LENGTH(content)),0) AS size
|
|
2099
|
+
FROM ${tableName}`
|
|
2100
|
+
}),
|
|
2101
|
+
"getStats size"
|
|
2102
|
+
)
|
|
2103
|
+
]);
|
|
2104
|
+
const row1 = countResult.rows[0];
|
|
2105
|
+
const row2 = sizeResult.rows[0];
|
|
2106
|
+
const countRaw = row1?.count;
|
|
2107
|
+
const dimsRaw = row1?.dims;
|
|
2108
|
+
const sizeRaw = row2?.size;
|
|
2109
|
+
const normalize = /* @__PURE__ */ __name((v) => typeof v === "bigint" ? Number(v) : typeof v === "string" ? Number.parseInt(v, 10) || 0 : v ?? 0, "normalize");
|
|
2110
|
+
return {
|
|
2111
|
+
count: normalize(countRaw),
|
|
2112
|
+
dimensions: dimsRaw != null ? normalize(dimsRaw) : this.dimensions,
|
|
2113
|
+
cacheSize: this.vectorCache.size,
|
|
2114
|
+
tableSizeBytes: normalize(sizeRaw)
|
|
2115
|
+
};
|
|
2116
|
+
}
|
|
2117
|
+
};
|
|
2118
|
+
|
|
2119
|
+
// src/vector-adapter-edge.ts
|
|
2120
|
+
var LibSQLVectorAdapterEdge = class extends LibSQLVectorCore {
|
|
2121
|
+
static {
|
|
2122
|
+
__name(this, "LibSQLVectorAdapterEdge");
|
|
2123
|
+
}
|
|
2124
|
+
constructor(options) {
|
|
2125
|
+
if (!options.url) {
|
|
2126
|
+
throw new Error("LibSQLVectorAdapterEdge requires a url option");
|
|
2127
|
+
}
|
|
2128
|
+
if (options.url.startsWith("file:") || options.url === ":memory:" || !options.url.startsWith("libsql://")) {
|
|
2129
|
+
throw new Error(
|
|
2130
|
+
"LibSQLVectorAdapterEdge only supports remote Turso URLs (libsql://). File-based databases are not supported in edge environments. Use LibSQLVectorAdapter from '@voltagent/libsql' for Node.js environments."
|
|
2131
|
+
);
|
|
2132
|
+
}
|
|
2133
|
+
if (!options.authToken) {
|
|
2134
|
+
throw new Error("LibSQLVectorAdapterEdge requires an authToken for remote connections");
|
|
2135
|
+
}
|
|
2136
|
+
const logger = options.logger ?? createPinoLogger3({
|
|
2137
|
+
name: "libsql-vector-adapter-edge",
|
|
2138
|
+
level: options.debug ? "debug" : "info"
|
|
2139
|
+
});
|
|
2140
|
+
const client = createClient3({
|
|
2141
|
+
url: options.url,
|
|
2142
|
+
authToken: options.authToken
|
|
2143
|
+
});
|
|
2144
|
+
super(client, options, logger);
|
|
2145
|
+
}
|
|
2146
|
+
};
|
|
2147
|
+
export {
|
|
2148
|
+
LibSQLMemoryAdapterEdge as LibSQLMemoryAdapter,
|
|
2149
|
+
LibSQLMemoryAdapterEdge,
|
|
2150
|
+
LibSQLObservabilityAdapterEdge as LibSQLObservabilityAdapter,
|
|
2151
|
+
LibSQLObservabilityAdapterEdge,
|
|
2152
|
+
LibSQLVectorAdapterEdge as LibSQLVectorAdapter,
|
|
2153
|
+
LibSQLVectorAdapterEdge
|
|
2154
|
+
};
|
|
2155
|
+
//# sourceMappingURL=edge.mjs.map
|