@mastra/libsql 0.10.0 → 0.10.1-alpha.1

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.cjs CHANGED
@@ -484,11 +484,15 @@ var processOperator = (key, operator, operatorValue) => {
484
484
  // src/vector/index.ts
485
485
  var LibSQLVector = class extends vector.MastraVector {
486
486
  turso;
487
+ maxRetries;
488
+ initialBackoffMs;
487
489
  constructor({
488
490
  connectionUrl,
489
491
  authToken,
490
492
  syncUrl,
491
- syncInterval
493
+ syncInterval,
494
+ maxRetries = 5,
495
+ initialBackoffMs = 100
492
496
  }) {
493
497
  super();
494
498
  this.turso = client.createClient({
@@ -497,12 +501,40 @@ var LibSQLVector = class extends vector.MastraVector {
497
501
  authToken,
498
502
  syncInterval
499
503
  });
504
+ this.maxRetries = maxRetries;
505
+ this.initialBackoffMs = initialBackoffMs;
500
506
  if (connectionUrl.includes(`file:`) || connectionUrl.includes(`:memory:`)) {
501
- void this.turso.execute({
502
- sql: "PRAGMA journal_mode=WAL;",
503
- args: {}
504
- });
507
+ 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));
508
+ 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));
509
+ }
510
+ }
511
+ async executeWriteOperationWithRetry(operation, isTransaction = false) {
512
+ let attempts = 0;
513
+ let backoff = this.initialBackoffMs;
514
+ while (attempts < this.maxRetries) {
515
+ try {
516
+ return await operation();
517
+ } catch (error) {
518
+ if (error.code === "SQLITE_BUSY" || error.message && error.message.toLowerCase().includes("database is locked")) {
519
+ attempts++;
520
+ if (attempts >= this.maxRetries) {
521
+ this.logger.error(
522
+ `LibSQLVector: Operation failed after ${this.maxRetries} attempts due to: ${error.message}`,
523
+ error
524
+ );
525
+ throw error;
526
+ }
527
+ this.logger.warn(
528
+ `LibSQLVector: Attempt ${attempts} failed due to ${isTransaction ? "transaction " : ""}database lock. Retrying in ${backoff}ms...`
529
+ );
530
+ await new Promise((resolve) => setTimeout(resolve, backoff));
531
+ backoff *= 2;
532
+ } else {
533
+ throw error;
534
+ }
535
+ }
505
536
  }
537
+ throw new Error("LibSQLVector: Max retries reached, but no error was re-thrown from the loop.");
506
538
  }
