@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/.turbo/turbo-build.log +7 -7
- package/CHANGELOG.md +18 -0
- package/dist/_tsup-dts-rollup.d.cts +60 -20
- package/dist/_tsup-dts-rollup.d.ts +60 -20
- package/dist/index.cjs +190 -127
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +190 -127
- package/package.json +5 -5
- package/src/storage/index.test.ts +0 -1
- package/src/storage/index.ts +102 -31
- package/src/vector/index.ts +166 -128
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
|
-
|
|
502
|
-
|
|
503
|
-
|
|
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
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
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
|
-
|
|
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
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
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
|
-
|
|
602
|
-
|
|
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
|
|
632
|
-
|
|
633
|
-
|
|
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
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
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
|
-
|
|
735
|
-
|
|
736
|
-
|
|
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
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
}
|
|
756
|
-
|
|
757
|
-
|
|
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
|
|
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
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
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
|
-
|
|
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
|
-
|
|
860
|
-
|
|
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
|
-
|
|
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.
|
|
1294
|
-
updatedAt: new Date(row.
|
|
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';
|