@mastra/core 0.0.0-commonjs-20250227130920

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 (137) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +180 -0
  3. package/dist/agent/index.cjs +1865 -0
  4. package/dist/agent/index.d.cts +15 -0
  5. package/dist/agent/index.d.ts +15 -0
  6. package/dist/agent/index.js +1 -0
  7. package/dist/base-D90KQ4XI.d.ts +139 -0
  8. package/dist/base-hs9NDAZ2.d.cts +139 -0
  9. package/dist/base-nKCMCNrM.d.ts +920 -0
  10. package/dist/base-nhesrHv3.d.cts +920 -0
  11. package/dist/base.cjs +138 -0
  12. package/dist/base.d.cts +6 -0
  13. package/dist/base.d.ts +6 -0
  14. package/dist/base.js +1 -0
  15. package/dist/bundler/index.cjs +158 -0
  16. package/dist/bundler/index.d.cts +28 -0
  17. package/dist/bundler/index.d.ts +28 -0
  18. package/dist/bundler/index.js +1 -0
  19. package/dist/chunk-33GSTUNK.js +620 -0
  20. package/dist/chunk-4YRYBCOZ.js +10 -0
  21. package/dist/chunk-55NFNRKO.js +10 -0
  22. package/dist/chunk-5XPCMNGW.js +215 -0
  23. package/dist/chunk-B3M27AMP.js +1479 -0
  24. package/dist/chunk-BB4KXGBU.js +83 -0
  25. package/dist/chunk-C6A6W6XS.js +77 -0
  26. package/dist/chunk-HQ55LN2U.js +318 -0
  27. package/dist/chunk-KNVTCZW7.js +416 -0
  28. package/dist/chunk-LH47WVJL.js +61 -0
  29. package/dist/chunk-NGD2HQYW.js +346 -0
  30. package/dist/chunk-NUDAZEOG.js +35 -0
  31. package/dist/chunk-OZ4XVJ6F.js +49 -0
  32. package/dist/chunk-PHMSPCTC.js +145 -0
  33. package/dist/chunk-PNZK456O.js +88 -0
  34. package/dist/chunk-QAAJAHDB.js +37 -0
  35. package/dist/chunk-RG66XEJT.js +8 -0
  36. package/dist/chunk-SIFBBGY6.js +190 -0
  37. package/dist/chunk-SVEAENO7.js +22 -0
  38. package/dist/chunk-SY5244IR.js +1499 -0
  39. package/dist/chunk-W5HVJX45.js +402 -0
  40. package/dist/chunk-WIBGG4X6.js +173 -0
  41. package/dist/chunk-ZDWFBE5L.js +1 -0
  42. package/dist/chunk-ZINPRHAN.js +22 -0
  43. package/dist/deployer/index.cjs +165 -0
  44. package/dist/deployer/index.d.cts +19 -0
  45. package/dist/deployer/index.d.ts +19 -0
  46. package/dist/deployer/index.js +1 -0
  47. package/dist/eval/index.cjs +110 -0
  48. package/dist/eval/index.d.cts +28 -0
  49. package/dist/eval/index.d.ts +28 -0
  50. package/dist/eval/index.js +1 -0
  51. package/dist/filter/index.cjs +192 -0
  52. package/dist/filter/index.d.cts +90 -0
  53. package/dist/filter/index.d.ts +90 -0
  54. package/dist/filter/index.js +1 -0
  55. package/dist/hooks/index.cjs +87 -0
  56. package/dist/hooks/index.d.cts +33 -0
  57. package/dist/hooks/index.d.ts +33 -0
  58. package/dist/hooks/index.js +1 -0
  59. package/dist/index-mKY1XrpK.d.cts +90 -0
  60. package/dist/index-mKY1XrpK.d.ts +90 -0
  61. package/dist/index.cjs +6844 -0
  62. package/dist/index.d.cts +97 -0
  63. package/dist/index.d.ts +97 -0
  64. package/dist/index.js +119 -0
  65. package/dist/integration/index.cjs +113 -0
  66. package/dist/integration/index.d.cts +52 -0
  67. package/dist/integration/index.d.ts +52 -0
  68. package/dist/integration/index.js +1 -0
  69. package/dist/llm/index.cjs +2 -0
  70. package/dist/llm/index.d.cts +15 -0
  71. package/dist/llm/index.d.ts +15 -0
  72. package/dist/llm/index.js +1 -0
  73. package/dist/logger/index.cjs +159 -0
  74. package/dist/logger/index.d.cts +3 -0
  75. package/dist/logger/index.d.ts +3 -0
  76. package/dist/logger/index.js +1 -0
  77. package/dist/mastra/index.cjs +1741 -0
  78. package/dist/mastra/index.d.cts +67 -0
  79. package/dist/mastra/index.d.ts +67 -0
  80. package/dist/mastra/index.js +1 -0
  81. package/dist/memory/index.cjs +1907 -0
  82. package/dist/memory/index.d.cts +15 -0
  83. package/dist/memory/index.d.ts +15 -0
  84. package/dist/memory/index.js +1 -0
  85. package/dist/relevance/index.cjs +1927 -0
  86. package/dist/relevance/index.d.cts +21 -0
  87. package/dist/relevance/index.d.ts +21 -0
  88. package/dist/relevance/index.js +1 -0
  89. package/dist/storage/index.cjs +361 -0
  90. package/dist/storage/index.d.cts +15 -0
  91. package/dist/storage/index.d.ts +15 -0
  92. package/dist/storage/index.js +2 -0
  93. package/dist/storage/libsql/index.cjs +770 -0
  94. package/dist/storage/libsql/index.d.cts +81 -0
  95. package/dist/storage/libsql/index.d.ts +81 -0
  96. package/dist/storage/libsql/index.js +1 -0
  97. package/dist/telemetry/index.cjs +413 -0
  98. package/dist/telemetry/index.d.cts +51 -0
  99. package/dist/telemetry/index.d.ts +51 -0
  100. package/dist/telemetry/index.js +1 -0
  101. package/dist/telemetry/otel-vendor.cjs +52 -0
  102. package/dist/telemetry/otel-vendor.d.cts +7 -0
  103. package/dist/telemetry/otel-vendor.d.ts +7 -0
  104. package/dist/telemetry/otel-vendor.js +7 -0
  105. package/dist/tools/index.cjs +25 -0
  106. package/dist/tools/index.d.cts +29 -0
  107. package/dist/tools/index.d.ts +29 -0
  108. package/dist/tools/index.js +1 -0
  109. package/dist/tts/index.cjs +328 -0
  110. package/dist/tts/index.d.cts +28 -0
  111. package/dist/tts/index.d.ts +28 -0
  112. package/dist/tts/index.js +1 -0
  113. package/dist/types-m9RryK9a.d.cts +14 -0
  114. package/dist/types-m9RryK9a.d.ts +14 -0
  115. package/dist/utils.cjs +179 -0
  116. package/dist/utils.d.cts +26 -0
  117. package/dist/utils.d.ts +26 -0
  118. package/dist/utils.js +1 -0
  119. package/dist/vector/index.cjs +145 -0
  120. package/dist/vector/index.d.cts +30 -0
  121. package/dist/vector/index.d.ts +30 -0
  122. package/dist/vector/index.js +1 -0
  123. package/dist/vector/libsql/index.cjs +951 -0
  124. package/dist/vector/libsql/index.d.cts +29 -0
  125. package/dist/vector/libsql/index.d.ts +29 -0
  126. package/dist/vector/libsql/index.js +1 -0
  127. package/dist/voice/index.cjs +369 -0
  128. package/dist/voice/index.d.cts +67 -0
  129. package/dist/voice/index.d.ts +67 -0
  130. package/dist/voice/index.js +76 -0
  131. package/dist/workflow-DqQ4pON_.d.cts +84 -0
  132. package/dist/workflow-Ng_F_Zaf.d.ts +84 -0
  133. package/dist/workflows/index.cjs +1628 -0
  134. package/dist/workflows/index.d.cts +48 -0
  135. package/dist/workflows/index.d.ts +48 -0
  136. package/dist/workflows/index.js +1 -0
  137. package/package.json +162 -0
