@mastra/core 0.5.0-alpha.8 → 0.5.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.
Files changed (91) hide show
  1. package/dist/agent/index.cjs +3 -2100
  2. package/dist/agent/index.d.cts +1 -1
  3. package/dist/agent/index.d.ts +1 -1
  4. package/dist/agent/index.js +1 -1
  5. package/dist/{base-CTdONy0_.d.cts → base-CIPKleAU.d.cts} +103 -70
  6. package/dist/{base-DIn_km7X.d.ts → base-C_Oq53qk.d.ts} +103 -70
  7. package/dist/base.cjs +5 -140
  8. package/dist/bundler/index.cjs +5 -160
  9. package/dist/chunk-2W2GYEYQ.cjs +25 -0
  10. package/dist/chunk-3ASEZT7U.cjs +1586 -0
  11. package/dist/chunk-43Y7WG5W.cjs +335 -0
  12. package/dist/{chunk-HBHPTMAC.js → chunk-4Y74D74B.js} +46 -6
  13. package/dist/chunk-ENT7U27Y.cjs +37 -0
  14. package/dist/chunk-F5UYWPV4.cjs +14 -0
  15. package/dist/chunk-FL3GQXQ2.cjs +218 -0
  16. package/dist/chunk-FRQFWZDN.cjs +2 -0
  17. package/dist/chunk-GXQRMKSN.cjs +367 -0
  18. package/dist/chunk-HJPMYDWO.cjs +37 -0
  19. package/dist/chunk-IIWRJFLQ.cjs +51 -0
  20. package/dist/chunk-KFQ7Z3PO.cjs +347 -0
  21. package/dist/{chunk-SWDQYPJS.js → chunk-KP5UAFLN.js} +3 -2
  22. package/dist/chunk-KPKFLQFR.cjs +12 -0
  23. package/dist/{chunk-RRJB4TCC.js → chunk-MLFXOST6.js} +1 -1
  24. package/dist/{chunk-KBSR2LLT.js → chunk-OD7ZMKHY.js} +176 -63
  25. package/dist/chunk-OTFLHXHZ.cjs +65 -0
  26. package/dist/chunk-RWTSGWWL.cjs +81 -0
  27. package/dist/chunk-ST5RMVLG.cjs +87 -0
  28. package/dist/chunk-SYQ7NK2E.cjs +24 -0
  29. package/dist/chunk-UZNQG7QO.cjs +1868 -0
  30. package/dist/chunk-V5ORZPFW.cjs +38 -0
  31. package/dist/chunk-VA4P7QJT.cjs +443 -0
  32. package/dist/chunk-WB2HREXE.cjs +166 -0
  33. package/dist/chunk-WOMOGDGR.cjs +691 -0
  34. package/dist/chunk-XB2TJ7LX.cjs +408 -0
  35. package/dist/{chunk-QABMKXI3.js → chunk-XF2FMJYK.js} +1 -1
  36. package/dist/chunk-XLSROQ26.cjs +91 -0
  37. package/dist/chunk-YK3XJ52U.cjs +192 -0
  38. package/dist/{chunk-SF5GHHOQ.js → chunk-YPD6BQIM.js} +121 -93
  39. package/dist/deployer/index.cjs +5 -167
  40. package/dist/eval/index.cjs +9 -105
  41. package/dist/eval/index.d.cts +1 -1
  42. package/dist/eval/index.d.ts +1 -1
  43. package/dist/hooks/index.cjs +14 -83
  44. package/dist/index.cjs +253 -7470
  45. package/dist/index.d.cts +4 -4
  46. package/dist/index.d.ts +4 -4
  47. package/dist/index.js +7 -7
  48. package/dist/integration/index.cjs +9 -108
  49. package/dist/integration/index.d.cts +1 -1
  50. package/dist/integration/index.d.ts +1 -1
  51. package/dist/llm/index.d.cts +1 -1
  52. package/dist/llm/index.d.ts +1 -1
  53. package/dist/logger/index.cjs +33 -161
  54. package/dist/mastra/index.cjs +3 -1755
  55. package/dist/mastra/index.d.cts +1 -1
  56. package/dist/mastra/index.d.ts +1 -1
  57. package/dist/mastra/index.js +1 -1
  58. package/dist/memory/index.cjs +4 -2050
  59. package/dist/memory/index.d.cts +1 -1
  60. package/dist/memory/index.d.ts +1 -1
  61. package/dist/memory/index.js +1 -1
  62. package/dist/relevance/index.cjs +10 -2161
  63. package/dist/relevance/index.d.cts +19 -2
  64. package/dist/relevance/index.d.ts +19 -2
  65. package/dist/relevance/index.js +1 -1
  66. package/dist/storage/index.cjs +29 -367
  67. package/dist/storage/index.d.cts +1 -1
  68. package/dist/storage/index.d.ts +1 -1
  69. package/dist/storage/libsql/index.cjs +9 -798
  70. package/dist/storage/libsql/index.d.cts +1 -1
  71. package/dist/storage/libsql/index.d.ts +1 -1
  72. package/dist/telemetry/index.cjs +21 -408
  73. package/dist/telemetry/index.d.cts +1 -1
  74. package/dist/telemetry/index.d.ts +1 -1
  75. package/dist/tools/index.cjs +11 -22
  76. package/dist/tools/index.d.cts +3 -3
  77. package/dist/tools/index.d.ts +3 -3
  78. package/dist/tts/index.cjs +3 -328
  79. package/dist/utils.cjs +41 -309
  80. package/dist/utils.d.cts +10 -4
  81. package/dist/utils.d.ts +10 -4
  82. package/dist/utils.js +1 -1
  83. package/dist/vector/filter/index.cjs +7 -189
  84. package/dist/vector/index.cjs +5 -172
  85. package/dist/vector/libsql/index.cjs +9 -1047
  86. package/dist/voice/index.cjs +8 -306
  87. package/dist/workflows/index.cjs +65 -1925
  88. package/dist/workflows/index.d.cts +4 -3
  89. package/dist/workflows/index.d.ts +4 -3
  90. package/dist/workflows/index.js +1 -1
  91. package/package.json +27 -27
