@storecraft/database-turso 1.0.9 → 1.0.11
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/README.md +18 -6
- package/index.js +32 -4
- package/jsconfig.json +1 -1
- package/kysely.turso.dialect.js +21 -17
- package/package.json +5 -1
- package/tests/runner.test.js +1 -4
- package/tests/sandbox.js +0 -4
- package/types.public.d.ts +18 -5
- package/vector-store/index.js +302 -0
- package/vector-store/types.d.ts +48 -0
- package/vector-store/types.private.d.ts +16 -0
package/README.md
CHANGED
@@ -5,14 +5,18 @@
|
|
5
5
|
width='90%' />
|
6
6
|
</div><hr/><br/>
|
7
7
|
|
8
|
-
Official `
|
8
|
+
Official `libSql` / `Turso` driver for `StoreCraft` on any platforms.
|
9
|
+
Includes a vector store.
|
9
10
|
|
10
11
|
```bash
|
11
12
|
npm i @storecraft/database-turso
|
12
13
|
```
|
13
14
|
|
14
15
|
## Setup
|
16
|
+
You can run a local database,
|
17
|
+
or,
|
15
18
|
|
19
|
+
connect to a cloud `libsql` and `Turso` platform
|
16
20
|
- First, login to your [turso](https://turso.tech) account.
|
17
21
|
- Create a database.
|
18
22
|
- Create an API Key.
|
@@ -26,7 +30,7 @@ import http from "node:http";
|
|
26
30
|
import { App } from '@storecraft/core'
|
27
31
|
import { NodePlatform } from '@storecraft/core/platform/node';
|
28
32
|
import { NodeLocalStorage } from '@storecraft/core/storage/node'
|
29
|
-
import { Turso } from '@storecraft/database-turso'
|
33
|
+
import { Turso, LibSQLVectorStore } from '@storecraft/database-turso'
|
30
34
|
import { migrateToLatest } from '@storecraft/database-turso/migrate.js'
|
31
35
|
|
32
36
|
const app = new App(
|
@@ -41,14 +45,22 @@ const app = new App(
|
|
41
45
|
new Turso(
|
42
46
|
{
|
43
47
|
prefers_batch_over_transactions: true,
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
+
// all of these configurations can be inferred by env variables at init
|
49
|
+
url: process.env.LIBSQL_URL,
|
50
|
+
authToken: process.env.LIBSQL_API_TOKEN,
|
51
|
+
// or local
|
52
|
+
url: 'file:local.db',
|
48
53
|
}
|
49
54
|
)
|
50
55
|
)
|
51
56
|
.withStorage(new NodeLocalStorage('storage'))
|
57
|
+
.withVectorStore(
|
58
|
+
new LibSQLVectorStore(
|
59
|
+
{
|
60
|
+
embedder: new OpenAIEmbedder()
|
61
|
+
}
|
62
|
+
)
|
63
|
+
)
|
52
64
|
|
53
65
|
await app.init();
|
54
66
|
await migrateToLatest(app.db, false);
|
package/index.js
CHANGED
@@ -1,23 +1,51 @@
|
|
1
|
+
/**
|
2
|
+
* @import { Config } from './types.public.js';
|
3
|
+
* @import { ENV } from '@storecraft/core';
|
4
|
+
*/
|
5
|
+
|
1
6
|
import { SQL } from '@storecraft/database-sql-base';
|
2
7
|
import { LibsqlDialect } from './kysely.turso.dialect.js';
|
3
8
|
|
9
|
+
export { LibSQLVectorStore } from './vector-store/index.js'
|
10
|
+
|
4
11
|
/**
|
5
12
|
* @extends {SQL}
|
6
13
|
*/
|
7
14
|
export class Turso extends SQL {
|
8
15
|
|
16
|
+
/** @satisfies {ENV<Config>} */
|
17
|
+
static EnvConfig = /** @type{const} */ ({
|
18
|
+
authToken: 'LIBSQL_AUTH_TOKEN',
|
19
|
+
url: 'LIBSQL_URL'
|
20
|
+
});
|
21
|
+
|
9
22
|
/**
|
10
23
|
*
|
11
|
-
* @param {
|
24
|
+
* @param {Config} [config] config
|
12
25
|
*/
|
13
|
-
constructor(config) {
|
26
|
+
constructor(config={}) {
|
14
27
|
super(
|
15
28
|
{
|
16
29
|
dialect_type: 'SQLITE',
|
17
|
-
dialect: new LibsqlDialect(
|
30
|
+
dialect: new LibsqlDialect(
|
31
|
+
{
|
32
|
+
...config,
|
33
|
+
prefers_batch_over_transactions: config.prefers_batch_over_transactions ?? true
|
34
|
+
}
|
35
|
+
),
|
18
36
|
}
|
19
37
|
);
|
20
|
-
|
21
38
|
}
|
39
|
+
|
22
40
|
|
41
|
+
/** @type {SQL["init"]} */
|
42
|
+
init = (app) => {
|
43
|
+
const dialect = /** @type {LibsqlDialect}*/ (this.config.dialect);
|
44
|
+
const dconfig = dialect.config;
|
45
|
+
|
46
|
+
dconfig.authToken ??= app.platform.env[Turso.EnvConfig.authToken];
|
47
|
+
dconfig.url ??= app.platform.env[Turso.EnvConfig.url];
|
48
|
+
|
49
|
+
super.init(app);
|
50
|
+
}
|
23
51
|
}
|
package/jsconfig.json
CHANGED
package/kysely.turso.dialect.js
CHANGED
@@ -1,12 +1,10 @@
|
|
1
|
-
import * as libsql from "@libsql/client";
|
2
|
-
import * as kysely from "kysely";
|
3
|
-
|
4
1
|
/**
|
5
|
-
* @
|
6
|
-
* @
|
7
|
-
* @
|
8
|
-
* @typedef {import('./types.public.d.ts').Config} Config
|
2
|
+
* @import { Driver, Dialect, DatabaseConnection, QueryResult } from 'kysely';
|
3
|
+
* @import { Config } from './types.public.js';
|
4
|
+
* @import { Row, InArgs } from '@libsql/client';
|
9
5
|
*/
|
6
|
+
import * as libsql from "@libsql/client";
|
7
|
+
import * as kysely from "kysely";
|
10
8
|
|
11
9
|
/**
|
12
10
|
*
|
@@ -24,18 +22,23 @@ export class LibsqlDialect {
|
|
24
22
|
this.#config = config;
|
25
23
|
}
|
26
24
|
|
25
|
+
get config() {
|
26
|
+
return this.#config;
|
27
|
+
}
|
28
|
+
|
27
29
|
createAdapter() { return new kysely.SqliteAdapter(); }
|
28
30
|
createQueryCompiler() { return new kysely.SqliteQueryCompiler(); }
|
29
31
|
createDriver() {
|
30
32
|
|
31
|
-
if (this.#config?.
|
33
|
+
if (this.#config?.url===undefined) {
|
32
34
|
throw new Error(
|
33
35
|
"Please specify either `client` or `url` in the LibsqlDialect config"
|
34
36
|
);
|
35
37
|
}
|
36
38
|
|
37
39
|
return new LibsqlDriver(
|
38
|
-
|
40
|
+
// @ts-ignore
|
41
|
+
libsql.createClient(this.#config),
|
39
42
|
this.#config
|
40
43
|
);
|
41
44
|
}
|
@@ -134,16 +137,17 @@ export class LibsqlConnection {
|
|
134
137
|
/**
|
135
138
|
* @param {kysely.CompiledQuery[]} compiledQueries
|
136
139
|
*
|
137
|
-
* @returns {Promise<
|
140
|
+
* @returns {Promise<QueryResult<Row>>}
|
138
141
|
*/
|
139
142
|
async #internal_executeQuery(compiledQueries) {
|
143
|
+
// console.log(compiledQueries)
|
140
144
|
const target = this.#transaction ?? this.client;
|
141
145
|
|
142
146
|
const stmts = compiledQueries.map(
|
143
147
|
cq => (
|
144
148
|
{
|
145
149
|
sql: cq.sql,
|
146
|
-
args: (/** @type {
|
150
|
+
args: (/** @type {InArgs} */ (cq.parameters)),
|
147
151
|
}
|
148
152
|
)
|
149
153
|
);
|
@@ -153,8 +157,8 @@ export class LibsqlConnection {
|
|
153
157
|
);
|
154
158
|
|
155
159
|
// console.log('q', JSON.stringify({sql, params}, null, 2))
|
156
|
-
console.log('stmts', JSON.stringify(stmts, null, 2))
|
157
|
-
console.log('result', JSON.stringify(results, null, 2))
|
160
|
+
// console.log('stmts', JSON.stringify(stmts, null, 2))
|
161
|
+
// console.log('result', JSON.stringify(results, null, 2))
|
158
162
|
|
159
163
|
const last_result = results?.at(-1);
|
160
164
|
|
@@ -172,10 +176,10 @@ export class LibsqlConnection {
|
|
172
176
|
*
|
173
177
|
* @param {kysely.CompiledQuery} compiledQuery
|
174
178
|
*
|
175
|
-
* @returns {Promise<
|
179
|
+
* @returns {Promise<QueryResult>}
|
176
180
|
*/
|
177
181
|
async executeQuery(compiledQuery) {
|
178
|
-
console.log('this.isBatch', this.isBatch)
|
182
|
+
// console.log('this.isBatch', this.isBatch)
|
179
183
|
if(this.isBatch) {
|
180
184
|
this.batch.push(compiledQuery);
|
181
185
|
return Promise.resolve(
|
@@ -202,7 +206,7 @@ export class LibsqlConnection {
|
|
202
206
|
}
|
203
207
|
|
204
208
|
async commitTransaction() {
|
205
|
-
console.log('commitTransaction')
|
209
|
+
// console.log('commitTransaction')
|
206
210
|
if(this.isBatch) {
|
207
211
|
// console.trace()
|
208
212
|
await this.#internal_executeQuery(this.batch);
|
@@ -237,7 +241,7 @@ export class LibsqlConnection {
|
|
237
241
|
* @param {kysely.CompiledQuery} compiledQuery
|
238
242
|
* @param {number} chunkSize
|
239
243
|
*
|
240
|
-
* @returns {AsyncIterableIterator<
|
244
|
+
* @returns {AsyncIterableIterator<QueryResult<R>>}
|
241
245
|
*/
|
242
246
|
async *streamQuery(compiledQuery, chunkSize) {
|
243
247
|
throw new Error("Libsql Driver does not support streaming yet");
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@storecraft/database-turso",
|
3
|
-
"version": "1.0.
|
3
|
+
"version": "1.0.11",
|
4
4
|
"description": "`Storecraft` database driver for `Turso` (cloud sqlite)",
|
5
5
|
"license": "MIT",
|
6
6
|
"author": "Tomer Shalev (https://github.com/store-craft)",
|
@@ -24,6 +24,10 @@
|
|
24
24
|
"import": "./index.js",
|
25
25
|
"types": "./types.public.d.ts"
|
26
26
|
},
|
27
|
+
"./vector-store": {
|
28
|
+
"import": "./vector-store/index.js",
|
29
|
+
"types": "./vector-store/types.d.ts"
|
30
|
+
},
|
27
31
|
"./*": {
|
28
32
|
"import": "./*"
|
29
33
|
}
|
package/tests/runner.test.js
CHANGED
@@ -18,11 +18,8 @@ export const create_app = async () => {
|
|
18
18
|
.withDatabase(
|
19
19
|
new Turso(
|
20
20
|
{
|
21
|
+
url: ':memory:',
|
21
22
|
prefers_batch_over_transactions: true,
|
22
|
-
libsqlConfig: {
|
23
|
-
url: process.env.TURSO_URL,
|
24
|
-
authToken: process.env.TURSO_API_TOKEN,
|
25
|
-
}
|
26
23
|
}
|
27
24
|
)
|
28
25
|
);
|
package/tests/sandbox.js
CHANGED
package/types.public.d.ts
CHANGED
@@ -1,16 +1,29 @@
|
|
1
1
|
import type { Config as LibSqlConfig } from '@libsql/client'
|
2
|
-
export
|
2
|
+
export * from './index.js';
|
3
3
|
|
4
|
-
export type Config = {
|
4
|
+
export type Config = Partial<Omit<LibSqlConfig, 'url' | 'authToken'>> & {
|
5
5
|
|
6
|
-
/**
|
7
|
-
*
|
6
|
+
/** The database URL.
|
7
|
+
*
|
8
|
+
* The client supports `libsql:`, `http:`/`https:`, `ws:`/`wss:` and `file:` URL. For more infomation,
|
9
|
+
* please refer to the project README:
|
10
|
+
*
|
11
|
+
* https://github.com/libsql/libsql-client-ts#supported-urls
|
12
|
+
*
|
13
|
+
* If missing, it will be inferred by env variable `LIBSQL_URL`
|
14
|
+
*/
|
15
|
+
url?: string;
|
16
|
+
/**
|
17
|
+
* Authentication token for the database. Not applicable for `url`=`file:local.db`.
|
18
|
+
*
|
19
|
+
* If missing, it will be inferred by env variable `LIBSQL_AUTH_TOKEN`
|
8
20
|
*/
|
9
|
-
|
21
|
+
authToken?: string;
|
10
22
|
|
11
23
|
/**
|
12
24
|
* @description if `true`, transactions are converted into a non-interactive batch,
|
13
25
|
* use with caution and prefer this when transactions are non-interactive
|
26
|
+
* @default true
|
14
27
|
*/
|
15
28
|
prefers_batch_over_transactions?: boolean;
|
16
29
|
}
|
@@ -0,0 +1,302 @@
|
|
1
|
+
/**
|
2
|
+
* @import {
|
3
|
+
* AIEmbedder, VectorStore
|
4
|
+
* } from '@storecraft/core/ai/core/types.private.js'
|
5
|
+
* @import { ENV } from '@storecraft/core';
|
6
|
+
* @import {
|
7
|
+
* Config
|
8
|
+
* } from './types.js'
|
9
|
+
* @import {
|
10
|
+
* VectorDocumentUpsert
|
11
|
+
* } from './types.private.js'
|
12
|
+
* @import { InArgs } from '@libsql/client';
|
13
|
+
*/
|
14
|
+
|
15
|
+
import * as libsql from "@libsql/client";
|
16
|
+
import {
|
17
|
+
truncate_or_pad_vector
|
18
|
+
} from "@storecraft/core/ai/models/vector-stores/index.js";
|
19
|
+
|
20
|
+
export const DEFAULT_INDEX_NAME = 'vector_store';
|
21
|
+
|
22
|
+
/** @param {any} json */
|
23
|
+
const parse_json_safely = json => {
|
24
|
+
try {
|
25
|
+
return JSON.parse(json);
|
26
|
+
} catch (e) {
|
27
|
+
return {};
|
28
|
+
} finally {
|
29
|
+
}
|
30
|
+
}
|
31
|
+
|
32
|
+
/**
|
33
|
+
* Implementation referenes:
|
34
|
+
* - https://docs.turso.tech/features/ai-and-embeddings#vectors-usage
|
35
|
+
* - https://github.com/langchain-ai/langchainjs/blob/9dfaae7e36a1ddce586b9c44fb96785fa38b36ec/libs/langchain-community/src/vectorstores/libsql.ts
|
36
|
+
*/
|
37
|
+
|
38
|
+
/**
|
39
|
+
* @typedef {VectorStore} Impl
|
40
|
+
*/
|
41
|
+
|
42
|
+
/**
|
43
|
+
* @description LibSQL / Turso Vector Store
|
44
|
+
*
|
45
|
+
* @implements {VectorStore}
|
46
|
+
*/
|
47
|
+
export class LibSQLVectorStore {
|
48
|
+
|
49
|
+
/** @satisfies {ENV<Config>} */
|
50
|
+
static EnvConfig = /** @type{const} */ ({
|
51
|
+
authToken: 'LIBSQL_VECTOR_AUTH_TOKEN',
|
52
|
+
url: 'LIBSQL_VECTOR_URL',
|
53
|
+
});
|
54
|
+
|
55
|
+
/** @type {Config} */
|
56
|
+
config;
|
57
|
+
|
58
|
+
/** @type {libsql.Client} */
|
59
|
+
#client
|
60
|
+
|
61
|
+
/**
|
62
|
+
*
|
63
|
+
* @param {Config} config
|
64
|
+
*/
|
65
|
+
constructor(config) {
|
66
|
+
this.config = {
|
67
|
+
index_name: DEFAULT_INDEX_NAME,
|
68
|
+
similarity: 'cosine',
|
69
|
+
dimensions: 1536,
|
70
|
+
...config,
|
71
|
+
};
|
72
|
+
}
|
73
|
+
|
74
|
+
get client() {
|
75
|
+
if(!this.config.url) {
|
76
|
+
throw new Error('LibSQLVectorStore::client() - missing url');
|
77
|
+
}
|
78
|
+
|
79
|
+
// @ts-ignore
|
80
|
+
this.#client = this.#client ?? libsql.createClient(this.config);
|
81
|
+
|
82
|
+
return this.#client;
|
83
|
+
}
|
84
|
+
|
85
|
+
get index_name() {
|
86
|
+
return this.config.index_name;
|
87
|
+
}
|
88
|
+
|
89
|
+
get table_name() {
|
90
|
+
return `${this.index_name}_table`;
|
91
|
+
}
|
92
|
+
|
93
|
+
/** @type {VectorStore["onInit"]} */
|
94
|
+
onInit = (app) => {
|
95
|
+
this.config.authToken ??= app.platform.env[
|
96
|
+
LibSQLVectorStore.EnvConfig.authToken ?? 'LIBSQL_AUTH_TOKEN'
|
97
|
+
];
|
98
|
+
this.config.url ??= app.platform.env[
|
99
|
+
LibSQLVectorStore.EnvConfig.url ?? 'LIBSQL_URL'
|
100
|
+
];
|
101
|
+
}
|
102
|
+
|
103
|
+
/** @type {VectorStore["embedder"]} */
|
104
|
+
get embedder() {
|
105
|
+
return this.config.embedder
|
106
|
+
}
|
107
|
+
|
108
|
+
// (id TEXT, metadata TEXT, pageContent Text, updated_at TEXT, namespace TEXT, embedding F32_BLOB
|
109
|
+
/** @type {VectorStore["upsertVectors"]} */
|
110
|
+
upsertVectors = async (vectors, documents, options) => {
|
111
|
+
|
112
|
+
const updated_at = new Date().toISOString();
|
113
|
+
/** @type {VectorDocumentUpsert[]} */
|
114
|
+
const docs_upsert = documents.map(
|
115
|
+
(doc, ix) => (
|
116
|
+
{
|
117
|
+
embedding: `[${truncate_or_pad_vector(vectors[ix], this.config.dimensions).join(',')}]`,
|
118
|
+
id: doc.id,
|
119
|
+
metadata: JSON.stringify(doc.metadata ?? {}),
|
120
|
+
pageContent: doc.pageContent,
|
121
|
+
updated_at,
|
122
|
+
namespace: doc.namespace,
|
123
|
+
}
|
124
|
+
)
|
125
|
+
);
|
126
|
+
|
127
|
+
/** @type {import("@libsql/client").InStatement[]} */
|
128
|
+
const stmts_delete = docs_upsert.map(
|
129
|
+
(doc, ix) => (
|
130
|
+
{
|
131
|
+
sql: `DELETE FROM ${this.table_name} WHERE id=?`,
|
132
|
+
args: [doc.id]
|
133
|
+
}
|
134
|
+
)
|
135
|
+
);
|
136
|
+
|
137
|
+
/** @type {import("@libsql/client").InStatement[]} */
|
138
|
+
const stmts_insert = docs_upsert.map(
|
139
|
+
(doc, ix) => (
|
140
|
+
{
|
141
|
+
sql: `
|
142
|
+
INSERT INTO ${this.table_name} (id, metadata, pageContent, updated_at, namespace, embedding)
|
143
|
+
VALUES (:id, :metadata, :pageContent, :updated_at, :namespace, vector(:embedding))
|
144
|
+
`,
|
145
|
+
args: doc
|
146
|
+
}
|
147
|
+
)
|
148
|
+
);
|
149
|
+
|
150
|
+
const result = await this.client.batch(
|
151
|
+
[
|
152
|
+
...stmts_delete,
|
153
|
+
...stmts_insert,
|
154
|
+
]
|
155
|
+
);
|
156
|
+
|
157
|
+
}
|
158
|
+
|
159
|
+
/** @type {VectorStore["upsertDocuments"]} */
|
160
|
+
upsertDocuments = async (documents, options) => {
|
161
|
+
// first, generate embeddings for the documents
|
162
|
+
const result = await this.embedder.generateEmbeddings(
|
163
|
+
{
|
164
|
+
content: documents.map(
|
165
|
+
doc => (
|
166
|
+
{
|
167
|
+
content: doc.pageContent,
|
168
|
+
type: 'text'
|
169
|
+
}
|
170
|
+
)
|
171
|
+
)
|
172
|
+
}
|
173
|
+
);
|
174
|
+
|
175
|
+
const vectors = result.content;
|
176
|
+
|
177
|
+
// console.log(vectors)
|
178
|
+
|
179
|
+
return this.upsertVectors(
|
180
|
+
vectors, documents, options
|
181
|
+
)
|
182
|
+
}
|
183
|
+
|
184
|
+
/** @type {VectorStore["delete"]} */
|
185
|
+
delete = async (ids) => {
|
186
|
+
await this.client.execute(
|
187
|
+
{
|
188
|
+
sql: `DELETE FROM ${this.table_name} WHERE id IN (${ids.map(id => '?').join(',')})`,
|
189
|
+
args: ids
|
190
|
+
}
|
191
|
+
);
|
192
|
+
}
|
193
|
+
|
194
|
+
/** @type {VectorStore["similaritySearch"]} */
|
195
|
+
similaritySearch = async (query, k, namespaces) => {
|
196
|
+
|
197
|
+
const embedding_result = await this.embedder.generateEmbeddings(
|
198
|
+
{
|
199
|
+
content: [
|
200
|
+
{
|
201
|
+
content: query,
|
202
|
+
type: 'text'
|
203
|
+
}
|
204
|
+
]
|
205
|
+
}
|
206
|
+
);
|
207
|
+
const vector = truncate_or_pad_vector(
|
208
|
+
embedding_result.content[0], this.config.dimensions
|
209
|
+
);
|
210
|
+
const vector_sql_value = `[${vector.join(',')}]`
|
211
|
+
const distance_fn = this.config.similarity==='cosine' ? 'vector_distance_cos' : 'vector_distance_l2'
|
212
|
+
// SELECT title, year
|
213
|
+
// FROM vector_top_k('movies_idx', vector32('[0.064, 0.777, 0.661, 0.687]'), 3)
|
214
|
+
// JOIN movies ON movies.rowid = id
|
215
|
+
// WHERE year >= 2020;
|
216
|
+
const table = this.table_name;
|
217
|
+
const index_name = this.index_name;
|
218
|
+
/** @type {InArgs} */
|
219
|
+
let args = [];
|
220
|
+
let sql = `
|
221
|
+
SELECT id, metadata, pageContent, updated_at, namespace, ${distance_fn}(embedding, vector(?)) AS score
|
222
|
+
FROM vector_top_k('${index_name}', vector(?), ?) as top_k_view
|
223
|
+
JOIN ${table} ON ${table}.rowid = top_k_view.id
|
224
|
+
`;
|
225
|
+
args.push(vector_sql_value, vector_sql_value, k);
|
226
|
+
|
227
|
+
if(Array.isArray(namespaces) && namespaces.length) {
|
228
|
+
sql += `\nWHERE namespace IN (${namespaces.map(n => '?').join(',')})`
|
229
|
+
args.push(...namespaces);
|
230
|
+
}
|
231
|
+
|
232
|
+
sql += `
|
233
|
+
ORDER BY
|
234
|
+
${distance_fn}(embedding, vector(?))
|
235
|
+
ASC;
|
236
|
+
`
|
237
|
+
args.push(vector_sql_value);
|
238
|
+
|
239
|
+
|
240
|
+
const result = await this.client.execute({ sql, args });
|
241
|
+
|
242
|
+
return result.rows.map(
|
243
|
+
(row) => (
|
244
|
+
{
|
245
|
+
document: {
|
246
|
+
pageContent: String(row.pageContent),
|
247
|
+
id: String(row.id),
|
248
|
+
metadata: parse_json_safely(row.metadata),
|
249
|
+
namespace: String(row.namespace),
|
250
|
+
},
|
251
|
+
score: Number(row.score)
|
252
|
+
}
|
253
|
+
)
|
254
|
+
);
|
255
|
+
|
256
|
+
}
|
257
|
+
|
258
|
+
|
259
|
+
/**
|
260
|
+
*
|
261
|
+
* @param {boolean} [delete_index_if_exists_before=false]
|
262
|
+
* @returns {Promise<boolean>}
|
263
|
+
*/
|
264
|
+
createVectorIndex = async (delete_index_if_exists_before=false) => {
|
265
|
+
|
266
|
+
/** @type {string[]} */
|
267
|
+
const batch = [];
|
268
|
+
|
269
|
+
if(delete_index_if_exists_before) {
|
270
|
+
await this.deleteVectorIndex();
|
271
|
+
}
|
272
|
+
|
273
|
+
batch.push(
|
274
|
+
`CREATE TABLE IF NOT EXISTS ${this.table_name} (id TEXT, metadata TEXT, pageContent Text, updated_at TEXT, namespace TEXT, embedding F32_BLOB(${this.config.dimensions}));`,
|
275
|
+
`CREATE INDEX IF NOT EXISTS ${this.index_name} ON ${this.table_name}(libsql_vector_idx(embedding));`
|
276
|
+
);
|
277
|
+
|
278
|
+
const result = await this.client.batch(batch);
|
279
|
+
return true;
|
280
|
+
}
|
281
|
+
|
282
|
+
/**
|
283
|
+
*
|
284
|
+
* @returns {Promise<boolean>}
|
285
|
+
*/
|
286
|
+
deleteVectorIndex = async () => {
|
287
|
+
|
288
|
+
/** @type {string[]} */
|
289
|
+
const batch = [];
|
290
|
+
|
291
|
+
batch.push(
|
292
|
+
`DROP INDEX IF EXISTS ${this.index_name}`,
|
293
|
+
`DROP TABLE IF EXISTS ${this.table_name}`,
|
294
|
+
);
|
295
|
+
|
296
|
+
const result = await this.client.batch(batch);
|
297
|
+
return true;
|
298
|
+
}
|
299
|
+
|
300
|
+
|
301
|
+
}
|
302
|
+
|
@@ -0,0 +1,48 @@
|
|
1
|
+
|
2
|
+
import type { AIEmbedder } from '@storecraft/core/ai';
|
3
|
+
export * from './index.js';
|
4
|
+
|
5
|
+
export type Config = {
|
6
|
+
/**
|
7
|
+
* @description The database URL.
|
8
|
+
*
|
9
|
+
* The client supports `libsql:`, `http:`/`https:`, `ws:`/`wss:` and `file:` URL. For more infomation,
|
10
|
+
* please refer to the project README:
|
11
|
+
*
|
12
|
+
* https://github.com/libsql/libsql-client-ts#supported-urls
|
13
|
+
*
|
14
|
+
* If missing, it will be inferred by env variable `LIBSQL_VECTOR_URL` or `LIBSQL_URL`
|
15
|
+
*/
|
16
|
+
url?: string;
|
17
|
+
/**
|
18
|
+
* @description Authentication token for the database. Not applicable for local `url`=`file:local.db`.
|
19
|
+
*
|
20
|
+
* If missing, it will be inferred by env variable `LIBSQL_VECTOR_AUTH_TOKEN` or `LIBSQL_AUTH_TOKEN`
|
21
|
+
*
|
22
|
+
* @default ENV variable `LIBSQL_AUTH_TOKEN`
|
23
|
+
*/
|
24
|
+
authToken?: string;
|
25
|
+
|
26
|
+
/**
|
27
|
+
* @description The name of the index
|
28
|
+
* @default 'vector_store'
|
29
|
+
*/
|
30
|
+
index_name?: string,
|
31
|
+
|
32
|
+
/**
|
33
|
+
* @description The dimensions of the vectors to be inserted in the index.
|
34
|
+
* @default 1536
|
35
|
+
*/
|
36
|
+
dimensions?: number,
|
37
|
+
|
38
|
+
/**
|
39
|
+
* @description The similiarity metric
|
40
|
+
* @default 'cosine'
|
41
|
+
*/
|
42
|
+
similarity?: 'euclidean' | 'cosine',
|
43
|
+
|
44
|
+
/**
|
45
|
+
* @description Embedding model provider
|
46
|
+
*/
|
47
|
+
embedder: AIEmbedder
|
48
|
+
}
|
@@ -0,0 +1,16 @@
|
|
1
|
+
|
2
|
+
export type VectorDocument = {
|
3
|
+
id: string,
|
4
|
+
metadata: Record<string, any>,
|
5
|
+
embedding: number[],
|
6
|
+
pageContent: string,
|
7
|
+
updated_at: string,
|
8
|
+
score?: number
|
9
|
+
namespace?: string
|
10
|
+
}
|
11
|
+
|
12
|
+
export type VectorDocumentUpsert = Omit<VectorDocument, 'embedding' | 'metadata' | 'score'> & {
|
13
|
+
embedding: string,
|
14
|
+
metadata: string,
|
15
|
+
}
|
16
|
+
|