@@ -0,0 +1,1907 @@
1
+ 'use strict';
2
+
3
+ var stream = require('stream');
4
+ var pino = require('pino');
5
+ var pretty = require('pino-pretty');
6
+ var path = require('path');
7
+ var client = require('@libsql/client');
8
+ var ai = require('ai');
9
+ var node_modulesPath = require('node_modules-path');
10
+
11
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
12
+
13
+ var pino__default = /*#__PURE__*/_interopDefault(pino);
14
+ var pretty__default = /*#__PURE__*/_interopDefault(pretty);
15
+ var path__default = /*#__PURE__*/_interopDefault(path);
16
+ var node_modulesPath__default = /*#__PURE__*/_interopDefault(node_modulesPath);
17
+
18
+ // src/logger/index.ts
19
+ var RegisteredLogger = {
20
+ LLM: "LLM"};
21
+ var LogLevel = {
22
+ INFO: "info"};
23
+ var Logger = class {
24
+ logger;
25
+ transports;
26
+ constructor(options = {}) {
27
+ this.transports = options.transports || {};
28
+ const transportsAry = Object.entries(this.transports);
29
+ this.logger = pino__default.default(
30
+ {
31
+ name: options.name || "app",
32
+ level: options.level || LogLevel.INFO
33
+ },
34
+ options.overrideDefaultTransports ? options?.transports?.default : transportsAry.length === 0 ? pretty__default.default({
35
+ colorize: true,
36
+ levelFirst: true,
37
+ ignore: "pid,hostname",
38
+ colorizeObjects: true,
39
+ translateTime: "SYS:standard",
40
+ singleLine: false
41
+ }) : pino__default.default.multistream([
42
+ ...transportsAry.map(([_, transport]) => ({
43
+ stream: transport,
44
+ level: options.level || LogLevel.INFO
45
+ })),
46
+ {
47
+ stream: pretty__default.default({
48
+ colorize: true,
49
+ levelFirst: true,
50
+ ignore: "pid,hostname",
51
+ colorizeObjects: true,
52
+ translateTime: "SYS:standard",
53
+ singleLine: false
54
+ }),
55
+ level: options.level || LogLevel.INFO
56
+ }
57
+ ])
58
+ );
59
+ }
60
+ debug(message, args = {}) {
61
+ this.logger.debug(args, message);
62
+ }
63
+ info(message, args = {}) {
64
+ this.logger.info(args, message);
65
+ }
66
+ warn(message, args = {}) {
67
+ this.logger.warn(args, message);
68
+ }
69
+ error(message, args = {}) {
70
+ this.logger.error(args, message);
71
+ }
72
+ // Stream creation for process output handling
73
+ createStream() {
74
+ return new stream.Transform({
75
+ transform: (chunk, _encoding, callback) => {
76
+ const line = chunk.toString().trim();
77
+ if (line) {
78
+ this.info(line);
79
+ }
80
+ callback(null, chunk);
81
+ }
82
+ });
83
+ }
84
+ async getLogs(transportId) {
85
+ if (!transportId || !this.transports[transportId]) {
86
+ return [];
87
+ }
88
+ return this.transports[transportId].getLogs();
89
+ }
90
+ async getLogsByRunId({ runId, transportId }) {
91
+ return this.transports[transportId]?.getLogsByRunId({ runId });
92
+ }
93
+ };
94
+ function createLogger(options) {
95
+ return new Logger(options);
96
+ }
97
+
98
+ // src/base.ts
99
+ var MastraBase = class {
100
+ component = RegisteredLogger.LLM;
101
+ logger;
102
+ name;
103
+ telemetry;
104
+ constructor({ component, name }) {
105
+ this.component = component || RegisteredLogger.LLM;
106
+ this.name = name;
107
+ this.logger = createLogger({ name: `${this.component} - ${this.name}` });
108
+ }
109
+ /**
110
+ * Set the logger for the agent
111
+ * @param logger
112
+ */
113
+ __setLogger(logger) {
114
+ this.logger = logger;
115
+ this.logger.debug(`Logger updated [component=${this.component}] [name=${this.name}]`);
116
+ }
117
+ /**
118
+ * Set the telemetry for the
119
+ * @param telemetry
120
+ */
121
+ __setTelemetry(telemetry) {
122
+ this.telemetry = telemetry;
123
+ this.logger.debug(`Telemetry updated [component=${this.component}] [tracer=${this.telemetry.tracer}]`);
124
+ }
125
+ /**
126
+ * Get the telemetry on the vector
127
+ * @returns telemetry
128
+ */
129
+ __getTelemetry() {
130
+ return this.telemetry;
131
+ }
132
+ /*
133
+ get experimental_telemetry config
134
+ */
135
+ get experimental_telemetry() {
136
+ return this.telemetry ? {
137
+ // tracer: this.telemetry.tracer,
138
+ tracer: this.telemetry.getBaggageTracer(),
139
+ isEnabled: !!this.telemetry.tracer
140
+ } : void 0;
141
+ }
142
+ };
143
+
144
+ // src/storage/constants.ts
145
+ var TABLE_WORKFLOW_SNAPSHOT = "mastra_workflow_snapshot";
146
+ var TABLE_EVALS = "mastra_evals";
147
+ var TABLE_MESSAGES = "mastra_messages";
148
+ var TABLE_THREADS = "mastra_threads";
149
+ var TABLE_TRACES = "mastra_traces";
150
+
151
+ // src/storage/base.ts
152
+ var MastraStorage = class extends MastraBase {
153
+ /** @deprecated import from { TABLE_WORKFLOW_SNAPSHOT } '@mastra/core/storage' instead */
154
+ static TABLE_WORKFLOW_SNAPSHOT = TABLE_WORKFLOW_SNAPSHOT;
155
+ /** @deprecated import from { TABLE_EVALS } '@mastra/core/storage' instead */
156
+ static TABLE_EVALS = TABLE_EVALS;
157
+ /** @deprecated import from { TABLE_MESSAGES } '@mastra/core/storage' instead */
158
+ static TABLE_MESSAGES = TABLE_MESSAGES;
159
+ /** @deprecated import from { TABLE_THREADS } '@mastra/core/storage' instead */
160
+ static TABLE_THREADS = TABLE_THREADS;
161
+ /** @deprecated import { TABLE_TRACES } from '@mastra/core/storage' instead */
162
+ static TABLE_TRACES = TABLE_TRACES;
163
+ hasInitialized = null;
164
+ constructor({ name }) {
165
+ super({
166
+ component: "STORAGE",
167
+ name
168
+ });
169
+ }
170
+ async __batchInsert({
171
+ tableName,
172
+ records
173
+ }) {
174
+ await this.init();
175
+ return this.batchInsert({ tableName, records });
176
+ }
177
+ async __getThreadById({ threadId }) {
178
+ await this.init();
179
+ return this.getThreadById({ threadId });
180
+ }
181
+ async __getThreadsByResourceId({ resourceId }) {
182
+ await this.init();
183
+ return this.getThreadsByResourceId({ resourceId });
184
+ }
185
+ async __saveThread({ thread }) {
186
+ await this.init();
187
+ return this.saveThread({ thread });
188
+ }
189
+ async __updateThread({
190
+ id,
191
+ title,
192
+ metadata
193
+ }) {
194
+ await this.init();
195
+ return this.updateThread({ id, title, metadata });
196
+ }
197
+ async __deleteThread({ threadId }) {
198
+ await this.init();
199
+ return this.deleteThread({ threadId });
200
+ }
201
+ async __getMessages({ threadId, selectBy, threadConfig }) {
202
+ await this.init();
203
+ return this.getMessages({ threadId, selectBy, threadConfig });
204
+ }
205
+ async __saveMessages({ messages }) {
206
+ await this.init();
207
+ return this.saveMessages({ messages });
208
+ }
209
+ async __getTraces({
210
+ scope,
211
+ page,
212
+ perPage,
213
+ attributes
214
+ }) {
215
+ await this.init();
216
+ return this.getTraces({ scope, page, perPage, attributes });
217
+ }
218
+ async init() {
219
+ if (await this.hasInitialized) {
220
+ return;
221
+ }
222
+ this.hasInitialized = Promise.all([
223
+ this.createTable({
224
+ tableName: TABLE_WORKFLOW_SNAPSHOT,
225
+ schema: {
226
+ workflow_name: {
227
+ type: "text"
228
+ },
229
+ run_id: {
230
+ type: "text"
231
+ },
232
+ snapshot: {
233
+ type: "text"
234
+ },
235
+ createdAt: {
236
+ type: "timestamp"
237
+ },
238
+ updatedAt: {
239
+ type: "timestamp"
240
+ }
241
+ }
242
+ }),
243
+ this.createTable({
244
+ tableName: TABLE_EVALS,
245
+ schema: {
246
+ input: {
247
+ type: "text"
248
+ },
249
+ output: {
250
+ type: "text"
251
+ },
252
+ result: {
253
+ type: "jsonb"
254
+ },
255
+ agent_name: {
256
+ type: "text"
257
+ },
258
+ metric_name: {
259
+ type: "text"
260
+ },
261
+ instructions: {
262
+ type: "text"
263
+ },
264
+ test_info: {
265
+ type: "jsonb",
266
+ nullable: true
267
+ },
268
+ global_run_id: {
269
+ type: "text"
270
+ },
271
+ run_id: {
272
+ type: "text"
273
+ },
274
+ created_at: {
275
+ type: "timestamp"
276
+ }
277
+ }
278
+ }),
279
+ this.createTable({
280
+ tableName: TABLE_THREADS,
281
+ schema: {
282
+ id: { type: "text", nullable: false, primaryKey: true },
283
+ resourceId: { type: "text", nullable: false },
284
+ title: { type: "text", nullable: false },
285
+ metadata: { type: "text", nullable: true },
286
+ createdAt: { type: "timestamp", nullable: false },
287
+ updatedAt: { type: "timestamp", nullable: false }
288
+ }
289
+ }),
290
+ this.createTable({
291
+ tableName: TABLE_MESSAGES,
292
+ schema: {
293
+ id: { type: "text", nullable: false, primaryKey: true },
294
+ thread_id: { type: "text", nullable: false },
295
+ content: { type: "text", nullable: false },
296
+ role: { type: "text", nullable: false },
297
+ type: { type: "text", nullable: false },
298
+ createdAt: { type: "timestamp", nullable: false }
299
+ }
300
+ }),
301
+ this.createTable({
302
+ tableName: TABLE_TRACES,
303
+ schema: {
304
+ id: { type: "text", nullable: false, primaryKey: true },
305
+ parentSpanId: { type: "text", nullable: true },
306
+ name: { type: "text", nullable: false },
307
+ traceId: { type: "text", nullable: false },
308
+ scope: { type: "text", nullable: false },
309
+ kind: { type: "integer", nullable: false },
310
+ attributes: { type: "jsonb", nullable: true },
311
+ status: { type: "jsonb", nullable: true },
312
+ events: { type: "jsonb", nullable: true },
313
+ links: { type: "jsonb", nullable: true },
314
+ other: { type: "text", nullable: true },
315
+ startTime: { type: "bigint", nullable: false },
316
+ endTime: { type: "bigint", nullable: false },
317
+ createdAt: { type: "timestamp", nullable: false }
318
+ }
319
+ })
320
+ ]).then(() => true);
321
+ await this.hasInitialized;
322
+ }
323
+ async persistWorkflowSnapshot({
324
+ workflowName,
325
+ runId,
326
+ snapshot
327
+ }) {
328
+ await this.init();
329
+ const data = {
330
+ workflow_name: workflowName,
331
+ run_id: runId,
332
+ snapshot,
333
+ createdAt: /* @__PURE__ */ new Date(),
334
+ updatedAt: /* @__PURE__ */ new Date()
335
+ };
336
+ this.logger.debug("Persisting workflow snapshot", { workflowName, runId, data });
337
+ await this.insert({
338
+ tableName: TABLE_WORKFLOW_SNAPSHOT,
339
+ record: data
340
+ });
341
+ }
342
+ async loadWorkflowSnapshot({
343
+ workflowName,
344
+ runId
345
+ }) {
346
+ if (!this.hasInitialized) {
347
+ await this.init();
348
+ }
349
+ this.logger.debug("Loading workflow snapshot", { workflowName, runId });
350
+ const d = await this.load({
351
+ tableName: TABLE_WORKFLOW_SNAPSHOT,
352
+ keys: { workflow_name: workflowName, run_id: runId }
353
+ });
354
+ return d ? d.snapshot : null;
355
+ }
356
+ async __getEvalsByAgentName(agentName, type) {
357
+ await this.init();
358
+ return this.getEvalsByAgentName(agentName, type);
359
+ }
360
+ };
361
+
362
+ // src/storage/libsql/index.ts
363
+ function safelyParseJSON(jsonString) {
364
+ try {
365
+ return JSON.parse(jsonString);
366
+ } catch {
367
+ return {};
368
+ }
369
+ }
370
+ var LibSQLStore = class extends MastraStorage {
371
+ client;
372
+ constructor({ config }) {
373
+ super({ name: `LibSQLStore` });
374
+ this.client = client.createClient({
375
+ url: this.rewriteDbUrl(config.url),
376
+ authToken: config.authToken
377
+ });
378
+ }
379
+ rewriteDbUrl(url) {
380
+ if (url.startsWith("file:") && !url.startsWith("file:/")) {
381
+ const cwd = process.cwd();
382
+ const relativePath = url.slice("file:".length);
383
+ if (cwd.endsWith(".mastra") || cwd.endsWith(".mastra/")) {
384
+ const baseDir = path.join(cwd, `..`);
385
+ const fullPath = path.join(baseDir, relativePath);
386
+ this.logger.debug(
387
+ `Initializing LibSQL db with url ${url} with relative file path from inside .mastra directory. Rewriting relative file url to "file:${fullPath}". This ensures it's outside the .mastra directory. If the db is stored inside .mastra it will be deleted when Mastra re-bundles code.`
388
+ );
389
+ return `file:${fullPath}`;
390
+ }
391
+ }
392
+ return url;
393
+ }
394
+ getCreateTableSQL(tableName, schema) {
395
+ const columns = Object.entries(schema).map(([name, col]) => {
396
+ let type = col.type.toUpperCase();
397
+ if (type === "TEXT") type = "TEXT";
398
+ if (type === "TIMESTAMP") type = "TEXT";
399
+ const nullable = col.nullable ? "" : "NOT NULL";
400
+ const primaryKey = col.primaryKey ? "PRIMARY KEY" : "";
401
+ return `${name} ${type} ${nullable} ${primaryKey}`.trim();
402
+ });
403
+ if (tableName === TABLE_WORKFLOW_SNAPSHOT) {
404
+ const stmnt = `CREATE TABLE IF NOT EXISTS ${tableName} (
405
+ ${columns.join(",\n")},
406
+ PRIMARY KEY (workflow_name, run_id)
407
+ )`;
408
+ return stmnt;
409
+ }
410
+ return `CREATE TABLE IF NOT EXISTS ${tableName} (${columns.join(", ")})`;
411
+ }
412
+ async createTable({
413
+ tableName,
414
+ schema
415
+ }) {
416
+ try {
417
+ this.logger.debug(`Creating database table`, { tableName, operation: "schema init" });
418
+ const sql = this.getCreateTableSQL(tableName, schema);
419
+ await this.client.execute(sql);
420
+ } catch (error) {
421
+ this.logger.error(`Error creating table ${tableName}: ${error}`);
422
+ throw error;
423
+ }
424
+ }
425
+ async clearTable({ tableName }) {
426
+ try {
427
+ await this.client.execute(`DELETE FROM ${tableName}`);
428
+ } catch (e) {
429
+ if (e instanceof Error) {
430
+ this.logger.error(e.message);
431
+ }
432
+ }
433
+ }
434
+ prepareStatement({ tableName, record }) {
435
+ const columns = Object.keys(record);
436
+ const values = Object.values(record).map((v) => {
437
+ if (typeof v === `undefined`) {
438
+ return null;
439
+ }
440
+ return typeof v === "object" ? JSON.stringify(v) : v;
441
+ });
442
+ const placeholders = values.map(() => "?").join(", ");
443
+ return {
444
+ sql: `INSERT OR REPLACE INTO ${tableName} (${columns.join(", ")}) VALUES (${placeholders})`,
445
+ args: values
446
+ };
447
+ }
448
+ async insert({ tableName, record }) {
449
+ try {
450
+ await this.client.execute(this.prepareStatement({ tableName, record }));
451
+ } catch (error) {
452
+ this.logger.error(`Error upserting into table ${tableName}: ${error}`);
453
+ throw error;
454
+ }
455
+ }
456
+ async batchInsert({ tableName, records }) {
457
+ if (records.length === 0) return;
458
+ try {
459
+ const batchStatements = records.map((r) => this.prepareStatement({ tableName, record: r }));
460
+ await this.client.batch(batchStatements, "write");
461
+ } catch (error) {
462
+ this.logger.error(`Error upserting into table ${tableName}: ${error}`);
463
+ throw error;
464
+ }
465
+ }
466
+ async load({ tableName, keys }) {
467
+ const conditions = Object.entries(keys).map(([key]) => `${key} = ?`).join(" AND ");
468
+ const values = Object.values(keys);
469
+ const result = await this.client.execute({
470
+ sql: `SELECT * FROM ${tableName} WHERE ${conditions} ORDER BY createdAt DESC LIMIT 1`,
471
+ args: values
472
+ });
473
+ if (!result.rows || result.rows.length === 0) {
474
+ return null;
475
+ }
476
+ const row = result.rows[0];
477
+ const parsed = Object.fromEntries(
478
+ Object.entries(row || {}).map(([k, v]) => {
479
+ try {
480
+ return [k, typeof v === "string" ? JSON.parse(v) : v];
481
+ } catch {
482
+ return [k, v];
483
+ }
484
+ })
485
+ );
486
+ return parsed;
487
+ }
488
+ async getThreadById({ threadId }) {
489
+ const result = await this.load({
490
+ tableName: TABLE_THREADS,
491
+ keys: { id: threadId }
492
+ });
493
+ if (!result) {
494
+ return null;
495
+ }
496
+ return {
497
+ ...result,
498
+ metadata: typeof result.metadata === "string" ? JSON.parse(result.metadata) : result.metadata
499
+ };
500
+ }
501
+ async getThreadsByResourceId({ resourceId }) {
502
+ const result = await this.client.execute({
503
+ sql: `SELECT * FROM ${TABLE_THREADS} WHERE resourceId = ?`,
504
+ args: [resourceId]
505
+ });
506
+ if (!result.rows) {
507
+ return [];
508
+ }
509
+ return result.rows.map((thread) => ({
510
+ id: thread.id,
511
+ resourceId: thread.resourceId,
512
+ title: thread.title,
513
+ createdAt: thread.createdAt,
514
+ updatedAt: thread.updatedAt,
515
+ metadata: typeof thread.metadata === "string" ? JSON.parse(thread.metadata) : thread.metadata
516
+ }));
517
+ }
518
+ async saveThread({ thread }) {
519
+ await this.insert({
520
+ tableName: TABLE_THREADS,
521
+ record: {
522
+ ...thread,
523
+ metadata: JSON.stringify(thread.metadata)
524
+ }
525
+ });
526
+ return thread;
527
+ }
528
+ async updateThread({
529
+ id,
530
+ title,
531
+ metadata
532
+ }) {
533
+ const thread = await this.getThreadById({ threadId: id });
534
+ if (!thread) {
535
+ throw new Error(`Thread ${id} not found`);
536
+ }
537
+ const updatedThread = {
538
+ ...thread,
539
+ title,
540
+ metadata: {
541
+ ...thread.metadata,
542
+ ...metadata
543
+ }
544
+ };
545
+ await this.client.execute({
546
+ sql: `UPDATE ${TABLE_THREADS} SET title = ?, metadata = ? WHERE id = ?`,
547
+ args: [title, JSON.stringify(updatedThread.metadata), id]
548
+ });
549
+ return updatedThread;
550
+ }
551
+ async deleteThread({ threadId }) {
552
+ await this.client.execute({
553
+ sql: `DELETE FROM ${TABLE_THREADS} WHERE id = ?`,
554
+ args: [threadId]
555
+ });
556
+ }
557
+ parseRow(row) {
558
+ let content = row.content;
559
+ try {
560
+ content = JSON.parse(row.content);
561
+ } catch {
562
+ }
563
+ return {
564
+ id: row.id,
565
+ content,
566
+ role: row.role,
567
+ type: row.type,
568
+ createdAt: new Date(row.createdAt),
569
+ threadId: row.thread_id
570
+ };
571
+ }
572
+ async getMessages({ threadId, selectBy }) {
573
+ try {
574
+ const messages = [];
575
+ const limit = typeof selectBy?.last === `number` ? selectBy.last : 40;
576
+ if (selectBy?.include?.length) {
577
+ const includeIds = selectBy.include.map((i) => i.id);
578
+ const maxPrev = Math.max(...selectBy.include.map((i) => i.withPreviousMessages || 0));
579
+ const maxNext = Math.max(...selectBy.include.map((i) => i.withNextMessages || 0));
580
+ const includeResult = await this.client.execute({
581
+ sql: `
582
+ WITH numbered_messages AS (
583
+ SELECT
584
+ id,
585
+ content,
586
+ role,
587
+ type,
588
+ "createdAt",
589
+ thread_id,
590
+ ROW_NUMBER() OVER (ORDER BY "createdAt" ASC) as row_num
591
+ FROM "${TABLE_MESSAGES}"
592
+ WHERE thread_id = ?
593
+ ),
594
+ target_positions AS (
595
+ SELECT row_num as target_pos
596
+ FROM numbered_messages
597
+ WHERE id IN (${includeIds.map(() => "?").join(", ")})
598
+ )
599
+ SELECT DISTINCT m.*
600
+ FROM numbered_messages m
601
+ CROSS JOIN target_positions t
602
+ WHERE m.row_num BETWEEN (t.target_pos - ?) AND (t.target_pos + ?)
603
+ ORDER BY m."createdAt" ASC
604
+ `,
605
+ args: [threadId, ...includeIds, maxPrev, maxNext]
606
+ });
607
+ if (includeResult.rows) {
608
+ messages.push(...includeResult.rows.map((row) => this.parseRow(row)));
609
+ }
610
+ }
611
+ const excludeIds = messages.map((m) => m.id);
612
+ const remainingSql = `
613
+ SELECT
614
+ id,
615
+ content,
616
+ role,
617
+ type,
618
+ "createdAt",
619
+ thread_id
620
+ FROM "${TABLE_MESSAGES}"
621
+ WHERE thread_id = ?
622
+ ${excludeIds.length ? `AND id NOT IN (${excludeIds.map(() => "?").join(", ")})` : ""}
623
+ ORDER BY "createdAt" DESC
624
+ LIMIT ?
625
+ `;
626
+ const remainingArgs = [threadId, ...excludeIds.length ? excludeIds : [], limit];
627
+ const remainingResult = await this.client.execute({
628
+ sql: remainingSql,
629
+ args: remainingArgs
630
+ });
631
+ if (remainingResult.rows) {
632
+ messages.push(...remainingResult.rows.map((row) => this.parseRow(row)));
633
+ }
634
+ messages.sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime());
635
+ return messages;
636
+ } catch (error) {
637
+ this.logger.error("Error getting messages:", error);
638
+ throw error;
639
+ }
640
+ }
641
+ async saveMessages({ messages }) {
642
+ if (messages.length === 0) return messages;
643
+ const tx = await this.client.transaction("write");
644
+ try {
645
+ const threadId = messages[0]?.threadId;
646
+ if (!threadId) {
647
+ throw new Error("Thread ID is required");
648
+ }
649
+ for (const message of messages) {
650
+ const time = message.createdAt || /* @__PURE__ */ new Date();
651
+ await tx.execute({
652
+ sql: `INSERT INTO ${TABLE_MESSAGES} (id, thread_id, content, role, type, createdAt)
653
+ VALUES (?, ?, ?, ?, ?, ?)`,
654
+ args: [
655
+ message.id,
656
+ threadId,
657
+ typeof message.content === "object" ? JSON.stringify(message.content) : message.content,
658
+ message.role,
659
+ message.type,
660
+ time instanceof Date ? time.toISOString() : time
661
+ ]
662
+ });
663
+ }
664
+ await tx.commit();
665
+ return messages;
666
+ } catch (error) {
667
+ this.logger.error("Failed to save messages in database: " + error?.message);
668
+ await tx.rollback();
669
+ throw error;
670
+ }
671
+ }
672
+ transformEvalRow(row) {
673
+ const resultValue = JSON.parse(row.result);
674
+ const testInfoValue = row.test_info ? JSON.parse(row.test_info) : void 0;
675
+ if (!resultValue || typeof resultValue !== "object" || !("score" in resultValue)) {
676
+ throw new Error(`Invalid MetricResult format: ${JSON.stringify(resultValue)}`);
677
+ }
678
+ return {
679
+ input: row.input,
680
+ output: row.output,
681
+ result: resultValue,
682
+ agentName: row.agent_name,
683
+ metricName: row.metric_name,
684
+ instructions: row.instructions,
685
+ testInfo: testInfoValue,
686
+ globalRunId: row.global_run_id,
687
+ runId: row.run_id,
688
+ createdAt: row.created_at
689
+ };
690
+ }
691
+ async getEvalsByAgentName(agentName, type) {
692
+ try {
693
+ const baseQuery = `SELECT * FROM ${TABLE_EVALS} WHERE agent_name = ?`;
694
+ const typeCondition = type === "test" ? " AND test_info IS NOT NULL AND test_info->>'testPath' IS NOT NULL" : type === "live" ? " AND (test_info IS NULL OR test_info->>'testPath' IS NULL)" : "";
695
+ const result = await this.client.execute({
696
+ sql: `${baseQuery}${typeCondition} ORDER BY created_at DESC`,
697
+ args: [agentName]
698
+ });
699
+ return result.rows?.map((row) => this.transformEvalRow(row)) ?? [];
700
+ } catch (error) {
701
+ if (error instanceof Error && error.message.includes("no such table")) {
702
+ return [];
703
+ }
704
+ this.logger.error("Failed to get evals for the specified agent: " + error?.message);
705
+ throw error;
706
+ }
707
+ }
708
+ // TODO: add types
709
+ async getTraces({
710
+ name,
711
+ scope,
712
+ page,
713
+ perPage,
714
+ attributes
715
+ } = {
716
+ page: 0,
717
+ perPage: 100
718
+ }) {
719
+ const limit = perPage;
720
+ const offset = page * perPage;
721
+ const args = [];
722
+ const conditions = [];
723
+ if (name) {
724
+ conditions.push("name LIKE CONCAT(?, '%')");
725
+ }
726
+ if (scope) {
727
+ conditions.push("scope = ?");
728
+ }
729
+ if (attributes) {
730
+ Object.keys(attributes).forEach((key) => {
731
+ conditions.push(`attributes->>'$.${key}' = ?`);
732
+ });
733
+ }
734
+ const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
735
+ if (name) {
736
+ args.push(name);
737
+ }
738
+ if (scope) {
739
+ args.push(scope);
740
+ }
741
+ if (attributes) {
742
+ for (const [_key, value] of Object.entries(attributes)) {
743
+ args.push(value);
744
+ }
745
+ }
746
+ args.push(limit, offset);
747
+ const result = await this.client.execute({
748
+ sql: `SELECT * FROM ${TABLE_TRACES} ${whereClause} ORDER BY "startTime" DESC LIMIT ? OFFSET ?`,
749
+ args
750
+ });
751
+ if (!result.rows) {
752
+ return [];
753
+ }
754
+ return result.rows.map((row) => ({
755
+ id: row.id,
756
+ parentSpanId: row.parentSpanId,
757
+ traceId: row.traceId,
758
+ name: row.name,
759
+ scope: row.scope,
760
+ kind: row.kind,
761
+ status: safelyParseJSON(row.status),
762
+ events: safelyParseJSON(row.events),
763
+ links: safelyParseJSON(row.links),
764
+ attributes: safelyParseJSON(row.attributes),
765
+ startTime: row.startTime,
766
+ endTime: row.endTime,
767
+ other: safelyParseJSON(row.other),
768
+ createdAt: row.createdAt
769
+ }));
770
+ }
771
+ };
772
+ function deepMerge(target, source) {
773
+ const output = { ...target };
774
+ if (!source) return output;
775
+ Object.keys(source).forEach((key) => {
776
+ const targetValue = output[key];
777
+ const sourceValue = source[key];
778
+ if (Array.isArray(targetValue) && Array.isArray(sourceValue)) {
779
+ output[key] = sourceValue;
780
+ } else if (sourceValue instanceof Object && targetValue instanceof Object && !Array.isArray(sourceValue) && !Array.isArray(targetValue)) {
781
+ output[key] = deepMerge(targetValue, sourceValue);
782
+ } else if (sourceValue !== void 0) {
783
+ output[key] = sourceValue;
784
+ }
785
+ });
786
+ return output;
787
+ }
788
+ var cachedPath = false;
789
+ function getModelCachePath() {
790
+ if (cachedPath) return cachedPath;
791
+ const firstNodeModules = node_modulesPath__default.default().split("node_modules")[0];
792
+ cachedPath = path__default.default.join(firstNodeModules, "node_modules", ".fastembed-model-cache");
793
+ return cachedPath;
794
+ }
795
+ function unbundleableImport(name) {
796
+ const nonStaticallyAnalyzableName = `${name}?d=${Date.now()}`;
797
+ return import(nonStaticallyAnalyzableName.split(`?`)[0]);
798
+ }
799
+ async function generateEmbeddings(values, modelType) {
800
+ try {
801
+ let mod;
802
+ const importErrors = [];
803
+ {
804
+ try {
805
+ mod = await unbundleableImport("fastembed");
806
+ } catch (e) {
807
+ if (e instanceof Error) {
808
+ importErrors.push(e);
809
+ } else {
810
+ throw e;
811
+ }
812
+ }
813
+ }
814
+ if (!mod) {
815
+ throw new Error(`${importErrors.map((e) => e.message).join(`
816
+ `)}
817
+
818
+ This runtime does not support fastembed-js, which is the default embedder in Mastra.
819
+ Scroll up to read import errors. These errors mean you can't use the default Mastra embedder on this hosting platform.
820
+ You can either use Mastra Cloud which supports the default embedder, or you can configure an alternate provider.
821
+
822
+ For example if you're using Memory:
823
+
824
+ import { openai } from "@ai-sdk/openai";
825
+
826
+ const memory = new Memory({
827
+ embedder: openai.embedding("text-embedding-3-small"), // <- doesn't have to be openai
828
+ })
829
+
830
+ Visit https://sdk.vercel.ai/docs/foundations/overview#embedding-models to find an alternate embedding provider
831
+
832
+ If you do not want to use the Memory semantic recall feature, you can disable it entirely and this error will go away.
833
+
834
+ const memory = new Memory({
835
+ options: {
836
+ semanticRecall: false // <- an embedder will not be required with this set to false
837
+ }
838
+ })
839
+ `);
840
+ }
841
+ const { FlagEmbedding, EmbeddingModel } = mod;
842
+ const model = await FlagEmbedding.init({
843
+ model: EmbeddingModel[modelType],
844
+ cacheDir: getModelCachePath()
845
+ });
846
+ const embeddings = await model.embed(values);
847
+ const allResults = [];
848
+ for await (const result of embeddings) {
849
+ allResults.push(...result.map((embedding) => Array.from(embedding)));
850
+ }
851
+ if (allResults.length === 0) throw new Error("No embeddings generated");
852
+ return {
853
+ embeddings: allResults
854
+ };
855
+ } catch (error) {
856
+ console.error("Error generating embeddings:", error);
857
+ throw error;
858
+ }
859
+ }
860
+ var fastEmbedProvider = ai.experimental_customProvider({
861
+ textEmbeddingModels: {
862
+ "bge-small-en-v1.5": {
863
+ specificationVersion: "v1",
864
+ provider: "fastembed",
865
+ modelId: "bge-small-en-v1.5",
866
+ maxEmbeddingsPerCall: 256,
867
+ supportsParallelCalls: true,
868
+ async doEmbed({ values }) {
869
+ return generateEmbeddings(values, "BGESmallENV15");
870
+ }
871
+ },
872
+ "bge-base-en-v1.5": {
873
+ specificationVersion: "v1",
874
+ provider: "fastembed",
875
+ modelId: "bge-base-en-v1.5",
876
+ maxEmbeddingsPerCall: 256,
877
+ supportsParallelCalls: true,
878
+ async doEmbed({ values }) {
879
+ return generateEmbeddings(values, "BGEBaseENV15");
880
+ }
881
+ }
882
+ }
883
+ });
884
+ var defaultEmbedder = fastEmbedProvider.textEmbeddingModel;
885
+
886
+ // src/vector/index.ts
887
+ var MastraVector = class extends MastraBase {
888
+ constructor() {
889
+ super({ name: "MastraVector", component: "VECTOR" });
890
+ }
891
+ };
892
+
893
+ // src/filter/base.ts
894
+ var BaseFilterTranslator = class _BaseFilterTranslator {
895
+ /**
896
+ * Operator type checks
897
+ */
898
+ isOperator(key) {
899
+ return key.startsWith("$");
900
+ }
901
+ static BASIC_OPERATORS = ["$eq", "$ne"];
902
+ static NUMERIC_OPERATORS = ["$gt", "$gte", "$lt", "$lte"];
903
+ static ARRAY_OPERATORS = ["$in", "$nin", "$all", "$elemMatch"];
904
+ static LOGICAL_OPERATORS = ["$and", "$or", "$not", "$nor"];
905
+ static ELEMENT_OPERATORS = ["$exists"];
906
+ static REGEX_OPERATORS = ["$regex", "$options"];
907
+ static DEFAULT_OPERATORS = {
908
+ logical: _BaseFilterTranslator.LOGICAL_OPERATORS,
909
+ basic: _BaseFilterTranslator.BASIC_OPERATORS,
910
+ numeric: _BaseFilterTranslator.NUMERIC_OPERATORS,
911
+ array: _BaseFilterTranslator.ARRAY_OPERATORS,
912
+ element: _BaseFilterTranslator.ELEMENT_OPERATORS,
913
+ regex: _BaseFilterTranslator.REGEX_OPERATORS
914
+ };
915
+ isLogicalOperator(key) {
916
+ return _BaseFilterTranslator.DEFAULT_OPERATORS.logical.includes(key);
917
+ }
918
+ isBasicOperator(key) {
919
+ return _BaseFilterTranslator.DEFAULT_OPERATORS.basic.includes(key);
920
+ }
921
+ isNumericOperator(key) {
922
+ return _BaseFilterTranslator.DEFAULT_OPERATORS.numeric.includes(key);
923
+ }
924
+ isArrayOperator(key) {
925
+ return _BaseFilterTranslator.DEFAULT_OPERATORS.array.includes(key);
926
+ }
927
+ isElementOperator(key) {
928
+ return _BaseFilterTranslator.DEFAULT_OPERATORS.element.includes(key);
929
+ }
930
+ isRegexOperator(key) {
931
+ return _BaseFilterTranslator.DEFAULT_OPERATORS.regex.includes(key);
932
+ }
933
+ isFieldOperator(key) {
934
+ return this.isOperator(key) && !this.isLogicalOperator(key);
935
+ }
936
+ isCustomOperator(key) {
937
+ const support = this.getSupportedOperators();
938
+ return support.custom?.includes(key) ?? false;
939
+ }
940
+ getSupportedOperators() {
941
+ return _BaseFilterTranslator.DEFAULT_OPERATORS;
942
+ }
943
+ isValidOperator(key) {
944
+ const support = this.getSupportedOperators();
945
+ const allSupported = Object.values(support).flat();
946
+ return allSupported.includes(key);
947
+ }
948
+ /**
949
+ * Value normalization for comparison operators
950
+ */
951
+ normalizeComparisonValue(value) {
952
+ if (value instanceof Date) {
953
+ return value.toISOString();
954
+ }
955
+ if (typeof value === "number" && Object.is(value, -0)) {
956
+ return 0;
957
+ }
958
+ return value;
959
+ }
960
+ /**
961
+ * Helper method to simulate $all operator using $and + $eq when needed.
962
+ * Some vector stores don't support $all natively.
963
+ */
964
+ simulateAllOperator(field, values) {
965
+ return {
966
+ $and: values.map((value) => ({
967
+ [field]: { $in: [this.normalizeComparisonValue(value)] }
968
+ }))
969
+ };
970
+ }
971
+ /**
972
+ * Utility functions for type checking
973
+ */
974
+ isPrimitive(value) {
975
+ return value === null || value === void 0 || typeof value === "string" || typeof value === "number" || typeof value === "boolean";
976
+ }
977
+ isRegex(value) {
978
+ return value instanceof RegExp;
979
+ }
980
+ isEmpty(obj) {
981
+ return obj === null || obj === void 0 || typeof obj === "object" && Object.keys(obj).length === 0;
982
+ }
983
+ static ErrorMessages = {
984
+ UNSUPPORTED_OPERATOR: (op) => `Unsupported operator: ${op}`,
985
+ INVALID_LOGICAL_OPERATOR_LOCATION: (op, path2) => `Logical operator ${op} cannot be used at field level: ${path2}`,
986
+ NOT_REQUIRES_OBJECT: `$not operator requires an object`,
987
+ NOT_CANNOT_BE_EMPTY: `$not operator cannot be empty`,
988
+ INVALID_LOGICAL_OPERATOR_CONTENT: (path2) => `Logical operators must contain field conditions, not direct operators: ${path2}`,
989
+ INVALID_TOP_LEVEL_OPERATOR: (op) => `Invalid top-level operator: ${op}`,
990
+ ELEM_MATCH_REQUIRES_OBJECT: `$elemMatch requires an object with conditions`
991
+ };
992
+ /**
993
+ * Helper to handle array value normalization consistently
994
+ */
995
+ normalizeArrayValues(values) {
996
+ return values.map((value) => this.normalizeComparisonValue(value));
997
+ }
998
+ validateFilter(filter) {
999
+ const validation = this.validateFilterSupport(filter);
1000
+ if (!validation.supported) {
1001
+ throw new Error(validation.messages.join(", "));
1002
+ }
1003
+ }
1004
+ /**
1005
+ * Validates if a filter structure is supported by the specific vector DB
1006
+ * and returns detailed validation information.
1007
+ */
1008
+ validateFilterSupport(node, path2 = "") {
1009
+ const messages = [];
1010
+ if (this.isPrimitive(node) || this.isEmpty(node)) {
1011
+ return { supported: true, messages: [] };
1012
+ }
1013
+ if (Array.isArray(node)) {
1014
+ const arrayResults = node.map((item) => this.validateFilterSupport(item, path2));
1015
+ const arrayMessages = arrayResults.flatMap((r) => r.messages);
1016
+ return {
1017
+ supported: arrayResults.every((r) => r.supported),
1018
+ messages: arrayMessages
1019
+ };
1020
+ }
1021
+ const nodeObj = node;
1022
+ let isSupported = true;
1023
+ for (const [key, value] of Object.entries(nodeObj)) {
1024
+ const newPath = path2 ? `${path2}.${key}` : key;
1025
+ if (this.isOperator(key)) {
1026
+ if (!this.isValidOperator(key)) {
1027
+ isSupported = false;
1028
+ messages.push(_BaseFilterTranslator.ErrorMessages.UNSUPPORTED_OPERATOR(key));
1029
+ continue;
1030
+ }
1031
+ if (!path2 && !this.isLogicalOperator(key)) {
1032
+ isSupported = false;
1033
+ messages.push(_BaseFilterTranslator.ErrorMessages.INVALID_TOP_LEVEL_OPERATOR(key));
1034
+ continue;
1035
+ }
1036
+ if (key === "$elemMatch" && (typeof value !== "object" || Array.isArray(value))) {
1037
+ isSupported = false;
1038
+ messages.push(_BaseFilterTranslator.ErrorMessages.ELEM_MATCH_REQUIRES_OBJECT);
1039
+ continue;
1040
+ }
1041
+ if (this.isLogicalOperator(key)) {
1042
+ if (key === "$not") {
1043
+ if (Array.isArray(value) || typeof value !== "object") {
1044
+ isSupported = false;
1045
+ messages.push(_BaseFilterTranslator.ErrorMessages.NOT_REQUIRES_OBJECT);
1046
+ continue;
1047
+ }
1048
+ if (this.isEmpty(value)) {
1049
+ isSupported = false;
1050
+ messages.push(_BaseFilterTranslator.ErrorMessages.NOT_CANNOT_BE_EMPTY);
1051
+ continue;
1052
+ }
1053
+ continue;
1054
+ }
1055
+ if (path2 && !this.isLogicalOperator(path2.split(".").pop())) {
1056
+ isSupported = false;
1057
+ messages.push(_BaseFilterTranslator.ErrorMessages.INVALID_LOGICAL_OPERATOR_LOCATION(key, newPath));
1058
+ continue;
1059
+ }
1060
+ if (Array.isArray(value)) {
1061
+ const hasDirectOperators = value.some(
1062
+ (item) => typeof item === "object" && Object.keys(item).length === 1 && this.isFieldOperator(Object.keys(item)[0])
1063
+ );
1064
+ if (hasDirectOperators) {
1065
+ isSupported = false;
1066
+ messages.push(_BaseFilterTranslator.ErrorMessages.INVALID_LOGICAL_OPERATOR_CONTENT(newPath));
1067
+ continue;
1068
+ }
1069
+ }
1070
+ }
1071
+ }
1072
+ const nestedValidation = this.validateFilterSupport(value, newPath);
1073
+ if (!nestedValidation.supported) {
1074
+ isSupported = false;
1075
+ messages.push(...nestedValidation.messages);
1076
+ }
1077
+ }
1078
+ return { supported: isSupported, messages };
1079
+ }
1080
+ };
1081
+
1082
+ // src/vector/libsql/filter.ts
1083
+ var LibSQLFilterTranslator = class extends BaseFilterTranslator {
1084
+ getSupportedOperators() {
1085
+ return {
1086
+ ...BaseFilterTranslator.DEFAULT_OPERATORS,
1087
+ regex: [],
1088
+ custom: ["$contains", "$size"]
1089
+ };
1090
+ }
1091
+ translate(filter) {
1092
+ if (this.isEmpty(filter)) {
1093
+ return filter;
1094
+ }
1095
+ this.validateFilter(filter);
1096
+ return this.translateNode(filter);
1097
+ }
1098
+ translateNode(node, currentPath = "") {
1099
+ if (this.isRegex(node)) {
1100
+ throw new Error("Direct regex pattern format is not supported in LibSQL");
1101
+ }
1102
+ const withPath = (result2) => currentPath ? { [currentPath]: result2 } : result2;
1103
+ if (this.isPrimitive(node)) {
1104
+ return withPath({ $eq: this.normalizeComparisonValue(node) });
1105
+ }
1106
+ if (Array.isArray(node)) {
1107
+ return withPath({ $in: this.normalizeArrayValues(node) });
1108
+ }
1109
+ const entries = Object.entries(node);
1110
+ const result = {};
1111
+ for (const [key, value] of entries) {
1112
+ const newPath = currentPath ? `${currentPath}.${key}` : key;
1113
+ if (this.isLogicalOperator(key)) {
1114
+ result[key] = Array.isArray(value) ? value.map((filter) => this.translateNode(filter)) : this.translateNode(value);
1115
+ } else if (this.isOperator(key)) {
1116
+ if (this.isArrayOperator(key) && !Array.isArray(value) && key !== "$elemMatch") {
1117
+ result[key] = [value];
1118
+ } else if (this.isBasicOperator(key) && Array.isArray(value)) {
1119
+ result[key] = JSON.stringify(value);
1120
+ } else {
1121
+ result[key] = value;
1122
+ }
1123
+ } else if (typeof value === "object" && value !== null) {
1124
+ const hasOperators = Object.keys(value).some((k) => this.isOperator(k));
1125
+ if (hasOperators) {
1126
+ result[newPath] = this.translateNode(value);
1127
+ } else {
1128
+ Object.assign(result, this.translateNode(value, newPath));
1129
+ }
1130
+ } else {
1131
+ result[newPath] = this.translateNode(value);
1132
+ }
1133
+ }
1134
+ return result;
1135
+ }
1136
+ // TODO: Look more into regex support for LibSQL
1137
+ // private translateRegexPattern(pattern: string, options: string = ''): any {
1138
+ // if (!options) return { $regex: pattern };
1139
+ // const flags = options
1140
+ // .split('')
1141
+ // .filter(f => 'imsux'.includes(f))
1142
+ // .join('');
1143
+ // return {
1144
+ // $regex: pattern,
1145
+ // $options: flags,
1146
+ // };
1147
+ // }
1148
+ };
1149
+
1150
+ // src/vector/libsql/sql-builder.ts
1151
+ var createBasicOperator = (symbol) => {
1152
+ return (key) => ({
1153
+ sql: `CASE
1154
+ WHEN ? IS NULL THEN json_extract(metadata, '$."${handleKey(key)}"') IS ${symbol === "=" ? "" : "NOT"} NULL
1155
+ ELSE json_extract(metadata, '$."${handleKey(key)}"') ${symbol} ?
1156
+ END`,
1157
+ needsValue: true,
1158
+ transformValue: (value) => {
1159
+ return [value, value];
1160
+ }
1161
+ });
1162
+ };
1163
+ var createNumericOperator = (symbol) => {
1164
+ return (key) => ({
1165
+ sql: `CAST(json_extract(metadata, '$."${handleKey(key)}"') AS NUMERIC) ${symbol} ?`,
1166
+ needsValue: true
1167
+ });
1168
+ };
1169
+ var validateJsonArray = (key) => `json_valid(json_extract(metadata, '$."${handleKey(key)}"'))
1170
+ AND json_type(json_extract(metadata, '$."${handleKey(key)}"')) = 'array'`;
1171
+ var FILTER_OPERATORS = {
1172
+ $eq: createBasicOperator("="),
1173
+ $ne: createBasicOperator("!="),
1174
+ $gt: createNumericOperator(">"),
1175
+ $gte: createNumericOperator(">="),
1176
+ $lt: createNumericOperator("<"),
1177
+ $lte: createNumericOperator("<="),
1178
+ // Array Operators
1179
+ $in: (key, value) => ({
1180
+ sql: `json_extract(metadata, '$."${handleKey(key)}"') IN (${value.map(() => "?").join(",")})`,
1181
+ needsValue: true
1182
+ }),
1183
+ $nin: (key, value) => ({
1184
+ sql: `json_extract(metadata, '$."${handleKey(key)}"') NOT IN (${value.map(() => "?").join(",")})`,
1185
+ needsValue: true
1186
+ }),
1187
+ $all: (key) => ({
1188
+ sql: `json_extract(metadata, '$."${handleKey(key)}"') = ?`,
1189
+ needsValue: true,
1190
+ transformValue: (value) => {
1191
+ const arrayValue = Array.isArray(value) ? value : [value];
1192
+ if (arrayValue.length === 0) {
1193
+ return {
1194
+ sql: "1 = 0",
1195
+ values: []
1196
+ };
1197
+ }
1198
+ return {
1199
+ sql: `(
1200
+ CASE
1201
+ WHEN ${validateJsonArray(key)} THEN
1202
+ NOT EXISTS (
1203
+ SELECT value
1204
+ FROM json_each(?)
1205
+ WHERE value NOT IN (
1206
+ SELECT value
1207
+ FROM json_each(json_extract(metadata, '$."${handleKey(key)}"'))
1208
+ )
1209
+ )
1210
+ ELSE FALSE
1211
+ END
1212
+ )`,
1213
+ values: [JSON.stringify(arrayValue)]
1214
+ };
1215
+ }
1216
+ }),
1217
+ $elemMatch: (key) => ({
1218
+ sql: `json_extract(metadata, '$."${handleKey(key)}"') = ?`,
1219
+ needsValue: true,
1220
+ transformValue: (value) => {
1221
+ if (typeof value !== "object" || Array.isArray(value)) {
1222
+ throw new Error("$elemMatch requires an object with conditions");
1223
+ }
1224
+ const conditions = Object.entries(value).map(([field, fieldValue]) => {
1225
+ if (field.startsWith("$")) {
1226
+ const { sql, values } = buildCondition("elem.value", { [field]: fieldValue });
1227
+ const pattern = /json_extract\(metadata, '\$\."[^"]*"(\."[^"]*")*'\)/g;
1228
+ const elemSql = sql.replace(pattern, "elem.value");
1229
+ return { sql: elemSql, values };
1230
+ } else if (typeof fieldValue === "object" && !Array.isArray(fieldValue)) {
1231
+ const { sql, values } = buildCondition(field, fieldValue);
1232
+ const pattern = /json_extract\(metadata, '\$\."[^"]*"(\."[^"]*")*'\)/g;
1233
+ const elemSql = sql.replace(pattern, `json_extract(elem.value, '$."${field}"')`);
1234
+ return { sql: elemSql, values };
1235
+ } else {
1236
+ return {
1237
+ sql: `json_extract(elem.value, '$."${field}"') = ?`,
1238
+ values: [fieldValue]
1239
+ };
1240
+ }
1241
+ });
1242
+ return {
1243
+ sql: `(
1244
+ CASE
1245
+ WHEN ${validateJsonArray(key)} THEN
1246
+ EXISTS (
1247
+ SELECT 1
1248
+ FROM json_each(json_extract(metadata, '$."${handleKey(key)}"')) as elem
1249
+ WHERE ${conditions.map((c) => c.sql).join(" AND ")}
1250
+ )
1251
+ ELSE FALSE
1252
+ END
1253
+ )`,
1254
+ values: conditions.flatMap((c) => c.values)
1255
+ };
1256
+ }
1257
+ }),
1258
+ // Element Operators
1259
+ $exists: (key) => ({
1260
+ sql: `json_extract(metadata, '$."${handleKey(key)}"') IS NOT NULL`,
1261
+ needsValue: false
1262
+ }),
1263
+ // Logical Operators
1264
+ $and: (key) => ({
1265
+ sql: `(${key})`,
1266
+ needsValue: false
1267
+ }),
1268
+ $or: (key) => ({
1269
+ sql: `(${key})`,
1270
+ needsValue: false
1271
+ }),
1272
+ $not: (key) => ({ sql: `NOT (${key})`, needsValue: false }),
1273
+ $nor: (key) => ({
1274
+ sql: `NOT (${key})`,
1275
+ needsValue: false
1276
+ }),
1277
+ $size: (key, paramIndex) => ({
1278
+ sql: `(
1279
+ CASE
1280
+ WHEN json_type(json_extract(metadata, '$."${handleKey(key)}"')) = 'array' THEN
1281
+ json_array_length(json_extract(metadata, '$."${handleKey(key)}"')) = $${paramIndex}
1282
+ ELSE FALSE
1283
+ END
1284
+ )`,
1285
+ needsValue: true
1286
+ }),
1287
+ // /**
1288
+ // * Regex Operators
1289
+ // * Supports case insensitive and multiline
1290
+ // */
1291
+ // $regex: (key: string): FilterOperator => ({
1292
+ // sql: `json_extract(metadata, '$."${handleKey(key)}"') = ?`,
1293
+ // needsValue: true,
1294
+ // transformValue: (value: any) => {
1295
+ // const pattern = typeof value === 'object' ? value.$regex : value;
1296
+ // const options = typeof value === 'object' ? value.$options || '' : '';
1297
+ // let sql = `json_extract(metadata, '$."${handleKey(key)}"')`;
1298
+ // // Handle multiline
1299
+ // // if (options.includes('m')) {
1300
+ // // sql = `REPLACE(${sql}, CHAR(10), '\n')`;
1301
+ // // }
1302
+ // // let finalPattern = pattern;
1303
+ // // if (options) {
1304
+ // // finalPattern = `(\\?${options})${pattern}`;
1305
+ // // }
1306
+ // // // Handle case insensitivity
1307
+ // // if (options.includes('i')) {
1308
+ // // sql = `LOWER(${sql}) REGEXP LOWER(?)`;
1309
+ // // } else {
1310
+ // // sql = `${sql} REGEXP ?`;
1311
+ // // }
1312
+ // if (options.includes('m')) {
1313
+ // sql = `EXISTS (
1314
+ // SELECT 1
1315
+ // FROM json_each(
1316
+ // json_array(
1317
+ // ${sql},
1318
+ // REPLACE(${sql}, CHAR(10), CHAR(13))
1319
+ // )
1320
+ // ) as lines
1321
+ // WHERE lines.value REGEXP ?
1322
+ // )`;
1323
+ // } else {
1324
+ // sql = `${sql} REGEXP ?`;
1325
+ // }
1326
+ // // Handle case insensitivity
1327
+ // if (options.includes('i')) {
1328
+ // sql = sql.replace('REGEXP ?', 'REGEXP LOWER(?)');
1329
+ // sql = sql.replace('value REGEXP', 'LOWER(value) REGEXP');
1330
+ // }
1331
+ // // Handle extended - allows whitespace and comments in pattern
1332
+ // if (options.includes('x')) {
1333
+ // // Remove whitespace and comments from pattern
1334
+ // const cleanPattern = pattern.replace(/\s+|#.*$/gm, '');
1335
+ // return {
1336
+ // sql,
1337
+ // values: [cleanPattern],
1338
+ // };
1339
+ // }
1340
+ // return {
1341
+ // sql,
1342
+ // values: [pattern],
1343
+ // };
1344
+ // },
1345
+ // }),
1346
+ $contains: (key) => ({
1347
+ sql: `json_extract(metadata, '$."${handleKey(key)}"') = ?`,
1348
+ needsValue: true,
1349
+ transformValue: (value) => {
1350
+ if (Array.isArray(value)) {
1351
+ return {
1352
+ sql: `(
1353
+ SELECT ${validateJsonArray(key)}
1354
+ AND EXISTS (
1355
+ SELECT 1
1356
+ FROM json_each(json_extract(metadata, '$."${handleKey(key)}"')) as m
1357
+ WHERE m.value IN (SELECT value FROM json_each(?))
1358
+ )
1359
+ )`,
1360
+ values: [JSON.stringify(value)]
1361
+ };
1362
+ }
1363
+ if (value && typeof value === "object") {
1364
+ let traverse2 = function(obj, path2 = []) {
1365
+ for (const [k, v] of Object.entries(obj)) {
1366
+ const currentPath = [...path2, k];
1367
+ if (v && typeof v === "object" && !Array.isArray(v)) {
1368
+ traverse2(v, currentPath);
1369
+ } else {
1370
+ paths.push(currentPath.join("."));
1371
+ values.push(v);
1372
+ }
1373
+ }
1374
+ };
1375
+ const paths = [];
1376
+ const values = [];
1377
+ traverse2(value);
1378
+ return {
1379
+ sql: `(${paths.map((path2) => `json_extract(metadata, '$."${handleKey(key)}"."${path2}"') = ?`).join(" AND ")})`,
1380
+ values
1381
+ };
1382
+ }
1383
+ return value;
1384
+ }
1385
+ })
1386
+ };
1387
+ var handleKey = (key) => {
1388
+ return key.replace(/\./g, '"."');
1389
+ };
1390
+ function buildFilterQuery(filter) {
1391
+ if (!filter) {
1392
+ return { sql: "", values: [] };
1393
+ }
1394
+ const values = [];
1395
+ const conditions = Object.entries(filter).map(([key, value]) => {
1396
+ const condition = buildCondition(key, value);
1397
+ values.push(...condition.values);
1398
+ return condition.sql;
1399
+ }).join(" AND ");
1400
+ return {
1401
+ sql: conditions ? `WHERE ${conditions}` : "",
1402
+ values
1403
+ };
1404
+ }
1405
+ function buildCondition(key, value, parentPath) {
1406
+ if (["$and", "$or", "$not", "$nor"].includes(key)) {
1407
+ return handleLogicalOperator(key, value);
1408
+ }
1409
+ if (!value || typeof value !== "object") {
1410
+ return {
1411
+ sql: `json_extract(metadata, '$."${key.replace(/\./g, '"."')}"') = ?`,
1412
+ values: [value]
1413
+ };
1414
+ }
1415
+ return handleOperator(key, value);
1416
+ }
1417
+ function handleLogicalOperator(key, value, parentPath) {
1418
+ if (!value || value.length === 0) {
1419
+ switch (key) {
1420
+ case "$and":
1421
+ case "$nor":
1422
+ return { sql: "true", values: [] };
1423
+ case "$or":
1424
+ return { sql: "false", values: [] };
1425
+ case "$not":
1426
+ throw new Error("$not operator cannot be empty");
1427
+ default:
1428
+ return { sql: "true", values: [] };
1429
+ }
1430
+ }
1431
+ if (key === "$not") {
1432
+ const entries = Object.entries(value);
1433
+ const conditions2 = entries.map(([fieldKey, fieldValue]) => buildCondition(fieldKey, fieldValue));
1434
+ return {
1435
+ sql: `NOT (${conditions2.map((c) => c.sql).join(" AND ")})`,
1436
+ values: conditions2.flatMap((c) => c.values)
1437
+ };
1438
+ }
1439
+ const values = [];
1440
+ const joinOperator = key === "$or" || key === "$nor" ? "OR" : "AND";
1441
+ const conditions = Array.isArray(value) ? value.map((f) => {
1442
+ const entries = Object.entries(f);
1443
+ return entries.map(([k, v]) => buildCondition(k, v));
1444
+ }) : [buildCondition(key, value)];
1445
+ const joined = conditions.flat().map((c) => {
1446
+ values.push(...c.values);
1447
+ return c.sql;
1448
+ }).join(` ${joinOperator} `);
1449
+ return {
1450
+ sql: key === "$nor" ? `NOT (${joined})` : `(${joined})`,
1451
+ values
1452
+ };
1453
+ }
1454
+ function handleOperator(key, value) {
1455
+ if (typeof value === "object" && !Array.isArray(value)) {
1456
+ const entries = Object.entries(value);
1457
+ const results = entries.map(
1458
+ ([operator2, operatorValue2]) => operator2 === "$not" ? {
1459
+ sql: `NOT (${Object.entries(operatorValue2).map(([op, val]) => processOperator(key, op, val).sql).join(" AND ")})`,
1460
+ values: Object.entries(operatorValue2).flatMap(
1461
+ ([op, val]) => processOperator(key, op, val).values
1462
+ )
1463
+ } : processOperator(key, operator2, operatorValue2)
1464
+ );
1465
+ return {
1466
+ sql: `(${results.map((r) => r.sql).join(" AND ")})`,
1467
+ values: results.flatMap((r) => r.values)
1468
+ };
1469
+ }
1470
+ const [[operator, operatorValue] = []] = Object.entries(value);
1471
+ return processOperator(key, operator, operatorValue);
1472
+ }
1473
+ var processOperator = (key, operator, operatorValue) => {
1474
+ if (!operator.startsWith("$") || !FILTER_OPERATORS[operator]) {
1475
+ throw new Error(`Invalid operator: ${operator}`);
1476
+ }
1477
+ const operatorFn = FILTER_OPERATORS[operator];
1478
+ const operatorResult = operatorFn(key, operatorValue);
1479
+ if (!operatorResult.needsValue) {
1480
+ return { sql: operatorResult.sql, values: [] };
1481
+ }
1482
+ const transformed = operatorResult.transformValue ? operatorResult.transformValue(operatorValue) : operatorValue;
1483
+ if (transformed && typeof transformed === "object" && "sql" in transformed) {
1484
+ return transformed;
1485
+ }
1486
+ return {
1487
+ sql: operatorResult.sql,
1488
+ values: Array.isArray(transformed) ? transformed : [transformed]
1489
+ };
1490
+ };
1491
+
1492
+ // src/vector/libsql/index.ts
1493
+ var LibSQLVector = class extends MastraVector {
1494
+ turso;
1495
+ constructor({
1496
+ connectionUrl,
1497
+ authToken,
1498
+ syncUrl,
1499
+ syncInterval
1500
+ }) {
1501
+ super();
1502
+ this.turso = client.createClient({
1503
+ url: this.rewriteDbUrl(connectionUrl),
1504
+ syncUrl,
1505
+ authToken,
1506
+ syncInterval
1507
+ });
1508
+ }
1509
+ rewriteDbUrl(url) {
1510
+ if (url.startsWith("file:") && !url.startsWith("file:/")) {
1511
+ const cwd = process.cwd();
1512
+ const relativePath = url.slice("file:".length);
1513
+ if (cwd.endsWith(".mastra") || cwd.endsWith(".mastra/")) {
1514
+ const baseDir = path.join(cwd, `..`);
1515
+ const fullPath = path.join(baseDir, relativePath);
1516
+ this.logger.debug(
1517
+ `Initializing LibSQL db with url ${url} with relative file path from inside .mastra directory. Rewriting relative file url to "file:${fullPath}". This ensures it's outside the .mastra directory. If the db is stored inside .mastra it will be deleted when Mastra re-bundles code.`
1518
+ );
1519
+ return `file:${fullPath}`;
1520
+ }
1521
+ }
1522
+ return url;
1523
+ }
1524
+ transformFilter(filter) {
1525
+ const libsqlFilter = new LibSQLFilterTranslator();
1526
+ const translatedFilter = libsqlFilter.translate(filter ?? {});
1527
+ return translatedFilter;
1528
+ }
1529
+ async query(indexName, queryVector, topK = 10, filter, includeVector = false, minScore = 0) {
1530
+ try {
1531
+ const vectorStr = `[${queryVector.join(",")}]`;
1532
+ const translatedFilter = this.transformFilter(filter);
1533
+ const { sql: filterQuery, values: filterValues } = buildFilterQuery(translatedFilter);
1534
+ filterValues.push(minScore);
1535
+ const query = `
1536
+ WITH vector_scores AS (
1537
+ SELECT
1538
+ vector_id as id,
1539
+ (1-vector_distance_cos(embedding, '${vectorStr}')) as score,
1540
+ metadata
1541
+ ${includeVector ? ", vector_extract(embedding) as embedding" : ""}
1542
+ FROM ${indexName}
1543
+ ${filterQuery}
1544
+ )
1545
+ SELECT *
1546
+ FROM vector_scores
1547
+ WHERE score > ?
1548
+ ORDER BY score DESC
1549
+ LIMIT ${topK}`;
1550
+ const result = await this.turso.execute({
1551
+ sql: query,
1552
+ args: filterValues
1553
+ });
1554
+ return result.rows.map(({ id, score, metadata, embedding }) => ({
1555
+ id,
1556
+ score,
1557
+ metadata: JSON.parse(metadata ?? "{}"),
1558
+ ...includeVector && embedding && { vector: JSON.parse(embedding) }
1559
+ }));
1560
+ } finally {
1561
+ }
1562
+ }
1563
+ async upsert(indexName, vectors, metadata, ids) {
1564
+ const tx = await this.turso.transaction("write");
1565
+ try {
1566
+ const vectorIds = ids || vectors.map(() => crypto.randomUUID());
1567
+ for (let i = 0; i < vectors.length; i++) {
1568
+ const query = `
1569
+ INSERT INTO ${indexName} (vector_id, embedding, metadata)
1570
+ VALUES (?, vector32(?), ?)
1571
+ ON CONFLICT(vector_id) DO UPDATE SET
1572
+ embedding = vector32(?),
1573
+ metadata = ?
1574
+ `;
1575
+ await tx.execute({
1576
+ sql: query,
1577
+ // @ts-ignore
1578
+ args: [
1579
+ vectorIds[i],
1580
+ JSON.stringify(vectors[i]),
1581
+ JSON.stringify(metadata?.[i] || {}),
1582
+ JSON.stringify(vectors[i]),
1583
+ JSON.stringify(metadata?.[i] || {})
1584
+ ]
1585
+ });
1586
+ }
1587
+ await tx.commit();
1588
+ return vectorIds;
1589
+ } catch (error) {
1590
+ await tx.rollback();
1591
+ throw error;
1592
+ }
1593
+ }
1594
+ async createIndex(indexName, dimension, _metric = "cosine") {
1595
+ try {
1596
+ if (!indexName.match(/^[a-zA-Z_][a-zA-Z0-9_]*$/)) {
1597
+ throw new Error("Invalid index name format");
1598
+ }
1599
+ if (!Number.isInteger(dimension) || dimension <= 0) {
1600
+ throw new Error("Dimension must be a positive integer");
1601
+ }
1602
+ await this.turso.execute({
1603
+ sql: `
1604
+ CREATE TABLE IF NOT EXISTS ${indexName} (
1605
+ id SERIAL PRIMARY KEY,
1606
+ vector_id TEXT UNIQUE NOT NULL,
1607
+ embedding F32_BLOB(${dimension}),
1608
+ metadata TEXT DEFAULT '{}'
1609
+ );
1610
+ `,
1611
+ args: []
1612
+ });
1613
+ await this.turso.execute({
1614
+ sql: `
1615
+ CREATE INDEX IF NOT EXISTS ${indexName}_vector_idx
1616
+ ON ${indexName} (libsql_vector_idx(embedding))
1617
+ `,
1618
+ args: []
1619
+ });
1620
+ } catch (error) {
1621
+ console.error("Failed to create vector table:", error);
1622
+ throw error;
1623
+ } finally {
1624
+ }
1625
+ }
1626
+ async deleteIndex(indexName) {
1627
+ try {
1628
+ await this.turso.execute({
1629
+ sql: `DROP TABLE IF EXISTS ${indexName}`,
1630
+ args: []
1631
+ });
1632
+ } catch (error) {
1633
+ console.error("Failed to delete vector table:", error);
1634
+ throw new Error(`Failed to delete vector table: ${error.message}`);
1635
+ } finally {
1636
+ }
1637
+ }
1638
+ async listIndexes() {
1639
+ try {
1640
+ const vectorTablesQuery = `
1641
+ SELECT name FROM sqlite_master
1642
+ WHERE type='table'
1643
+ AND sql LIKE '%F32_BLOB%';
1644
+ `;
1645
+ const result = await this.turso.execute({
1646
+ sql: vectorTablesQuery,
1647
+ args: []
1648
+ });
1649
+ return result.rows.map((row) => row.name);
1650
+ } catch (error) {
1651
+ throw new Error(`Failed to list vector tables: ${error.message}`);
1652
+ }
1653
+ }
1654
+ async describeIndex(indexName) {
1655
+ try {
1656
+ const tableInfoQuery = `
1657
+ SELECT sql
1658
+ FROM sqlite_master
1659
+ WHERE type='table'
1660
+ AND name = ?;
1661
+ `;
1662
+ const tableInfo = await this.turso.execute({
1663
+ sql: tableInfoQuery,
1664
+ args: [indexName]
1665
+ });
1666
+ if (!tableInfo.rows[0]?.sql) {
1667
+ throw new Error(`Table ${indexName} not found`);
1668
+ }
1669
+ const dimension = parseInt(tableInfo.rows[0].sql.match(/F32_BLOB\((\d+)\)/)?.[1] || "0");
1670
+ const countQuery = `
1671
+ SELECT COUNT(*) as count
1672
+ FROM ${indexName};
1673
+ `;
1674
+ const countResult = await this.turso.execute({
1675
+ sql: countQuery,
1676
+ args: []
1677
+ });
1678
+ const metric = "cosine";
1679
+ return {
1680
+ dimension,
1681
+ count: countResult?.rows?.[0]?.count ?? 0,
1682
+ metric
1683
+ };
1684
+ } catch (e) {
1685
+ throw new Error(`Failed to describe vector table: ${e.message}`);
1686
+ }
1687
+ }
1688
+ async truncateIndex(indexName) {
1689
+ await this.turso.execute({
1690
+ sql: `DELETE FROM ${indexName}`,
1691
+ args: []
1692
+ });
1693
+ }
1694
+ };
1695
+
1696
+ // src/memory/memory.ts
1697
+ var MastraMemory = class extends MastraBase {
1698
+ MAX_CONTEXT_TOKENS;
1699
+ storage;
1700
+ vector;
1701
+ embedder;
1702
+ threadConfig = {
1703
+ lastMessages: 40,
1704
+ semanticRecall: true
1705
+ };
1706
+ constructor(config) {
1707
+ super({ component: "MEMORY", name: config.name });
1708
+ this.storage = config.storage || new LibSQLStore({
1709
+ config: {
1710
+ url: "file:memory.db"
1711
+ }
1712
+ });
1713
+ if (config.vector) {
1714
+ this.vector = config.vector;
1715
+ } else {
1716
+ this.vector = new LibSQLVector({
1717
+ connectionUrl: "file:memory-vector.db"
1718
+ // file name needs to be different than default storage or it wont work properly
1719
+ });
1720
+ }
1721
+ if (config.embedder) {
1722
+ this.embedder = config.embedder;
1723
+ } else {
1724
+ this.embedder = defaultEmbedder("bge-small-en-v1.5");
1725
+ }
1726
+ if (config.options) {
1727
+ this.threadConfig = this.getMergedThreadConfig(config.options);
1728
+ }
1729
+ }
1730
+ setStorage(storage) {
1731
+ this.storage = storage;
1732
+ }
1733
+ setVector(vector) {
1734
+ this.vector = vector;
1735
+ }
1736
+ setEmbedder(embedder) {
1737
+ this.embedder = embedder;
1738
+ }
1739
+ /**
1740
+ * Get a system message to inject into the conversation.
1741
+ * This will be called before each conversation turn.
1742
+ * Implementations can override this to inject custom system messages.
1743
+ */
1744
+ async getSystemMessage(_input) {
1745
+ return null;
1746
+ }
1747
+ async createEmbeddingIndex() {
1748
+ const defaultDimensions = 1536;
1749
+ const dimensionsByModelId = {
1750
+ "bge-small-en-v1.5": 384,
1751
+ "bge-base-en-v1.5": 768
1752
+ };
1753
+ const dimensions = dimensionsByModelId[this.embedder.modelId] || defaultDimensions;
1754
+ const isDefault = dimensions === defaultDimensions;
1755
+ const indexName = isDefault ? "memory_messages" : `memory_messages_${dimensions}`;
1756
+ await this.vector.createIndex(indexName, dimensions);
1757
+ return { indexName };
1758
+ }
1759
+ getMergedThreadConfig(config) {
1760
+ return deepMerge(this.threadConfig, config || {});
1761
+ }
1762
+ estimateTokens(text) {
1763
+ return Math.ceil(text.split(" ").length * 1.3);
1764
+ }
1765
+ parseMessages(messages) {
1766
+ return messages.map((msg) => ({
1767
+ ...msg,
1768
+ content: typeof msg.content === "string" && (msg.content.startsWith("[") || msg.content.startsWith("{")) ? JSON.parse(msg.content) : msg.content
1769
+ }));
1770
+ }
1771
+ convertToUIMessages(messages) {
1772
+ function addToolMessageToChat({
1773
+ toolMessage,
1774
+ messages: messages2,
1775
+ toolResultContents
1776
+ }) {
1777
+ const chatMessages2 = messages2.map((message) => {
1778
+ if (message.toolInvocations) {
1779
+ return {
1780
+ ...message,
1781
+ toolInvocations: message.toolInvocations.map((toolInvocation) => {
1782
+ const toolResult = toolMessage.content.find((tool) => tool.toolCallId === toolInvocation.toolCallId);
1783
+ if (toolResult) {
1784
+ return {
1785
+ ...toolInvocation,
1786
+ state: "result",
1787
+ result: toolResult.result
1788
+ };
1789
+ }
1790
+ return toolInvocation;
1791
+ })
1792
+ };
1793
+ }
1794
+ return message;
1795
+ });
1796
+ const resultContents = [...toolResultContents, ...toolMessage.content];
1797
+ return { chatMessages: chatMessages2, toolResultContents: resultContents };
1798
+ }
1799
+ const { chatMessages } = messages.reduce(
1800
+ (obj, message) => {
1801
+ if (message.role === "tool") {
1802
+ return addToolMessageToChat({
1803
+ toolMessage: message,
1804
+ messages: obj.chatMessages,
1805
+ toolResultContents: obj.toolResultContents
1806
+ });
1807
+ }
1808
+ let textContent = "";
1809
+ let toolInvocations = [];
1810
+ if (typeof message.content === "string") {
1811
+ textContent = message.content;
1812
+ } else if (Array.isArray(message.content)) {
1813
+ for (const content of message.content) {
1814
+ if (content.type === "text") {
1815
+ textContent += content.text;
1816
+ } else if (content.type === "tool-call") {
1817
+ const toolResult = obj.toolResultContents.find((tool) => tool.toolCallId === content.toolCallId);
1818
+ toolInvocations.push({
1819
+ state: toolResult ? "result" : "call",
1820
+ toolCallId: content.toolCallId,
1821
+ toolName: content.toolName,
1822
+ args: content.args,
1823
+ result: toolResult?.result
1824
+ });
1825
+ }
1826
+ }
1827
+ }
1828
+ obj.chatMessages.push({
1829
+ id: message.id,
1830
+ role: message.role,
1831
+ content: textContent,
1832
+ toolInvocations
1833
+ });
1834
+ return obj;
1835
+ },
1836
+ { chatMessages: [], toolResultContents: [] }
1837
+ );
1838
+ return chatMessages;
1839
+ }
1840
+ /**
1841
+ * Helper method to create a new thread
1842
+ * @param title - Optional title for the thread
1843
+ * @param metadata - Optional metadata for the thread
1844
+ * @returns Promise resolving to the created thread
1845
+ */
1846
+ async createThread({
1847
+ threadId,
1848
+ resourceId,
1849
+ title,
1850
+ metadata,
1851
+ memoryConfig
1852
+ }) {
1853
+ const thread = {
1854
+ id: threadId || this.generateId(),
1855
+ title: title || "New Thread",
1856
+ resourceId,
1857
+ createdAt: /* @__PURE__ */ new Date(),
1858
+ updatedAt: /* @__PURE__ */ new Date(),
1859
+ metadata
1860
+ };
1861
+ return this.saveThread({ thread, memoryConfig });
1862
+ }
1863
+ /**
1864
+ * Helper method to add a single message to a thread
1865
+ * @param threadId - The thread to add the message to
1866
+ * @param content - The message content
1867
+ * @param role - The role of the message sender
1868
+ * @param type - The type of the message
1869
+ * @param toolNames - Optional array of tool names that were called
1870
+ * @param toolCallArgs - Optional array of tool call arguments
1871
+ * @param toolCallIds - Optional array of tool call ids
1872
+ * @returns Promise resolving to the saved message
1873
+ */
1874
+ async addMessage({
1875
+ threadId,
1876
+ config,
1877
+ content,
1878
+ role,
1879
+ type,
1880
+ toolNames,
1881
+ toolCallArgs,
1882
+ toolCallIds
1883
+ }) {
1884
+ const message = {
1885
+ id: this.generateId(),
1886
+ content,
1887
+ role,
1888
+ createdAt: /* @__PURE__ */ new Date(),
1889
+ threadId,
1890
+ type,
1891
+ toolNames,
1892
+ toolCallArgs,
1893
+ toolCallIds
1894
+ };
1895
+ const savedMessages = await this.saveMessages({ messages: [message], memoryConfig: config });
1896
+ return savedMessages[0];
1897
+ }
1898
+ /**
1899
+ * Generates a unique identifier
1900
+ * @returns A unique string ID
1901
+ */
1902
+ generateId() {
1903
+ return crypto.randomUUID();
1904
+ }
1905
+ };
1906
+
1907
+ exports.MastraMemory = MastraMemory;