@@ -0,0 +1,691 @@
1
+ 'use strict';
2
+
3
+ var chunkENT7U27Y_cjs = require('./chunk-ENT7U27Y.cjs');
4
+ var chunkYK3XJ52U_cjs = require('./chunk-YK3XJ52U.cjs');
5
+ var path = require('path');
6
+ var client = require('@libsql/client');
7
+
8
+ // src/vector/libsql/filter.ts
9
+ var LibSQLFilterTranslator = class extends chunkYK3XJ52U_cjs.BaseFilterTranslator {
10
+ getSupportedOperators() {
11
+ return {
12
+ ...chunkYK3XJ52U_cjs.BaseFilterTranslator.DEFAULT_OPERATORS,
13
+ regex: [],
14
+ custom: ["$contains", "$size"]
15
+ };
16
+ }
17
+ translate(filter) {
18
+ if (this.isEmpty(filter)) {
19
+ return filter;
20
+ }
21
+ this.validateFilter(filter);
22
+ return this.translateNode(filter);
23
+ }
24
+ translateNode(node, currentPath = "") {
25
+ if (this.isRegex(node)) {
26
+ throw new Error("Direct regex pattern format is not supported in LibSQL");
27
+ }
28
+ const withPath = (result2) => currentPath ? { [currentPath]: result2 } : result2;
29
+ if (this.isPrimitive(node)) {
30
+ return withPath({ $eq: this.normalizeComparisonValue(node) });
31
+ }
32
+ if (Array.isArray(node)) {
33
+ return withPath({ $in: this.normalizeArrayValues(node) });
34
+ }
35
+ const entries = Object.entries(node);
36
+ const result = {};
37
+ for (const [key, value] of entries) {
38
+ const newPath = currentPath ? `${currentPath}.${key}` : key;
39
+ if (this.isLogicalOperator(key)) {
40
+ result[key] = Array.isArray(value) ? value.map((filter) => this.translateNode(filter)) : this.translateNode(value);
41
+ } else if (this.isOperator(key)) {
42
+ if (this.isArrayOperator(key) && !Array.isArray(value) && key !== "$elemMatch") {
43
+ result[key] = [value];
44
+ } else if (this.isBasicOperator(key) && Array.isArray(value)) {
45
+ result[key] = JSON.stringify(value);
46
+ } else {
47
+ result[key] = value;
48
+ }
49
+ } else if (typeof value === "object" && value !== null) {
50
+ const hasOperators = Object.keys(value).some((k) => this.isOperator(k));
51
+ if (hasOperators) {
52
+ result[newPath] = this.translateNode(value);
53
+ } else {
54
+ Object.assign(result, this.translateNode(value, newPath));
55
+ }
56
+ } else {
57
+ result[newPath] = this.translateNode(value);
58
+ }
59
+ }
60
+ return result;
61
+ }
62
+ // TODO: Look more into regex support for LibSQL
63
+ // private translateRegexPattern(pattern: string, options: string = ''): any {
64
+ // if (!options) return { $regex: pattern };
65
+ // const flags = options
66
+ // .split('')
67
+ // .filter(f => 'imsux'.includes(f))
68
+ // .join('');
69
+ // return {
70
+ // $regex: pattern,
71
+ // $options: flags,
72
+ // };
73
+ // }
74
+ };
75
+
76
+ // src/vector/libsql/sql-builder.ts
77
+ var createBasicOperator = (symbol) => {
78
+ return (key) => ({
79
+ sql: `CASE
80
+ WHEN ? IS NULL THEN json_extract(metadata, '$."${handleKey(key)}"') IS ${symbol === "=" ? "" : "NOT"} NULL
81
+ ELSE json_extract(metadata, '$."${handleKey(key)}"') ${symbol} ?
82
+ END`,
83
+ needsValue: true,
84
+ transformValue: (value) => {
85
+ return [value, value];
86
+ }
87
+ });
88
+ };
89
+ var createNumericOperator = (symbol) => {
90
+ return (key) => ({
91
+ sql: `CAST(json_extract(metadata, '$."${handleKey(key)}"') AS NUMERIC) ${symbol} ?`,
92
+ needsValue: true
93
+ });
94
+ };
95
+ var validateJsonArray = (key) => `json_valid(json_extract(metadata, '$."${handleKey(key)}"'))
96
+ AND json_type(json_extract(metadata, '$."${handleKey(key)}"')) = 'array'`;
97
+ var FILTER_OPERATORS = {
98
+ $eq: createBasicOperator("="),
99
+ $ne: createBasicOperator("!="),
100
+ $gt: createNumericOperator(">"),
101
+ $gte: createNumericOperator(">="),
102
+ $lt: createNumericOperator("<"),
103
+ $lte: createNumericOperator("<="),
104
+ // Array Operators
105
+ $in: (key, value) => ({
106
+ sql: `json_extract(metadata, '$."${handleKey(key)}"') IN (${value.map(() => "?").join(",")})`,
107
+ needsValue: true
108
+ }),
109
+ $nin: (key, value) => ({
110
+ sql: `json_extract(metadata, '$."${handleKey(key)}"') NOT IN (${value.map(() => "?").join(",")})`,
111
+ needsValue: true
112
+ }),
113
+ $all: (key) => ({
114
+ sql: `json_extract(metadata, '$."${handleKey(key)}"') = ?`,
115
+ needsValue: true,
116
+ transformValue: (value) => {
117
+ const arrayValue = Array.isArray(value) ? value : [value];
118
+ if (arrayValue.length === 0) {
119
+ return {
120
+ sql: "1 = 0",
121
+ values: []
122
+ };
123
+ }
124
+ return {
125
+ sql: `(
126
+ CASE
127
+ WHEN ${validateJsonArray(key)} THEN
128
+ NOT EXISTS (
129
+ SELECT value
130
+ FROM json_each(?)
131
+ WHERE value NOT IN (
132
+ SELECT value
133
+ FROM json_each(json_extract(metadata, '$."${handleKey(key)}"'))
134
+ )
135
+ )
136
+ ELSE FALSE
137
+ END
138
+ )`,
139
+ values: [JSON.stringify(arrayValue)]
140
+ };
141
+ }
142
+ }),
143
+ $elemMatch: (key) => ({
144
+ sql: `json_extract(metadata, '$."${handleKey(key)}"') = ?`,
145
+ needsValue: true,
146
+ transformValue: (value) => {
147
+ if (typeof value !== "object" || Array.isArray(value)) {
148
+ throw new Error("$elemMatch requires an object with conditions");
149
+ }
150
+ const conditions = Object.entries(value).map(([field, fieldValue]) => {
151
+ if (field.startsWith("$")) {
152
+ const { sql, values } = buildCondition("elem.value", { [field]: fieldValue });
153
+ const pattern = /json_extract\(metadata, '\$\."[^"]*"(\."[^"]*")*'\)/g;
154
+ const elemSql = sql.replace(pattern, "elem.value");
155
+ return { sql: elemSql, values };
156
+ } else if (typeof fieldValue === "object" && !Array.isArray(fieldValue)) {
157
+ const { sql, values } = buildCondition(field, fieldValue);
158
+ const pattern = /json_extract\(metadata, '\$\."[^"]*"(\."[^"]*")*'\)/g;
159
+ const elemSql = sql.replace(pattern, `json_extract(elem.value, '$."${field}"')`);
160
+ return { sql: elemSql, values };
161
+ } else {
162
+ return {
163
+ sql: `json_extract(elem.value, '$."${field}"') = ?`,
164
+ values: [fieldValue]
165
+ };
166
+ }
167
+ });
168
+ return {
169
+ sql: `(
170
+ CASE
171
+ WHEN ${validateJsonArray(key)} THEN
172
+ EXISTS (
173
+ SELECT 1
174
+ FROM json_each(json_extract(metadata, '$."${handleKey(key)}"')) as elem
175
+ WHERE ${conditions.map((c) => c.sql).join(" AND ")}
176
+ )
177
+ ELSE FALSE
178
+ END
179
+ )`,
180
+ values: conditions.flatMap((c) => c.values)
181
+ };
182
+ }
183
+ }),
184
+ // Element Operators
185
+ $exists: (key) => ({
186
+ sql: `json_extract(metadata, '$."${handleKey(key)}"') IS NOT NULL`,
187
+ needsValue: false
188
+ }),
189
+ // Logical Operators
190
+ $and: (key) => ({
191
+ sql: `(${key})`,
192
+ needsValue: false
193
+ }),
194
+ $or: (key) => ({
195
+ sql: `(${key})`,
196
+ needsValue: false
197
+ }),
198
+ $not: (key) => ({ sql: `NOT (${key})`, needsValue: false }),
199
+ $nor: (key) => ({
200
+ sql: `NOT (${key})`,
201
+ needsValue: false
202
+ }),
203
+ $size: (key, paramIndex) => ({
204
+ sql: `(
205
+ CASE
206
+ WHEN json_type(json_extract(metadata, '$."${handleKey(key)}"')) = 'array' THEN
207
+ json_array_length(json_extract(metadata, '$."${handleKey(key)}"')) = $${paramIndex}
208
+ ELSE FALSE
209
+ END
210
+ )`,
211
+ needsValue: true
212
+ }),
213
+ // /**
214
+ // * Regex Operators
215
+ // * Supports case insensitive and multiline
216
+ // */
217
+ // $regex: (key: string): FilterOperator => ({
218
+ // sql: `json_extract(metadata, '$."${handleKey(key)}"') = ?`,
219
+ // needsValue: true,
220
+ // transformValue: (value: any) => {
221
+ // const pattern = typeof value === 'object' ? value.$regex : value;
222
+ // const options = typeof value === 'object' ? value.$options || '' : '';
223
+ // let sql = `json_extract(metadata, '$."${handleKey(key)}"')`;
224
+ // // Handle multiline
225
+ // // if (options.includes('m')) {
226
+ // // sql = `REPLACE(${sql}, CHAR(10), '\n')`;
227
+ // // }
228
+ // // let finalPattern = pattern;
229
+ // // if (options) {
230
+ // // finalPattern = `(\\?${options})${pattern}`;
231
+ // // }
232
+ // // // Handle case insensitivity
233
+ // // if (options.includes('i')) {
234
+ // // sql = `LOWER(${sql}) REGEXP LOWER(?)`;
235
+ // // } else {
236
+ // // sql = `${sql} REGEXP ?`;
237
+ // // }
238
+ // if (options.includes('m')) {
239
+ // sql = `EXISTS (
240
+ // SELECT 1
241
+ // FROM json_each(
242
+ // json_array(
243
+ // ${sql},
244
+ // REPLACE(${sql}, CHAR(10), CHAR(13))
245
+ // )
246
+ // ) as lines
247
+ // WHERE lines.value REGEXP ?
248
+ // )`;
249
+ // } else {
250
+ // sql = `${sql} REGEXP ?`;
251
+ // }
252
+ // // Handle case insensitivity
253
+ // if (options.includes('i')) {
254
+ // sql = sql.replace('REGEXP ?', 'REGEXP LOWER(?)');
255
+ // sql = sql.replace('value REGEXP', 'LOWER(value) REGEXP');
256
+ // }
257
+ // // Handle extended - allows whitespace and comments in pattern
258
+ // if (options.includes('x')) {
259
+ // // Remove whitespace and comments from pattern
260
+ // const cleanPattern = pattern.replace(/\s+|#.*$/gm, '');
261
+ // return {
262
+ // sql,
263
+ // values: [cleanPattern],
264
+ // };
265
+ // }
266
+ // return {
267
+ // sql,
268
+ // values: [pattern],
269
+ // };
270
+ // },
271
+ // }),
272
+ $contains: (key) => ({
273
+ sql: `json_extract(metadata, '$."${handleKey(key)}"') = ?`,
274
+ needsValue: true,
275
+ transformValue: (value) => {
276
+ if (Array.isArray(value)) {
277
+ return {
278
+ sql: `(
279
+ SELECT ${validateJsonArray(key)}
280
+ AND EXISTS (
281
+ SELECT 1
282
+ FROM json_each(json_extract(metadata, '$."${handleKey(key)}"')) as m
283
+ WHERE m.value IN (SELECT value FROM json_each(?))
284
+ )
285
+ )`,
286
+ values: [JSON.stringify(value)]
287
+ };
288
+ }
289
+ if (value && typeof value === "object") {
290
+ let traverse2 = function(obj, path = []) {
291
+ for (const [k, v] of Object.entries(obj)) {
292
+ const currentPath = [...path, k];
293
+ if (v && typeof v === "object" && !Array.isArray(v)) {
294
+ traverse2(v, currentPath);
295
+ } else {
296
+ paths.push(currentPath.join("."));
297
+ values.push(v);
298
+ }
299
+ }
300
+ };
301
+ const paths = [];
302
+ const values = [];
303
+ traverse2(value);
304
+ return {
305
+ sql: `(${paths.map((path) => `json_extract(metadata, '$."${handleKey(key)}"."${path}"') = ?`).join(" AND ")})`,
306
+ values
307
+ };
308
+ }
309
+ return value;
310
+ }
311
+ })
312
+ };
313
+ var handleKey = (key) => {
314
+ return key.replace(/\./g, '"."');
315
+ };
316
+ function buildFilterQuery(filter) {
317
+ if (!filter) {
318
+ return { sql: "", values: [] };
319
+ }
320
+ const values = [];
321
+ const conditions = Object.entries(filter).map(([key, value]) => {
322
+ const condition = buildCondition(key, value);
323
+ values.push(...condition.values);
324
+ return condition.sql;
325
+ }).join(" AND ");
326
+ return {
327
+ sql: conditions ? `WHERE ${conditions}` : "",
328
+ values
329
+ };
330
+ }
331
+ function buildCondition(key, value, parentPath) {
332
+ if (["$and", "$or", "$not", "$nor"].includes(key)) {
333
+ return handleLogicalOperator(key, value);
334
+ }
335
+ if (!value || typeof value !== "object") {
336
+ return {
337
+ sql: `json_extract(metadata, '$."${key.replace(/\./g, '"."')}"') = ?`,
338
+ values: [value]
339
+ };
340
+ }
341
+ return handleOperator(key, value);
342
+ }
343
+ function handleLogicalOperator(key, value, parentPath) {
344
+ if (!value || value.length === 0) {
345
+ switch (key) {
346
+ case "$and":
347
+ case "$nor":
348
+ return { sql: "true", values: [] };
349
+ case "$or":
350
+ return { sql: "false", values: [] };
351
+ case "$not":
352
+ throw new Error("$not operator cannot be empty");
353
+ default:
354
+ return { sql: "true", values: [] };
355
+ }
356
+ }
357
+ if (key === "$not") {
358
+ const entries = Object.entries(value);
359
+ const conditions2 = entries.map(([fieldKey, fieldValue]) => buildCondition(fieldKey, fieldValue));
360
+ return {
361
+ sql: `NOT (${conditions2.map((c) => c.sql).join(" AND ")})`,
362
+ values: conditions2.flatMap((c) => c.values)
363
+ };
364
+ }
365
+ const values = [];
366
+ const joinOperator = key === "$or" || key === "$nor" ? "OR" : "AND";
367
+ const conditions = Array.isArray(value) ? value.map((f) => {
368
+ const entries = Object.entries(f);
369
+ return entries.map(([k, v]) => buildCondition(k, v));
370
+ }) : [buildCondition(key, value)];
371
+ const joined = conditions.flat().map((c) => {
372
+ values.push(...c.values);
373
+ return c.sql;
374
+ }).join(` ${joinOperator} `);
375
+ return {
376
+ sql: key === "$nor" ? `NOT (${joined})` : `(${joined})`,
377
+ values
378
+ };
379
+ }
380
+ function handleOperator(key, value) {
381
+ if (typeof value === "object" && !Array.isArray(value)) {
382
+ const entries = Object.entries(value);
383
+ const results = entries.map(
384
+ ([operator2, operatorValue2]) => operator2 === "$not" ? {
385
+ sql: `NOT (${Object.entries(operatorValue2).map(([op, val]) => processOperator(key, op, val).sql).join(" AND ")})`,
386
+ values: Object.entries(operatorValue2).flatMap(
387
+ ([op, val]) => processOperator(key, op, val).values
388
+ )
389
+ } : processOperator(key, operator2, operatorValue2)
390
+ );
391
+ return {
392
+ sql: `(${results.map((r) => r.sql).join(" AND ")})`,
393
+ values: results.flatMap((r) => r.values)
394
+ };
395
+ }
396
+ const [[operator, operatorValue] = []] = Object.entries(value);
397
+ return processOperator(key, operator, operatorValue);
398
+ }
399
+ var processOperator = (key, operator, operatorValue) => {
400
+ if (!operator.startsWith("$") || !FILTER_OPERATORS[operator]) {
401
+ throw new Error(`Invalid operator: ${operator}`);
402
+ }
403
+ const operatorFn = FILTER_OPERATORS[operator];
404
+ const operatorResult = operatorFn(key, operatorValue);
405
+ if (!operatorResult.needsValue) {
406
+ return { sql: operatorResult.sql, values: [] };
407
+ }
408
+ const transformed = operatorResult.transformValue ? operatorResult.transformValue(operatorValue) : operatorValue;
409
+ if (transformed && typeof transformed === "object" && "sql" in transformed) {
410
+ return transformed;
411
+ }
412
+ return {
413
+ sql: operatorResult.sql,
414
+ values: Array.isArray(transformed) ? transformed : [transformed]
415
+ };
416
+ };
417
+
418
+ // src/vector/libsql/index.ts
419
+ var LibSQLVector = class extends chunkENT7U27Y_cjs.MastraVector {
420
+ turso;
421
+ constructor({
422
+ connectionUrl,
423
+ authToken,
424
+ syncUrl,
425
+ syncInterval
426
+ }) {
427
+ super();
428
+ this.turso = client.createClient({
429
+ url: this.rewriteDbUrl(connectionUrl),
430
+ syncUrl,
431
+ authToken,
432
+ syncInterval
433
+ });
434
+ }
435
+ // If we're in the .mastra/output directory, use the dir outside .mastra dir
436
+ // reason we need to do this is libsql relative file paths are based on cwd, not current file path
437
+ // since mastra dev sets cwd to .mastra/output this means running an agent directly vs running with mastra dev
438
+ // will put db files in different locations, leading to an inconsistent experience between the two.
439
+ // Ex: with `file:ex.db`
440
+ // 1. `mastra dev`: ${cwd}/.mastra/output/ex.db
441
+ // 2. `tsx src/index.ts`: ${cwd}/ex.db
442
+ // so if we're in .mastra/output we need to rewrite the file url to be relative to the project root dir
443
+ // or the experience will be inconsistent
444
+ // this means `file:` urls are always relative to project root
445
+ // TODO: can we make this easier via bundling? https://github.com/mastra-ai/mastra/pull/2783#pullrequestreview-2662444241
446
+ rewriteDbUrl(url) {
447
+ if (url.startsWith("file:")) {
448
+ const pathPart = url.slice("file:".length);
449
+ if (path.isAbsolute(pathPart)) {
450
+ return url;
451
+ }
452
+ const cwd = process.cwd();
453
+ if (cwd.includes(".mastra") && (cwd.endsWith(`output`) || cwd.endsWith(`output/`) || cwd.endsWith(`output\\`))) {
454
+ const baseDir = path.join(cwd, `..`, `..`);
455
+ const fullPath = path.resolve(baseDir, pathPart);
456
+ this.logger.debug(
457
+ `Initializing LibSQL db with url ${url} with relative file path from inside .mastra/output directory. Rewriting relative file url to "file:${fullPath}". This ensures it's outside the .mastra/output directory.`
458
+ );
459
+ return `file:${fullPath}`;
460
+ }
461
+ }
462
+ return url;
463
+ }
464
+ transformFilter(filter) {
465
+ const translator = new LibSQLFilterTranslator();
466
+ return translator.translate(filter);
467
+ }
468
+ async query(...args) {
469
+ const params = this.normalizeArgs("query", args, ["minScore"]);
470
+ try {
471
+ const { indexName, queryVector, topK = 10, filter, includeVector = false, minScore = 0 } = params;
472
+ const vectorStr = `[${queryVector.join(",")}]`;
473
+ const translatedFilter = this.transformFilter(filter);
474
+ const { sql: filterQuery, values: filterValues } = buildFilterQuery(translatedFilter);
475
+ filterValues.push(minScore);
476
+ const query = `
477
+ WITH vector_scores AS (
478
+ SELECT
479
+ vector_id as id,
480
+ (1-vector_distance_cos(embedding, '${vectorStr}')) as score,
481
+ metadata
482
+ ${includeVector ? ", vector_extract(embedding) as embedding" : ""}
483
+ FROM ${indexName}
484
+ ${filterQuery}
485
+ )
486
+ SELECT *
487
+ FROM vector_scores
488
+ WHERE score > ?
489
+ ORDER BY score DESC
490
+ LIMIT ${topK}`;
491
+ const result = await this.turso.execute({
492
+ sql: query,
493
+ args: filterValues
494
+ });
495
+ return result.rows.map(({ id, score, metadata, embedding }) => ({
496
+ id,
497
+ score,
498
+ metadata: JSON.parse(metadata ?? "{}"),
499
+ ...includeVector && embedding && { vector: JSON.parse(embedding) }
500
+ }));
501
+ } finally {
502
+ }
503
+ }
504
+ async upsert(...args) {
505
+ const params = this.normalizeArgs("upsert", args);
506
+ const { indexName, vectors, metadata, ids } = params;
507
+ const tx = await this.turso.transaction("write");
508
+ try {
509
+ const vectorIds = ids || vectors.map(() => crypto.randomUUID());
510
+ for (let i = 0; i < vectors.length; i++) {
511
+ const query = `
512
+ INSERT INTO ${indexName} (vector_id, embedding, metadata)
513
+ VALUES (?, vector32(?), ?)
514
+ ON CONFLICT(vector_id) DO UPDATE SET
515
+ embedding = vector32(?),
516
+ metadata = ?
517
+ `;
518
+ await tx.execute({
519
+ sql: query,
520
+ // @ts-ignore
521
+ args: [
522
+ vectorIds[i],
523
+ JSON.stringify(vectors[i]),
524
+ JSON.stringify(metadata?.[i] || {}),
525
+ JSON.stringify(vectors[i]),
526
+ JSON.stringify(metadata?.[i] || {})
527
+ ]
528
+ });
529
+ }
530
+ await tx.commit();
531
+ return vectorIds;
532
+ } catch (error) {
533
+ await tx.rollback();
534
+ throw error;
535
+ }
536
+ }
537
+ async createIndex(...args) {
538
+ const params = this.normalizeArgs("createIndex", args);
539
+ const { indexName, dimension } = params;
540
+ try {
541
+ if (!indexName.match(/^[a-zA-Z_][a-zA-Z0-9_]*$/)) {
542
+ throw new Error("Invalid index name format");
543
+ }
544
+ if (!Number.isInteger(dimension) || dimension <= 0) {
545
+ throw new Error("Dimension must be a positive integer");
546
+ }
547
+ await this.turso.execute({
548
+ sql: `
549
+ CREATE TABLE IF NOT EXISTS ${indexName} (
550
+ id SERIAL PRIMARY KEY,
551
+ vector_id TEXT UNIQUE NOT NULL,
552
+ embedding F32_BLOB(${dimension}),
553
+ metadata TEXT DEFAULT '{}'
554
+ );
555
+ `,
556
+ args: []
557
+ });
558
+ await this.turso.execute({
559
+ sql: `
560
+ CREATE INDEX IF NOT EXISTS ${indexName}_vector_idx
561
+ ON ${indexName} (libsql_vector_idx(embedding))
562
+ `,
563
+ args: []
564
+ });
565
+ } catch (error) {
566
+ console.error("Failed to create vector table:", error);
567
+ throw error;
568
+ } finally {
569
+ }
570
+ }
571
+ async deleteIndex(indexName) {
572
+ try {
573
+ await this.turso.execute({
574
+ sql: `DROP TABLE IF EXISTS ${indexName}`,
575
+ args: []
576
+ });
577
+ } catch (error) {
578
+ console.error("Failed to delete vector table:", error);
579
+ throw new Error(`Failed to delete vector table: ${error.message}`);
580
+ } finally {
581
+ }
582
+ }
583
+ async listIndexes() {
584
+ try {
585
+ const vectorTablesQuery = `
586
+ SELECT name FROM sqlite_master
587
+ WHERE type='table'
588
+ AND sql LIKE '%F32_BLOB%';
589
+ `;
590
+ const result = await this.turso.execute({
591
+ sql: vectorTablesQuery,
592
+ args: []
593
+ });
594
+ return result.rows.map((row) => row.name);
595
+ } catch (error) {
596
+ throw new Error(`Failed to list vector tables: ${error.message}`);
597
+ }
598
+ }
599
+ async describeIndex(indexName) {
600
+ try {
601
+ const tableInfoQuery = `
602
+ SELECT sql
603
+ FROM sqlite_master
604
+ WHERE type='table'
605
+ AND name = ?;
606
+ `;
607
+ const tableInfo = await this.turso.execute({
608
+ sql: tableInfoQuery,
609
+ args: [indexName]
610
+ });
611
+ if (!tableInfo.rows[0]?.sql) {
612
+ throw new Error(`Table ${indexName} not found`);
613
+ }
614
+ const dimension = parseInt(tableInfo.rows[0].sql.match(/F32_BLOB\((\d+)\)/)?.[1] || "0");
615
+ const countQuery = `
616
+ SELECT COUNT(*) as count
617
+ FROM ${indexName};
618
+ `;
619
+ const countResult = await this.turso.execute({
620
+ sql: countQuery,
621
+ args: []
622
+ });
623
+ const metric = "cosine";
624
+ return {
625
+ dimension,
626
+ count: countResult?.rows?.[0]?.count ?? 0,
627
+ metric
628
+ };
629
+ } catch (e) {
630
+ throw new Error(`Failed to describe vector table: ${e.message}`);
631
+ }
632
+ }
633
+ /**
634
+ * Updates an index entry by its ID with the provided vector and/or metadata.
635
+ *
636
+ * @param indexName - The name of the index to update.
637
+ * @param id - The ID of the index entry to update.
638
+ * @param update - An object containing the vector and/or metadata to update.
639
+ * @param update.vector - An optional array of numbers representing the new vector.
640
+ * @param update.metadata - An optional record containing the new metadata.
641
+ * @returns A promise that resolves when the update is complete.
642
+ * @throws Will throw an error if no updates are provided or if the update operation fails.
643
+ */
644
+ async updateIndexById(indexName, id, update) {
645
+ try {
646
+ const updates = [];
647
+ const args = [];
648
+ if (update.vector) {
649
+ updates.push("embedding = vector32(?)");
650
+ args.push(JSON.stringify(update.vector));
651
+ }
652
+ if (update.metadata) {
653
+ updates.push("metadata = ?");
654
+ args.push(JSON.stringify(update.metadata));
655
+ }
656
+ if (updates.length === 0) {
657
+ throw new Error("No updates provided");
658
+ }
659
+ args.push(id);
660
+ const query = `
661
+ UPDATE ${indexName}
662
+ SET ${updates.join(", ")}
663
+ WHERE vector_id = ?;
664
+ `;
665
+ await this.turso.execute({
666
+ sql: query,
667
+ args
668
+ });
669
+ } catch (error) {
670
+ throw new Error(`Failed to update index by id: ${id} for index: ${indexName}: ${error.message}`);
671
+ }
672
+ }
673
+ async deleteIndexById(indexName, id) {
674
+ try {
675
+ await this.turso.execute({
676
+ sql: `DELETE FROM ${indexName} WHERE vector_id = ?`,
677
+ args: [id]
678
+ });
679
+ } catch (error) {
680
+ throw new Error(`Failed to delete index by id: ${id} for index: ${indexName}: ${error.message}`);
681
+ }
682
+ }
683
+ async truncateIndex(indexName) {
684
+ await this.turso.execute({
685
+ sql: `DELETE FROM ${indexName}`,
686
+ args: []
687
+ });
688
+ }
689
+ };
690
+
691
+ exports.LibSQLVector = LibSQLVector;