507
539
  transformFilter(filter) {
508
540
  const translator = new LibSQLFilterTranslator();
@@ -530,20 +562,20 @@ var LibSQLVector = class extends vector.MastraVector {
530
562
  filterValues.push(minScore);
531
563
  filterValues.push(topK);
532
564
  const query = `
533
- WITH vector_scores AS (
534
- SELECT
535
- vector_id as id,
536
- (1-vector_distance_cos(embedding, '${vectorStr}')) as score,
537
- metadata
538
- ${includeVector ? ", vector_extract(embedding) as embedding" : ""}
539
- FROM ${parsedIndexName}
540
- ${filterQuery}
541
- )
542
- SELECT *
543
- FROM vector_scores
544
- WHERE score > ?
545
- ORDER BY score DESC
546
- LIMIT ?`;
565
+ WITH vector_scores AS (
566
+ SELECT
567
+ vector_id as id,
568
+ (1-vector_distance_cos(embedding, '${vectorStr}')) as score,
569
+ metadata
570
+ ${includeVector ? ", vector_extract(embedding) as embedding" : ""}
571
+ FROM ${parsedIndexName}
572
+ ${filterQuery}
573
+ )
574
+ SELECT *
575
+ FROM vector_scores
576
+ WHERE score > ?
577
+ ORDER BY score DESC
578
+ LIMIT ?`;
547
579
  const result = await this.turso.execute({
548
580
  sql: query,
549
581
  args: filterValues
@@ -557,22 +589,24 @@ var LibSQLVector = class extends vector.MastraVector {
557
589
  } finally {
558
590
  }
559
591
  }
560
- async upsert({ indexName, vectors, metadata, ids }) {
592
+ upsert(args) {
593
+ return this.executeWriteOperationWithRetry(() => this.doUpsert(args), true);
594
+ }
595
+ async doUpsert({ indexName, vectors, metadata, ids }) {
561
596
  const tx = await this.turso.transaction("write");
562
597
  try {
563
598
  const parsedIndexName = utils.parseSqlIdentifier(indexName, "index name");
564
599
  const vectorIds = ids || vectors.map(() => crypto.randomUUID());
565
600
  for (let i = 0; i < vectors.length; i++) {
566
601
  const query = `
567
- INSERT INTO ${parsedIndexName} (vector_id, embedding, metadata)
568
- VALUES (?, vector32(?), ?)
569
- ON CONFLICT(vector_id) DO UPDATE SET
570
- embedding = vector32(?),
571
- metadata = ?
572
- `;
602
+ INSERT INTO ${parsedIndexName} (vector_id, embedding, metadata)
603
+ VALUES (?, vector32(?), ?)
604
+ ON CONFLICT(vector_id) DO UPDATE SET
605
+ embedding = vector32(?),
606
+ metadata = ?
607
+ `;
573
608
  await tx.execute({
574
609
  sql: query,
575
- // @ts-ignore
576
610
  args: [
577
611
  vectorIds[i],
578
612
  JSON.stringify(vectors[i]),
@@ -598,48 +632,42 @@ var LibSQLVector = class extends vector.MastraVector {
598
632
  throw error;
599
633
  }
600
634
  }
601
- async createIndex({ indexName, dimension }) {
602
- try {
603
- if (!Number.isInteger(dimension) || dimension <= 0) {
604
- throw new Error("Dimension must be a positive integer");
605
- }
606
- const parsedIndexName = utils.parseSqlIdentifier(indexName, "index name");
607
- await this.turso.execute({
608
- sql: `
609
- CREATE TABLE IF NOT EXISTS ${parsedIndexName} (
610
- id SERIAL PRIMARY KEY,
611
- vector_id TEXT UNIQUE NOT NULL,
612
- embedding F32_BLOB(${dimension}),
613
- metadata TEXT DEFAULT '{}'
614
- );
615
- `,
616
- args: []
617
- });
618
- await this.turso.execute({
619
- sql: `
620
- CREATE INDEX IF NOT EXISTS ${parsedIndexName}_vector_idx
621
- ON ${parsedIndexName} (libsql_vector_idx(embedding))
622
- `,
623
- args: []
624
- });
625
- } catch (error) {
626
- console.error("Failed to create vector table:", error);
627
- throw error;
628
- } finally {
629
- }
635
+ createIndex(args) {
636
+ return this.executeWriteOperationWithRetry(() => this.doCreateIndex(args));
630
637
  }
631
- async deleteIndex({ indexName }) {
632
- try {
633
- const parsedIndexName = utils.parseSqlIdentifier(indexName, "index name");
634
- await this.turso.execute({
635
- sql: `DROP TABLE IF EXISTS ${parsedIndexName}`,
636
- args: []
637
- });
638
- } catch (error) {
639
- console.error("Failed to delete vector table:", error);
640
- throw new Error(`Failed to delete vector table: ${error.message}`);
641
- } finally {
638
+ async doCreateIndex({ indexName, dimension }) {
639
+ if (!Number.isInteger(dimension) || dimension <= 0) {
640
+ throw new Error("Dimension must be a positive integer");
642
641
  }
642
+ const parsedIndexName = utils.parseSqlIdentifier(indexName, "index name");
643
+ await this.turso.execute({
644
+ sql: `
645
+ CREATE TABLE IF NOT EXISTS ${parsedIndexName} (
646
+ id SERIAL PRIMARY KEY,
647
+ vector_id TEXT UNIQUE NOT NULL,
648
+ embedding F32_BLOB(${dimension}),
649
+ metadata TEXT DEFAULT '{}'
650
+ );
651
+ `,
652
+ args: []
653
+ });
654
+ await this.turso.execute({
655
+ sql: `
656
+ CREATE INDEX IF NOT EXISTS ${parsedIndexName}_vector_idx
657
+ ON ${parsedIndexName} (libsql_vector_idx(embedding))
658
+ `,
659
+ args: []
660
+ });
661
+ }
662
+ deleteIndex(args) {
663
+ return this.executeWriteOperationWithRetry(() => this.doDeleteIndex(args));
664
+ }
665
+ async doDeleteIndex({ indexName }) {
666
+ const parsedIndexName = utils.parseSqlIdentifier(indexName, "index name");
667
+ await this.turso.execute({
668
+ sql: `DROP TABLE IF EXISTS ${parsedIndexName}`,
669
+ args: []
670
+ });
643
671
  }
644
672
  async listIndexes() {
645
673
  try {
@@ -709,35 +737,34 @@ var LibSQLVector = class extends vector.MastraVector {
709
737
  * @returns A promise that resolves when the update is complete.
710
738
  * @throws Will throw an error if no updates are provided or if the update operation fails.
711
739
  */
712
- async updateVector({ indexName, id, update }) {
713
- try {
714
- const parsedIndexName = utils.parseSqlIdentifier(indexName, "index name");
715
- const updates = [];
716
- const args = [];
717
- if (update.vector) {
718
- updates.push("embedding = vector32(?)");
719
- args.push(JSON.stringify(update.vector));
720
- }
721
- if (update.metadata) {
722
- updates.push("metadata = ?");
723
- args.push(JSON.stringify(update.metadata));
724
- }
725
- if (updates.length === 0) {
726
- throw new Error("No updates provided");
727
- }
728
- args.push(id);
729
- const query = `
740
+ updateVector(args) {
741
+ return this.executeWriteOperationWithRetry(() => this.doUpdateVector(args));
742
+ }
743
+ async doUpdateVector({ indexName, id, update }) {
744
+ const parsedIndexName = utils.parseSqlIdentifier(indexName, "index name");
745
+ const updates = [];
746
+ const args = [];
747
+ if (update.vector) {
748
+ updates.push("embedding = vector32(?)");
749
+ args.push(JSON.stringify(update.vector));
750
+ }
751
+ if (update.metadata) {
752
+ updates.push("metadata = ?");
753
+ args.push(JSON.stringify(update.metadata));
754
+ }
755
+ if (updates.length === 0) {
756
+ throw new Error("No updates provided");
757
+ }
758
+ args.push(id);
759
+ const query = `
730
760
  UPDATE ${parsedIndexName}
731
761
  SET ${updates.join(", ")}
732
762
  WHERE vector_id = ?;
733
763
  `;
734
- await this.turso.execute({
735
- sql: query,
736
- args
737
- });
738
- } catch (error) {
739
- throw new Error(`Failed to update vector by id: ${id} for index: ${indexName}: ${error.message}`);
740
- }
764
+ await this.turso.execute({
765
+ sql: query,
766
+ args
767
+ });
741
768
  }
742
769
  /**
743
770
  * Deletes a vector by its ID.
@@ -746,18 +773,20 @@ var LibSQLVector = class extends vector.MastraVector {
746
773
  * @returns A promise that resolves when the deletion is complete.
747
774
  * @throws Will throw an error if the deletion operation fails.
748
775
  */
749
- async deleteVector({ indexName, id }) {
750
- try {
751
- const parsedIndexName = utils.parseSqlIdentifier(indexName, "index name");
752
- await this.turso.execute({
753
- sql: `DELETE FROM ${parsedIndexName} WHERE vector_id = ?`,
754
- args: [id]
755
- });
756
- } catch (error) {
757
- throw new Error(`Failed to delete vector by id: ${id} for index: ${indexName}: ${error.message}`);
758
- }
776
+ deleteVector(args) {
777
+ return this.executeWriteOperationWithRetry(() => this.doDeleteVector(args));
778
+ }
779
+ async doDeleteVector({ indexName, id }) {
780
+ const parsedIndexName = utils.parseSqlIdentifier(indexName, "index name");
781
+ await this.turso.execute({
782
+ sql: `DELETE FROM ${parsedIndexName} WHERE vector_id = ?`,
783
+ args: [id]
784
+ });
785
+ }
786
+ truncateIndex(args) {
787
+ return this.executeWriteOperationWithRetry(() => this._doTruncateIndex(args));
759
788
  }
760
- async truncateIndex({ indexName }) {
789
+ async _doTruncateIndex({ indexName }) {
761
790
  await this.turso.execute({
762
791
  sql: `DELETE FROM ${utils.parseSqlIdentifier(indexName, "index name")}`,
763
792
  args: []
@@ -773,12 +802,20 @@ function safelyParseJSON(jsonString) {
773
802
  }
774
803
  var LibSQLStore = class extends storage.MastraStorage {
775
804
  client;
805
+ maxRetries;
806
+ initialBackoffMs;
776
807
  constructor(config) {
777
808
  super({ name: `LibSQLStore` });
809
+ this.maxRetries = config.maxRetries ?? 5;
810
+ this.initialBackoffMs = config.initialBackoffMs ?? 100;
778
811
  if (config.url.endsWith(":memory:")) {
779
812
  this.shouldCacheInit = false;
780
813
  }
781
814
  this.client = client.createClient(config);
815
+ if (config.url.startsWith("file:") || config.url.includes(":memory:")) {
816
+ this.client.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));
817
+ this.client.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.", err));
818
+ }
782
819
  }
783
820
  getCreateTableSQL(tableName, schema) {
784
821
  const parsedTableName = utils.parseSqlIdentifier(tableName, "table name");
@@ -841,28 +878,53 @@ var LibSQLStore = class extends storage.MastraStorage {
841
878
  args: values
842
879
  };
843
880
  }
844
- async insert({ tableName, record }) {
845
- try {
846
- await this.client.execute(
847
- this.prepareStatement({
848
- tableName,
849
- record
850
- })
851
- );
852
- } catch (error) {
853
- this.logger.error(`Error upserting into table ${tableName}: ${error}`);
854
- throw error;
881
+ async executeWriteOperationWithRetry(operationFn, operationDescription) {
882
+ let retries = 0;
883
+ while (true) {
884
+ try {
885
+ return await operationFn();
886
+ } catch (error) {
887
+ if (error.message && (error.message.includes("SQLITE_BUSY") || error.message.includes("database is locked")) && retries < this.maxRetries) {
888
+ retries++;
889
+ const backoffTime = this.initialBackoffMs * Math.pow(2, retries - 1);
890
+ this.logger.warn(
891
+ `LibSQLStore: Encountered SQLITE_BUSY during ${operationDescription}. Retrying (${retries}/${this.maxRetries}) in ${backoffTime}ms...`
892
+ );
893
+ await new Promise((resolve) => setTimeout(resolve, backoffTime));
894
+ } else {
895
+ this.logger.error(`LibSQLStore: Error during ${operationDescription} after ${retries} retries: ${error}`);
896
+ throw error;
897
+ }
898
+ }
855
899
  }
856
900
  }
857
- async batchInsert({ tableName, records }) {
901
+ insert(args) {
902
+ return this.executeWriteOperationWithRetry(() => this.doInsert(args), `insert into table ${args.tableName}`);
903
+ }
904
+ async doInsert({
905
+ tableName,
906
+ record
907
+ }) {
908
+ await this.client.execute(
909
+ this.prepareStatement({
910
+ tableName,
911
+ record
912
+ })
913
+ );
914
+ }
915
+ batchInsert(args) {
916
+ return this.executeWriteOperationWithRetry(
917
+ () => this.doBatchInsert(args),
918
+ `batch insert into table ${args.tableName}`
919
+ );
920
+ }
921
+ async doBatchInsert({
922
+ tableName,
923
+ records
924
+ }) {
858
925
  if (records.length === 0) return;
859
- try {
860
- const batchStatements = records.map((r) => this.prepareStatement({ tableName, record: r }));
861
- await this.client.batch(batchStatements, "write");
862
- } catch (error) {
863
- this.logger.error(`Error upserting into table ${tableName}: ${error}`);
864
- throw error;
865
- }
926
+ const batchStatements = records.map((r) => this.prepareStatement({ tableName, record: r }));
927
+ await this.client.batch(batchStatements, "write");
866
928
  }
867
929
  async load({ tableName, keys }) {
868
930
  const parsedTableName = utils.parseSqlIdentifier(tableName, "table name");
@@ -963,14 +1025,15 @@ var LibSQLStore = class extends storage.MastraStorage {
963
1025
  content = JSON.parse(row.content);
964
1026
  } catch {
965
1027
  }
966
- return {
1028
+ const result = {
967
1029
  id: row.id,
968
1030
  content,
969
1031
  role: row.role,
970
- type: row.type,
971
1032
  createdAt: new Date(row.createdAt),
972
1033
  threadId: row.thread_id
973
1034
  };
1035
+ if (row.type && row.type !== `v2`) result.type = row.type;
1036
+ return result;
974
1037
  }
975
1038
  async getMessages({ threadId, selectBy }) {
976
1039
  try {
@@ -1058,7 +1121,7 @@ var LibSQLStore = class extends storage.MastraStorage {
1058
1121
  threadId,
1059
1122
  typeof message.content === "object" ? JSON.stringify(message.content) : message.content,
1060
1123
  message.role,
1061
- message.type,
1124
+ message.type || "v2",
1062
1125
  time instanceof Date ? time.toISOString() : time
1063
1126
  ]
1064
1127
  };
@@ -1290,8 +1353,8 @@ var LibSQLStore = class extends storage.MastraStorage {
1290
1353
  runId: row.run_id,
1291
1354
  snapshot: parsedSnapshot,
1292
1355
  resourceId: row.resourceId,
1293
- createdAt: new Date(row.created_at),
1294
- updatedAt: new Date(row.updated_at)
1356
+ createdAt: new Date(row.createdAt),
1357
+ updatedAt: new Date(row.updatedAt)
1295
1358
  };
1296
1359
  }
1297
1360
  };
package/dist/index.d.cts CHANGED
@@ -1,4 +1,5 @@
1
1
  export { LIBSQL_PROMPT } from './_tsup-dts-rollup.cjs';
2
+ export { LibSQLVectorConfig } from './_tsup-dts-rollup.cjs';
2
3
  export { LibSQLVector } from './_tsup-dts-rollup.cjs';
3
4
  export { LibSQLConfig } from './_tsup-dts-rollup.cjs';
4
5
  export { LibSQLStore } from './_tsup-dts-rollup.cjs';
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export { LIBSQL_PROMPT } from './_tsup-dts-rollup.js';
2
+ export { LibSQLVectorConfig } from './_tsup-dts-rollup.js';
2
3
  export { LibSQLVector } from './_tsup-dts-rollup.js';
3
4
  export { LibSQLConfig } from './_tsup-dts-rollup.js';
4
5
  export { LibSQLStore } from './_tsup-dts-rollup.js';