libsql-search 0.6.0 → 0.7.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/README.md +26 -47
- package/dist/index.cjs +160 -131
- package/dist/index.d.ts +6 -2
- package/dist/index.esm.js +160 -131
- package/docs/API.md +125 -96
- package/docs/INDEXING.md +12 -25
- package/docs/INTEGRATIONS.md +221 -29
- package/docs/MIGRATIONS.md +174 -0
- package/docs/PROVIDERS.md +112 -175
- package/docs/README.md +7 -9
- package/docs/TESTING.md +138 -0
- package/package.json +1 -1
package/docs/API.md
CHANGED
|
@@ -37,9 +37,16 @@ It also exports these types:
|
|
|
37
37
|
- `SearchOptions`
|
|
38
38
|
- `SearchResult`
|
|
39
39
|
|
|
40
|
+
## Navigation
|
|
41
|
+
|
|
42
|
+
- [Provider matrix and credential rules](./PROVIDERS.md)
|
|
43
|
+
- [Migration and reindexing guide](./MIGRATIONS.md)
|
|
44
|
+
- [Indexing and operational behavior](./INDEXING.md)
|
|
45
|
+
- [Testing guidance](./TESTING.md)
|
|
46
|
+
|
|
40
47
|
## `createTable(client, tableName?, dimensions?)`
|
|
41
48
|
|
|
42
|
-
Creates the table and supporting indexes
|
|
49
|
+
Creates the search table and supporting indexes.
|
|
43
50
|
|
|
44
51
|
```ts
|
|
45
52
|
await createTable(client);
|
|
@@ -50,10 +57,7 @@ Defaults:
|
|
|
50
57
|
- `tableName`: `"articles"`
|
|
51
58
|
- `dimensions`: `384`
|
|
52
59
|
|
|
53
|
-
`tableName` must be an ASCII SQLite identifier matching
|
|
54
|
-
`[A-Za-z_][A-Za-z0-9_]*`. Valid identifiers are quoted internally, so reserved
|
|
55
|
-
words such as `"select"` are safe to use. `dimensions` must be a positive
|
|
56
|
-
integer.
|
|
60
|
+
`tableName` must be an ASCII SQLite identifier matching `[A-Za-z_][A-Za-z0-9_]*`. Valid identifiers are quoted internally, so reserved words such as `"select"` are safe to use. `dimensions` must be a positive integer.
|
|
57
61
|
|
|
58
62
|
The created schema includes:
|
|
59
63
|
|
|
@@ -63,10 +67,12 @@ The created schema includes:
|
|
|
63
67
|
- `content`
|
|
64
68
|
- `folder`
|
|
65
69
|
- `tags`
|
|
66
|
-
- `embedding`
|
|
70
|
+
- `embedding F32_BLOB(dimensions)`
|
|
67
71
|
- `created_at`
|
|
68
72
|
- `updated_at`
|
|
69
73
|
|
|
74
|
+
`createTable()` uses `CREATE TABLE IF NOT EXISTS`, so it does not resize an existing vector column. See [Migration and reindexing guide](./MIGRATIONS.md) before changing widths or providers.
|
|
75
|
+
|
|
70
76
|
## `indexContent(options)`
|
|
71
77
|
|
|
72
78
|
Indexes Markdown files from a directory on disk.
|
|
@@ -89,8 +95,6 @@ Defaults:
|
|
|
89
95
|
- `exclude`: ["node_modules", ".git", "dist", "build"]
|
|
90
96
|
- `tableName`: `"articles"`
|
|
91
97
|
|
|
92
|
-
`tableName` follows the same identifier policy as `createTable()`.
|
|
93
|
-
|
|
94
98
|
Return shape:
|
|
95
99
|
|
|
96
100
|
```ts
|
|
@@ -104,10 +108,9 @@ Return shape:
|
|
|
104
108
|
Behavior notes:
|
|
105
109
|
|
|
106
110
|
- `indexContent()` deletes existing rows in the target table before rebuilding
|
|
107
|
-
-
|
|
108
|
-
|
|
109
|
-
- embeddings default to `intent: "document"` unless `embeddingOptions.intent`
|
|
110
|
-
is set explicitly
|
|
111
|
+
- rebuilds are not transactional
|
|
112
|
+
- frontmatter `title`, `description`, and `tags` are folded into the embedding text
|
|
113
|
+
- embeddings default to `intent: "document"` unless `embeddingOptions.intent` is set explicitly
|
|
111
114
|
- if a file has no frontmatter title, the filename becomes the title
|
|
112
115
|
|
|
113
116
|
## `search(options)`
|
|
@@ -129,9 +132,7 @@ Defaults:
|
|
|
129
132
|
- `limit`: `10`
|
|
130
133
|
- `tableName`: `"articles"`
|
|
131
134
|
|
|
132
|
-
`limit` must be an integer from `1` through `100`; invalid values are rejected
|
|
133
|
-
before query embedding generation. `tableName` follows the same identifier
|
|
134
|
-
policy as `createTable()`.
|
|
135
|
+
`limit` must be an integer from `1` through `100`; invalid values are rejected before query embedding generation.
|
|
135
136
|
|
|
136
137
|
Result shape:
|
|
137
138
|
|
|
@@ -150,8 +151,7 @@ interface SearchResult {
|
|
|
150
151
|
|
|
151
152
|
Lower `distance` values are better matches.
|
|
152
153
|
|
|
153
|
-
Search embeddings default to `intent: "query"` unless
|
|
154
|
-
`embeddingOptions.intent` is set explicitly.
|
|
154
|
+
Search embeddings default to `intent: "query"` unless `embeddingOptions.intent` is set explicitly.
|
|
155
155
|
|
|
156
156
|
## Article Retrieval Helpers
|
|
157
157
|
|
|
@@ -171,82 +171,141 @@ Returns articles in a specific folder.
|
|
|
171
171
|
|
|
172
172
|
Returns distinct folder names from the index.
|
|
173
173
|
|
|
174
|
-
All
|
|
174
|
+
All retrieval helpers validate `tableName` before executing SQL.
|
|
175
175
|
|
|
176
176
|
## Embedding Helpers
|
|
177
177
|
|
|
178
|
-
### `
|
|
178
|
+
### `EmbeddingOptions`
|
|
179
179
|
|
|
180
|
-
|
|
180
|
+
```ts
|
|
181
|
+
interface EmbeddingOptions {
|
|
182
|
+
provider?:
|
|
183
|
+
| "local"
|
|
184
|
+
| "cloudflare"
|
|
185
|
+
| "mistral"
|
|
186
|
+
| "gemini"
|
|
187
|
+
| "openai"
|
|
188
|
+
| "openai-compatible";
|
|
189
|
+
apiKey?: string;
|
|
190
|
+
accountId?: string;
|
|
191
|
+
apiToken?: string;
|
|
192
|
+
baseUrl?: string;
|
|
193
|
+
model?: string;
|
|
194
|
+
batchSize?: number;
|
|
195
|
+
dimensions?: number;
|
|
196
|
+
maxLength?: number;
|
|
197
|
+
intent?: "document" | "query";
|
|
198
|
+
timeoutMs?: number;
|
|
199
|
+
signal?: AbortSignal;
|
|
200
|
+
}
|
|
201
|
+
```
|
|
181
202
|
|
|
182
|
-
|
|
203
|
+
Important option rules:
|
|
183
204
|
|
|
184
|
-
|
|
185
|
-
|
|
205
|
+
- `provider` defaults to `local`
|
|
206
|
+
- `maxLength` defaults to `8000`
|
|
207
|
+
- `timeoutMs` defaults to `30000`
|
|
208
|
+
- `model` is only used by `openai-compatible`
|
|
209
|
+
- `baseUrl`, `model`, and `dimensions` are required for `openai-compatible`
|
|
210
|
+
- `batchSize` only applies to `openai-compatible` and defaults to `32`
|
|
211
|
+
- `openai-compatible` never reads `OPENAI_API_KEY`
|
|
212
|
+
- only the Gemini adapter currently changes payload formatting by `intent`
|
|
186
213
|
|
|
187
|
-
|
|
214
|
+
Dimension rules:
|
|
215
|
+
|
|
216
|
+
- local: fixed `384`
|
|
217
|
+
- Cloudflare: fixed `1024`
|
|
218
|
+
- Mistral: fixed `1024`
|
|
219
|
+
- Gemini: default `3072`, allowed integer range `128-3072`
|
|
220
|
+
- OpenAI: default `768`; `text-embedding-3-small` through `1536`, `text-embedding-3-large` above `1536`
|
|
221
|
+
- OpenAI-compatible: required positive integer, no default
|
|
188
222
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
223
|
+
See [Provider matrix and credential rules](./PROVIDERS.md) for the canonical provider table.
|
|
224
|
+
|
|
225
|
+
### `generateEmbedding(text, options?)`
|
|
226
|
+
|
|
227
|
+
Generates one embedding vector.
|
|
193
228
|
|
|
194
229
|
```ts
|
|
195
|
-
const
|
|
230
|
+
const embedding = await generateEmbedding("deploy docs", {
|
|
196
231
|
provider: "openai",
|
|
197
232
|
apiKey: process.env.OPENAI_API_KEY,
|
|
198
233
|
dimensions: 1536,
|
|
199
234
|
});
|
|
200
|
-
|
|
201
|
-
console.log(provider.metadata);
|
|
202
235
|
```
|
|
203
236
|
|
|
204
|
-
|
|
237
|
+
### `generateEmbeddings(texts, options?)`
|
|
205
238
|
|
|
206
|
-
|
|
207
|
-
- `model`
|
|
208
|
-
- `dimensions`
|
|
209
|
-
- `batch.mode`
|
|
210
|
-
- `batch.maxSize`, when the provider has a hard maximum
|
|
239
|
+
Generates an ordered batch of embeddings.
|
|
211
240
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
241
|
+
- empty batches return `[]` without loading the local model or making a hosted call
|
|
242
|
+
- OpenAI batches above `2048` inputs are rejected before network work
|
|
243
|
+
- `openai-compatible` batches are chunked sequentially according to `batchSize`
|
|
215
244
|
|
|
216
|
-
|
|
217
|
-
Gemini dimensions must be an integer from 128 through 3072.
|
|
245
|
+
### `createEmbeddingProvider(options?)`
|
|
218
246
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
247
|
+
Creates a provider client with immutable metadata and an `embed(texts, options?)` method.
|
|
248
|
+
|
|
249
|
+
```ts
|
|
250
|
+
const provider = createEmbeddingProvider({
|
|
251
|
+
provider: "openai-compatible",
|
|
252
|
+
baseUrl: "https://tei.example.internal/v1",
|
|
253
|
+
model: "bge-large-en-v1.5",
|
|
254
|
+
dimensions: 1024,
|
|
255
|
+
batchSize: 32,
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
console.log(provider.metadata);
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
Provider clients return a rich `EmbeddingBatchResult`; the compatibility helpers `generateEmbedding()` and `generateEmbeddings()` return only vectors.
|
|
262
|
+
|
|
263
|
+
Hosted provider clients are scoped to their current options. The library does not reuse a Cloudflare, Mistral, Gemini, or OpenAI client across different credential sets or configurations.
|
|
222
264
|
|
|
223
265
|
### `getEmbeddingProviderMetadata(options?)`
|
|
224
266
|
|
|
225
|
-
Returns the same metadata exposed by `createEmbeddingProvider(options).metadata`
|
|
226
|
-
without resolving hosted-provider credentials.
|
|
267
|
+
Returns the same metadata exposed by `createEmbeddingProvider(options).metadata` without resolving hosted-provider credentials.
|
|
227
268
|
|
|
228
|
-
|
|
269
|
+
Metadata shape:
|
|
229
270
|
|
|
230
271
|
```ts
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
272
|
+
interface EmbeddingProviderMetadata {
|
|
273
|
+
name:
|
|
274
|
+
| "local"
|
|
275
|
+
| "cloudflare"
|
|
276
|
+
| "mistral"
|
|
277
|
+
| "gemini"
|
|
278
|
+
| "openai"
|
|
279
|
+
| "openai-compatible";
|
|
280
|
+
model: string;
|
|
281
|
+
dimensions: number;
|
|
282
|
+
batch: {
|
|
283
|
+
mode: "native" | "sequential";
|
|
284
|
+
maxSize?: number;
|
|
285
|
+
};
|
|
236
286
|
}
|
|
237
287
|
```
|
|
238
288
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
289
|
+
Batch interpretation:
|
|
290
|
+
|
|
291
|
+
- `"native"` means the provider accepts a batch request upstream
|
|
292
|
+
- `"sequential"` means the library accepts a batch but processes items one-by-one
|
|
293
|
+
- `batch.maxSize` is a hard client-side limit when present
|
|
243
294
|
|
|
244
|
-
|
|
295
|
+
`openai-compatible` metadata reports `batch.mode: "native"` because the remote endpoint is expected to accept batch inputs, even though the library may split large arrays into sequential outbound chunks at `batchSize`.
|
|
296
|
+
|
|
297
|
+
### `EmbeddingBatchResult`
|
|
245
298
|
|
|
246
299
|
```ts
|
|
247
300
|
interface EmbeddingBatchResult {
|
|
248
301
|
embeddings: number[][];
|
|
249
|
-
provider:
|
|
302
|
+
provider:
|
|
303
|
+
| "local"
|
|
304
|
+
| "cloudflare"
|
|
305
|
+
| "mistral"
|
|
306
|
+
| "gemini"
|
|
307
|
+
| "openai"
|
|
308
|
+
| "openai-compatible";
|
|
250
309
|
model: string;
|
|
251
310
|
dimensions: number;
|
|
252
311
|
intent: "document" | "query";
|
|
@@ -255,47 +314,17 @@ interface EmbeddingBatchResult {
|
|
|
255
314
|
|
|
256
315
|
### `validateEmbeddingBatch(items, expectedCount, expectedDimensions, provider)`
|
|
257
316
|
|
|
258
|
-
Validates provider
|
|
259
|
-
cardinality, dimensions, finite numeric values, and indexed batch ordering.
|
|
260
|
-
|
|
261
|
-
`EmbeddingOptions` supports:
|
|
262
|
-
|
|
263
|
-
```ts
|
|
264
|
-
interface EmbeddingOptions {
|
|
265
|
-
provider?: "local" | "cloudflare" | "mistral" | "gemini" | "openai";
|
|
266
|
-
apiKey?: string;
|
|
267
|
-
accountId?: string;
|
|
268
|
-
apiToken?: string;
|
|
269
|
-
dimensions?: number;
|
|
270
|
-
maxLength?: number;
|
|
271
|
-
intent?: "document" | "query";
|
|
272
|
-
timeoutMs?: number;
|
|
273
|
-
signal?: AbortSignal;
|
|
274
|
-
}
|
|
275
|
-
```
|
|
317
|
+
Validates provider responses before they reach the database:
|
|
276
318
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
319
|
+
- result count must match the requested input count
|
|
320
|
+
- vectors must match the effective dimensions
|
|
321
|
+
- values must be finite numbers
|
|
322
|
+
- indexed provider responses are reordered and checked for contiguous indices
|
|
281
323
|
|
|
282
324
|
### `padEmbedding(embedding, targetDimensions)`
|
|
283
325
|
|
|
284
|
-
Pads or truncates
|
|
285
|
-
|
|
286
|
-
This helper remains exported for callers that used it directly. The local
|
|
287
|
-
provider does not use it; local vectors are validated at native 384 dimensions.
|
|
326
|
+
Pads or truncates a vector to the target width. This is exported for compatibility and migration workflows, but the current local provider uses its native `384` dimensions rather than padding by default.
|
|
288
327
|
|
|
289
328
|
### `prepareTextForEmbedding(fields)`
|
|
290
329
|
|
|
291
|
-
|
|
292
|
-
embedding model.
|
|
293
|
-
|
|
294
|
-
```ts
|
|
295
|
-
const text = prepareTextForEmbedding({
|
|
296
|
-
title: "My Article",
|
|
297
|
-
description: "How semantic search works",
|
|
298
|
-
tags: ["search", "turso"],
|
|
299
|
-
content: "# Content",
|
|
300
|
-
});
|
|
301
|
-
```
|
|
330
|
+
Builds the text that is embedded from title, description, content, and tags.
|
package/docs/INDEXING.md
CHANGED
|
@@ -2,8 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
## Content Shape
|
|
4
4
|
|
|
5
|
-
`indexContent()` walks a directory tree, reads Markdown files, parses
|
|
6
|
-
frontmatter with `gray-matter`, and stores:
|
|
5
|
+
`indexContent()` walks a directory tree, reads Markdown files, parses frontmatter with `gray-matter`, and stores:
|
|
7
6
|
|
|
8
7
|
- `slug`
|
|
9
8
|
- `title`
|
|
@@ -22,41 +21,32 @@ The slug is derived from the file path relative to `contentPath`.
|
|
|
22
21
|
await indexContent({
|
|
23
22
|
client,
|
|
24
23
|
contentPath: "./content",
|
|
25
|
-
tableName: "
|
|
24
|
+
tableName: "articles_local_384",
|
|
26
25
|
embeddingOptions: {
|
|
27
26
|
provider: "local",
|
|
28
27
|
},
|
|
29
28
|
});
|
|
30
29
|
```
|
|
31
30
|
|
|
32
|
-
That keeps the implementation simple, but it also means
|
|
33
|
-
leave the index partially repopulated.
|
|
31
|
+
That keeps the implementation simple, but it also means:
|
|
34
32
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
768-dimensional tables stored 384 model values followed by zero padding;
|
|
39
|
-
`indexContent()` clears rows but does not change the table's `F32_BLOB` width.
|
|
33
|
+
- failed rebuilds can leave the table partially repopulated
|
|
34
|
+
- provider or dimension changes should use a parallel table migration
|
|
35
|
+
- `createTable()` does not resize an existing vector column
|
|
40
36
|
|
|
41
|
-
|
|
42
|
-
model must be rebuilt for `gemini-embedding-2` even when staying at 768
|
|
43
|
-
dimensions, because the model and query/document formatting both changed. If
|
|
44
|
-
you adopt Gemini's 3072-dimensional default, recreate the vector table or build
|
|
45
|
-
into a separate table first; clearing rows with `indexContent()` does not change
|
|
46
|
-
the table's `F32_BLOB` width.
|
|
37
|
+
If provider, dimensions, model, endpoint, or embedding-space assumptions change, fully reindex into a new table. See the canonical [Migration and reindexing guide](./MIGRATIONS.md).
|
|
47
38
|
|
|
48
39
|
## Quality Guidelines
|
|
49
40
|
|
|
50
41
|
- include descriptive frontmatter titles
|
|
51
42
|
- add meaningful `tags` when they help retrieval
|
|
52
|
-
- use the same
|
|
43
|
+
- use the same provider and dimensions at index and query time
|
|
53
44
|
- keep `maxLength` intentional if your content is very large
|
|
54
45
|
- start with a smaller search `limit` and tune from real query behavior
|
|
55
46
|
|
|
56
47
|
## Build Integration
|
|
57
48
|
|
|
58
|
-
Many projects wire indexing into a dedicated script and call it before their
|
|
59
|
-
site build:
|
|
49
|
+
Many projects wire indexing into a dedicated script and call it before their site build:
|
|
60
50
|
|
|
61
51
|
```json
|
|
62
52
|
{
|
|
@@ -69,14 +59,11 @@ site build:
|
|
|
69
59
|
|
|
70
60
|
## Table Names
|
|
71
61
|
|
|
72
|
-
`tableName` must be an ASCII SQLite identifier matching
|
|
73
|
-
`[A-Za-z_][A-Za-z0-9_]*`. Valid names are quoted internally for table and index
|
|
74
|
-
SQL, so reserved words such as `"select"` work safely. Invalid names fail before
|
|
75
|
-
database calls or embedding generation.
|
|
62
|
+
`tableName` must be an ASCII SQLite identifier matching `[A-Za-z_][A-Za-z0-9_]*`. Valid names are quoted internally for table and index SQL, so reserved words such as `"select"` work safely. Invalid names fail before database calls or embedding generation.
|
|
76
63
|
|
|
77
64
|
## Runtime Notes
|
|
78
65
|
|
|
79
66
|
- local embeddings may download and cache a model on the first run
|
|
80
67
|
- Node users need `@libsql/client` installed alongside the package
|
|
81
|
-
-
|
|
82
|
-
|
|
68
|
+
- hosted providers send indexed or queried text to external services
|
|
69
|
+
- the repository validates package build and `deno check`, but indexing still depends on filesystem access
|