@mastra/libsql 1.8.2-alpha.0 → 1.9.0-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -509,6 +509,9 @@ var LibSQLVector = class extends MastraVector {
509
509
  turso;
510
510
  maxRetries;
511
511
  initialBackoffMs;
512
+ overFetchMultiplier;
513
+ isMemoryDb;
514
+ vectorIndexes;
512
515
  constructor({
513
516
  url,
514
517
  authToken,
@@ -516,6 +519,7 @@ var LibSQLVector = class extends MastraVector {
516
519
  syncInterval,
517
520
  maxRetries = 5,
518
521
  initialBackoffMs = 100,
522
+ vectorTopKOverFetchMultiplier = 10,
519
523
  id
520
524
  }) {
521
525
  super({ id });
@@ -527,10 +531,27 @@ var LibSQLVector = class extends MastraVector {
527
531
  });
528
532
  this.maxRetries = maxRetries;
529
533
  this.initialBackoffMs = initialBackoffMs;
530
- if (url.includes(`file:`) || url.includes(`:memory:`)) {
534
+ if (!Number.isInteger(vectorTopKOverFetchMultiplier) || vectorTopKOverFetchMultiplier < 1) {
535
+ throw new Error("vectorTopKOverFetchMultiplier must be a positive integer");
536
+ }
537
+ this.overFetchMultiplier = vectorTopKOverFetchMultiplier;
538
+ this.isMemoryDb = url.includes(":memory:");
539
+ if (url.includes(`file:`) || this.isMemoryDb) {
531
540
  this.turso.execute("PRAGMA journal_mode=WAL;").then(() => this.logger.debug("LibSQLStore: PRAGMA journal_mode=WAL set.")).catch((err) => this.logger.warn("LibSQLStore: Failed to set PRAGMA journal_mode=WAL.", err));
532
541
  this.turso.execute("PRAGMA busy_timeout = 5000;").then(() => this.logger.debug("LibSQLStore: PRAGMA busy_timeout=5000 set.")).catch((err) => this.logger.warn("LibSQLStore: Failed to set PRAGMA busy_timeout=5000.", err));
533
542
  }
543
+ this.vectorIndexes = this.isMemoryDb ? Promise.resolve(/* @__PURE__ */ new Set()) : this.discoverVectorIndexes();
544
+ }
545
+ async discoverVectorIndexes() {
546
+ try {
547
+ const result = await this.turso.execute({
548
+ sql: `SELECT name FROM sqlite_master WHERE type='index' AND name LIKE '%_vector_idx'`,
549
+ args: []
550
+ });
551
+ return new Set(result.rows.map((row) => row.name));
552
+ } catch {
553
+ return /* @__PURE__ */ new Set();
554
+ }
534
555
  }
535
556
  async executeWriteOperationWithRetry(operation, isTransaction = false) {
536
557
  let attempts = 0;
@@ -564,6 +585,40 @@ var LibSQLVector = class extends MastraVector {
564
585
  const translator = new LibSQLFilterTranslator();
565
586
  return translator.translate(filter);
566
587
  }
588
+ async hasVectorIndex(parsedIndexName) {
589
+ const indexes = await this.vectorIndexes;
590
+ return indexes.has(`${parsedIndexName}_vector_idx`);
591
+ }
592
+ async queryWithIndex(parsedIndexName, vectorStr, topK, filter, includeVector, minScore) {
593
+ const translatedFilter = this.transformFilter(filter);
594
+ const { sql: filterQuery, values: filterValues } = buildFilterQuery(translatedFilter);
595
+ const hasFilter = filterQuery.length > 0;
596
+ const fetchCount = hasFilter ? topK * this.overFetchMultiplier : topK * 2;
597
+ const embeddingSelect = includeVector ? ", vector_extract(t.embedding) as embedding" : "";
598
+ const filterCondition = hasFilter ? filterQuery.replace(/^\s*WHERE\s+/i, "") : "";
599
+ const whereClause = hasFilter ? `WHERE ${filterCondition} AND score > ?` : "WHERE score > ?";
600
+ const query = `
601
+ WITH candidates AS (
602
+ SELECT t.vector_id AS id,
603
+ (1 - vector_distance_cos(t.embedding, vector32(?))) AS score,
604
+ t.metadata
605
+ ${embeddingSelect}
606
+ FROM vector_top_k('${parsedIndexName}_vector_idx', vector32(?), ?) AS v
607
+ JOIN "${parsedIndexName}" AS t ON t.rowid = v.id
608
+ )
609
+ SELECT * FROM candidates
610
+ ${whereClause}
611
+ ORDER BY score DESC
612
+ LIMIT ?`;
613
+ const args = [vectorStr, vectorStr, fetchCount, ...filterValues, minScore, topK];
614
+ const result = await this.turso.execute({ sql: query, args });
615
+ return result.rows.map(({ id, score, metadata, embedding }) => ({
616
+ id,
617
+ score,
618
+ metadata: JSON.parse(metadata ?? "{}"),
619
+ ...includeVector && embedding && { vector: JSON.parse(embedding) }
620
+ }));
621
+ }
567
622
  async query({
568
623
  indexName,
569
624
  queryVector,
@@ -594,6 +649,23 @@ var LibSQLVector = class extends MastraVector {
594
649
  try {
595
650
  const parsedIndexName = parseSqlIdentifier(indexName, "index name");
596
651
  const vectorStr = `[${queryVector.join(",")}]`;
652
+ if (!this.isMemoryDb && await this.hasVectorIndex(parsedIndexName)) {
653
+ try {
654
+ const indexedResults = await this.queryWithIndex(
655
+ parsedIndexName,
656
+ vectorStr,
657
+ topK,
658
+ filter,
659
+ includeVector,
660
+ minScore
661
+ );
662
+ if (!filter || indexedResults.length >= topK) {
663
+ return indexedResults;
664
+ }
665
+ } catch (err) {
666
+ this.logger.warn("LibSQLVector: indexed query failed, falling back to brute-force", err);
667
+ }
668
+ }
597
669
  const translatedFilter = this.transformFilter(filter);
598
670
  const { sql: filterQuery, values: filterValues } = buildFilterQuery(translatedFilter);
599
671
  filterValues.push(minScore);
@@ -727,6 +799,7 @@ var LibSQLVector = class extends MastraVector {
727
799
  `,
728
800
  args: []
729
801
  });
802
+ void this.vectorIndexes.then((indexes) => indexes.add(`${parsedIndexName}_vector_idx`));
730
803
  }
731
804
  deleteIndex(args) {
732
805
  try {
@@ -749,6 +822,7 @@ var LibSQLVector = class extends MastraVector {
749
822
  sql: `DROP TABLE IF EXISTS ${parsedIndexName}`,
750
823
  args: []
751
824
  });
825
+ void this.vectorIndexes.then((indexes) => indexes.delete(`${parsedIndexName}_vector_idx`));
752
826
  }
753
827
  async listIndexes() {
754
828
  try {
@@ -1285,6 +1359,9 @@ function transformFromSqlRow({
1285
1359
  const dateColumns = new Set(
1286
1360
  Object.keys(TABLE_SCHEMAS[tableName]).filter((key) => TABLE_SCHEMAS[tableName][key].type === "timestamp").map((key) => key)
1287
1361
  );
1362
+ const booleanColumns = new Set(
1363
+ Object.keys(TABLE_SCHEMAS[tableName]).filter((key) => TABLE_SCHEMAS[tableName][key].type === "boolean").map((key) => key)
1364
+ );
1288
1365
  for (const [key, value] of Object.entries(sqlRow)) {
1289
1366
  if (value === null || value === void 0) {
1290
1367
  result[key] = value;
@@ -1298,6 +1375,10 @@ function transformFromSqlRow({
1298
1375
  result[key] = safelyParseJSON(value);
1299
1376
  continue;
1300
1377
  }
1378
+ if (booleanColumns.has(key)) {
1379
+ result[key] = Boolean(value);
1380
+ continue;
1381
+ }
1301
1382
  result[key] = value;
1302
1383
  }
1303
1384
  return result;
@@ -7850,6 +7931,39 @@ var ObservabilityLibSQL = class extends ObservabilityStorage {
7850
7931
  );
7851
7932
  }
7852
7933
  }
7934
+ async getTraceLight(args) {
7935
+ const { traceId } = args;
7936
+ try {
7937
+ const spans = await this.#db.selectMany({
7938
+ tableName: TABLE_SPANS,
7939
+ whereClause: { sql: " WHERE traceId = ?", args: [traceId] },
7940
+ orderBy: "startedAt ASC"
7941
+ });
7942
+ if (!spans || spans.length === 0) {
7943
+ return null;
7944
+ }
7945
+ return {
7946
+ traceId,
7947
+ spans: spans.map((span) => {
7948
+ const transformed = transformFromSqlRow({ tableName: TABLE_SPANS, sqlRow: span });
7949
+ const { input, output, attributes, metadata, tags, links, ...light } = transformed;
7950
+ return light;
7951
+ })
7952
+ };
7953
+ } catch (error) {
7954
+ throw new MastraError(
7955
+ {
7956
+ id: createStorageErrorId("LIBSQL", "GET_TRACE_LIGHT", "FAILED"),
7957
+ domain: ErrorDomain.STORAGE,
7958
+ category: ErrorCategory.USER,
7959
+ details: {
7960
+ traceId
7961
+ }
7962
+ },
7963
+ error
7964
+ );
7965
+ }
7966
+ }
7853
7967
  async updateSpan(args) {
7854
7968
  const { traceId, spanId, updates } = args;
7855
7969
  try {