libsql-search 0.5.0 → 0.6.0

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 CHANGED
@@ -20,8 +20,8 @@ Use it when you want:
20
20
 
21
21
  - Markdown indexing from local directories with frontmatter via `gray-matter`
22
22
  - libSQL/Turso storage and vector search
23
- - Embedding providers: local `Xenova/all-MiniLM-L6-v2`, Cloudflare Workers AI
24
- `@cf/baai/bge-m3`, Mistral `mistral-embed`, Google Gemini
23
+ - Embedding providers: local Hugging Face `Xenova/all-MiniLM-L6-v2`,
24
+ Cloudflare Workers AI `@cf/baai/bge-m3`, Mistral `mistral-embed`, Google Gemini
25
25
  `gemini-embedding-2`, and OpenAI `text-embedding-3-small` /
26
26
  `text-embedding-3-large`
27
27
  - npm distribution plus JSR publishing
@@ -65,14 +65,13 @@ const client = createClient({
65
65
  authToken: "your-auth-token",
66
66
  });
67
67
 
68
- await createTable(client, "articles", 768);
68
+ await createTable(client);
69
69
 
70
70
  await indexContent({
71
71
  client,
72
72
  contentPath: "./content",
73
73
  embeddingOptions: {
74
74
  provider: "local",
75
- dimensions: 768,
76
75
  },
77
76
  });
78
77
 
@@ -82,7 +81,6 @@ const results = await search({
82
81
  limit: 5,
83
82
  embeddingOptions: {
84
83
  provider: "local",
85
- dimensions: 768,
86
84
  },
87
85
  });
88
86
 
@@ -98,9 +96,9 @@ Important behavior:
98
96
  - Call `createTable()` before indexing or searching.
99
97
  - Keep dimensions aligned across table creation, indexing, and search queries.
100
98
  - `indexContent()` clears existing rows before rebuilding the index.
101
- - `local` is the default offline provider; Cloudflare is the recommended hosted
102
- option. Cloudflare and Mistral use 1024 dimensions. Gemini defaults to 3072
103
- dimensions and supports 128-3072.
99
+ - `local` is the default offline provider and uses 384 dimensions. Cloudflare is
100
+ the recommended hosted option. Cloudflare and Mistral use 1024 dimensions.
101
+ Gemini defaults to 3072 dimensions and supports 128-3072.
104
102
 
105
103
  ## Core API
106
104
 
package/dist/index.cjs CHANGED
@@ -4,7 +4,8 @@ var promises = require('fs/promises');
4
4
  var path = require('path');
5
5
  var matter = require('gray-matter');
6
6
 
7
- const DEFAULT_DIMENSIONS = 768;
7
+ const OPENAI_DEFAULT_DIMENSIONS = 768;
8
+ const LOCAL_DIMENSIONS = 384;
8
9
  const DEFAULT_MAX_LENGTH = 8e3;
9
10
  const DEFAULT_TIMEOUT_MS = 3e4;
10
11
  const LOCAL_MODEL = "Xenova/all-MiniLM-L6-v2";
@@ -33,17 +34,66 @@ function getEnvironmentVariable(name) {
33
34
  return void 0;
34
35
  }
35
36
  }
36
- async function getLocalEmbeddingModel(modelName) {
37
+ function deletePendingLocalModelCache(modelName, entry) {
38
+ if (!entry.settled && entry.waiters === 0 && localModelCacheByModel.get(modelName) === entry) {
39
+ localModelCacheByModel.delete(modelName);
40
+ }
41
+ }
42
+ async function getLocalEmbeddingModel(modelName, signal) {
37
43
  const cached = localModelCacheByModel.get(modelName);
38
- if (cached?.model) {
39
- return cached.model;
44
+ if (cached) {
45
+ cached.waiters++;
46
+ try {
47
+ return await waitForLocalEmbeddingModel(modelName, cached, signal);
48
+ } finally {
49
+ cached.waiters--;
50
+ deletePendingLocalModelCache(modelName, cached);
51
+ }
52
+ }
53
+ const modelPromise = (async () => {
54
+ console.log(`Loading local embedding model (${modelName})...`);
55
+ const { pipeline } = await import('@huggingface/transformers');
56
+ const model = await pipeline("feature-extraction", modelName);
57
+ console.log("Local model loaded successfully");
58
+ return model;
59
+ })();
60
+ const entry = {
61
+ promise: modelPromise,
62
+ settled: false,
63
+ waiters: 1
64
+ };
65
+ localModelCacheByModel.set(modelName, entry);
66
+ modelPromise.then(() => {
67
+ entry.settled = true;
68
+ }).catch(() => {
69
+ localModelCacheByModel.delete(modelName);
70
+ });
71
+ try {
72
+ return await waitForLocalEmbeddingModel(modelName, entry, signal);
73
+ } finally {
74
+ entry.waiters--;
75
+ deletePendingLocalModelCache(modelName, entry);
76
+ }
77
+ }
78
+ async function waitForLocalEmbeddingModel(modelName, entry, signal) {
79
+ if (signal.aborted) {
80
+ deletePendingLocalModelCache(modelName, entry);
81
+ throw providerError("local", "model inference was aborted");
82
+ }
83
+ let rejectAbort = () => {
84
+ };
85
+ const abortPromise = new Promise((_resolve, reject) => {
86
+ rejectAbort = reject;
87
+ });
88
+ const onAbort = () => {
89
+ rejectAbort(providerError("local", "model inference was aborted"));
90
+ };
91
+ signal.addEventListener("abort", onAbort, { once: true });
92
+ try {
93
+ return await Promise.race([entry.promise, abortPromise]);
94
+ } finally {
95
+ signal.removeEventListener("abort", onAbort);
40
96
  }
41
- console.log(`Loading local embedding model (${modelName})...`);
42
- const { pipeline } = await import('@xenova/transformers');
43
- const model = await pipeline("feature-extraction", modelName);
44
- localModelCacheByModel.set(modelName, { model });
45
- console.log("Local model loaded successfully");
46
- return model;
47
97
  }
48
98
  function getPositiveInteger(value, optionName) {
49
99
  if (!Number.isInteger(value) || value <= 0) {
@@ -285,6 +335,15 @@ function createProviderMetadata(provider, dimensions) {
285
335
  }
286
336
  }
287
337
  function getEffectiveDimensions(provider, dimensions) {
338
+ if (provider === "local") {
339
+ if (dimensions !== void 0 && dimensions !== LOCAL_DIMENSIONS) {
340
+ throw providerError(
341
+ "local",
342
+ `${LOCAL_MODEL} returns ${LOCAL_DIMENSIONS} dimensions; received dimensions ${String(dimensions)}`
343
+ );
344
+ }
345
+ return LOCAL_DIMENSIONS;
346
+ }
288
347
  if (provider === "gemini") {
289
348
  const effectiveDimensions = dimensions ?? GEMINI_DIMENSIONS;
290
349
  if (!Number.isInteger(effectiveDimensions) || effectiveDimensions < GEMINI_MIN_DIMENSIONS || effectiveDimensions > GEMINI_MAX_DIMENSIONS) {
@@ -313,7 +372,7 @@ function getEffectiveDimensions(provider, dimensions) {
313
372
  }
314
373
  return CLOUDFLARE_DIMENSIONS;
315
374
  }
316
- return getPositiveInteger(dimensions ?? DEFAULT_DIMENSIONS, "dimensions");
375
+ return getPositiveInteger(dimensions ?? OPENAI_DEFAULT_DIMENSIONS, "dimensions");
317
376
  }
318
377
  function assertBatchSize(metadata, count) {
319
378
  if (metadata.batch.maxSize !== void 0 && count > metadata.batch.maxSize) {
@@ -350,14 +409,13 @@ class LocalEmbeddingProvider {
350
409
  "model inference",
351
410
  this.#timeoutMs,
352
411
  async (signal) => {
353
- const model = await getLocalEmbeddingModel(this.metadata.model);
412
+ const model = await getLocalEmbeddingModel(this.metadata.model, signal);
354
413
  return embedSequentially("local", "model inference", texts, signal, async (text) => {
355
414
  const output = await model(text, {
356
415
  pooling: "mean",
357
416
  normalize: true
358
417
  });
359
- const embedding = Array.from(output.data);
360
- return padEmbedding(embedding, this.metadata.dimensions);
418
+ return Array.from(output.data);
361
419
  });
362
420
  },
363
421
  options.signal
@@ -824,7 +882,7 @@ async function insertDocument(client, document, quotedTableName) {
824
882
  ]
825
883
  });
826
884
  }
827
- async function createTable(client, tableName = "articles", dimensions = 768) {
885
+ async function createTable(client, tableName = "articles", dimensions = 384) {
828
886
  const quotedTableName = quoteSqlIdentifier(tableName, "tableName");
829
887
  const vectorDimensions = normalizeVectorDimensions(dimensions);
830
888
  const quotedEmbeddingIndexName = quoteSqlIdentifier(`${tableName}_embedding_idx`, "embedding index name");
package/dist/index.d.ts CHANGED
@@ -1,5 +1,9 @@
1
1
  import { Client } from '@libsql/client';
2
2
 
3
+ /**
4
+ * Multi-provider embedding generation
5
+ * Supports local Hugging Face Transformers, Gemini, OpenAI, Mistral, and Cloudflare Workers AI
6
+ */
3
7
  type EmbeddingProvider = 'local' | 'gemini' | 'openai' | 'mistral' | 'cloudflare';
4
8
  type EmbeddingIntent = 'document' | 'query';
5
9
  type EmbeddingBatchMode = 'native' | 'sequential';
package/dist/index.esm.js CHANGED
@@ -2,7 +2,8 @@ import { readdir, readFile } from 'fs/promises';
2
2
  import { join, extname, relative, dirname } from 'path';
3
3
  import matter from 'gray-matter';
4
4
 
5
- const DEFAULT_DIMENSIONS = 768;
5
+ const OPENAI_DEFAULT_DIMENSIONS = 768;
6
+ const LOCAL_DIMENSIONS = 384;
6
7
  const DEFAULT_MAX_LENGTH = 8e3;
7
8
  const DEFAULT_TIMEOUT_MS = 3e4;
8
9
  const LOCAL_MODEL = "Xenova/all-MiniLM-L6-v2";
@@ -31,17 +32,66 @@ function getEnvironmentVariable(name) {
31
32
  return void 0;
32
33
  }
33
34
  }
34
- async function getLocalEmbeddingModel(modelName) {
35
+ function deletePendingLocalModelCache(modelName, entry) {
36
+ if (!entry.settled && entry.waiters === 0 && localModelCacheByModel.get(modelName) === entry) {
37
+ localModelCacheByModel.delete(modelName);
38
+ }
39
+ }
40
+ async function getLocalEmbeddingModel(modelName, signal) {
35
41
  const cached = localModelCacheByModel.get(modelName);
36
- if (cached?.model) {
37
- return cached.model;
42
+ if (cached) {
43
+ cached.waiters++;
44
+ try {
45
+ return await waitForLocalEmbeddingModel(modelName, cached, signal);
46
+ } finally {
47
+ cached.waiters--;
48
+ deletePendingLocalModelCache(modelName, cached);
49
+ }
50
+ }
51
+ const modelPromise = (async () => {
52
+ console.log(`Loading local embedding model (${modelName})...`);
53
+ const { pipeline } = await import('@huggingface/transformers');
54
+ const model = await pipeline("feature-extraction", modelName);
55
+ console.log("Local model loaded successfully");
56
+ return model;
57
+ })();
58
+ const entry = {
59
+ promise: modelPromise,
60
+ settled: false,
61
+ waiters: 1
62
+ };
63
+ localModelCacheByModel.set(modelName, entry);
64
+ modelPromise.then(() => {
65
+ entry.settled = true;
66
+ }).catch(() => {
67
+ localModelCacheByModel.delete(modelName);
68
+ });
69
+ try {
70
+ return await waitForLocalEmbeddingModel(modelName, entry, signal);
71
+ } finally {
72
+ entry.waiters--;
73
+ deletePendingLocalModelCache(modelName, entry);
74
+ }
75
+ }
76
+ async function waitForLocalEmbeddingModel(modelName, entry, signal) {
77
+ if (signal.aborted) {
78
+ deletePendingLocalModelCache(modelName, entry);
79
+ throw providerError("local", "model inference was aborted");
80
+ }
81
+ let rejectAbort = () => {
82
+ };
83
+ const abortPromise = new Promise((_resolve, reject) => {
84
+ rejectAbort = reject;
85
+ });
86
+ const onAbort = () => {
87
+ rejectAbort(providerError("local", "model inference was aborted"));
88
+ };
89
+ signal.addEventListener("abort", onAbort, { once: true });
90
+ try {
91
+ return await Promise.race([entry.promise, abortPromise]);
92
+ } finally {
93
+ signal.removeEventListener("abort", onAbort);
38
94
  }
39
- console.log(`Loading local embedding model (${modelName})...`);
40
- const { pipeline } = await import('@xenova/transformers');
41
- const model = await pipeline("feature-extraction", modelName);
42
- localModelCacheByModel.set(modelName, { model });
43
- console.log("Local model loaded successfully");
44
- return model;
45
95
  }
46
96
  function getPositiveInteger(value, optionName) {
47
97
  if (!Number.isInteger(value) || value <= 0) {
@@ -283,6 +333,15 @@ function createProviderMetadata(provider, dimensions) {
283
333
  }
284
334
  }
285
335
  function getEffectiveDimensions(provider, dimensions) {
336
+ if (provider === "local") {
337
+ if (dimensions !== void 0 && dimensions !== LOCAL_DIMENSIONS) {
338
+ throw providerError(
339
+ "local",
340
+ `${LOCAL_MODEL} returns ${LOCAL_DIMENSIONS} dimensions; received dimensions ${String(dimensions)}`
341
+ );
342
+ }
343
+ return LOCAL_DIMENSIONS;
344
+ }
286
345
  if (provider === "gemini") {
287
346
  const effectiveDimensions = dimensions ?? GEMINI_DIMENSIONS;
288
347
  if (!Number.isInteger(effectiveDimensions) || effectiveDimensions < GEMINI_MIN_DIMENSIONS || effectiveDimensions > GEMINI_MAX_DIMENSIONS) {
@@ -311,7 +370,7 @@ function getEffectiveDimensions(provider, dimensions) {
311
370
  }
312
371
  return CLOUDFLARE_DIMENSIONS;
313
372
  }
314
- return getPositiveInteger(dimensions ?? DEFAULT_DIMENSIONS, "dimensions");
373
+ return getPositiveInteger(dimensions ?? OPENAI_DEFAULT_DIMENSIONS, "dimensions");
315
374
  }
316
375
  function assertBatchSize(metadata, count) {
317
376
  if (metadata.batch.maxSize !== void 0 && count > metadata.batch.maxSize) {
@@ -348,14 +407,13 @@ class LocalEmbeddingProvider {
348
407
  "model inference",
349
408
  this.#timeoutMs,
350
409
  async (signal) => {
351
- const model = await getLocalEmbeddingModel(this.metadata.model);
410
+ const model = await getLocalEmbeddingModel(this.metadata.model, signal);
352
411
  return embedSequentially("local", "model inference", texts, signal, async (text) => {
353
412
  const output = await model(text, {
354
413
  pooling: "mean",
355
414
  normalize: true
356
415
  });
357
- const embedding = Array.from(output.data);
358
- return padEmbedding(embedding, this.metadata.dimensions);
416
+ return Array.from(output.data);
359
417
  });
360
418
  },
361
419
  options.signal
@@ -822,7 +880,7 @@ async function insertDocument(client, document, quotedTableName) {
822
880
  ]
823
881
  });
824
882
  }
825
- async function createTable(client, tableName = "articles", dimensions = 768) {
883
+ async function createTable(client, tableName = "articles", dimensions = 384) {
826
884
  const quotedTableName = quoteSqlIdentifier(tableName, "tableName");
827
885
  const vectorDimensions = normalizeVectorDimensions(dimensions);
828
886
  const quotedEmbeddingIndexName = quoteSqlIdentifier(`${tableName}_embedding_idx`, "embedding index name");
package/docs/API.md CHANGED
@@ -42,13 +42,13 @@ It also exports these types:
42
42
  Creates the table and supporting indexes used by search.
43
43
 
44
44
  ```ts
45
- await createTable(client, "articles", 768);
45
+ await createTable(client);
46
46
  ```
47
47
 
48
48
  Defaults:
49
49
 
50
50
  - `tableName`: `"articles"`
51
- - `dimensions`: `768`
51
+ - `dimensions`: `384`
52
52
 
53
53
  `tableName` must be an ASCII SQLite identifier matching
54
54
  `[A-Za-z_][A-Za-z0-9_]*`. Valid identifiers are quoted internally, so reserved
@@ -216,6 +216,10 @@ credentials or configuration.
216
216
  Gemini uses `gemini-embedding-2`. Its default is 3072 dimensions, and explicit
217
217
  Gemini dimensions must be an integer from 128 through 3072.
218
218
 
219
+ Local embeddings use `Xenova/all-MiniLM-L6-v2` through
220
+ `@huggingface/transformers` and are fixed at the model's native 384 dimensions.
221
+ Passing any other local dimension is rejected before the runtime is loaded.
222
+
219
223
  ### `getEmbeddingProviderMetadata(options?)`
220
224
 
221
225
  Returns the same metadata exposed by `createEmbeddingProvider(options).metadata`
@@ -279,6 +283,9 @@ Cloudflare uses `accountId` and `apiToken`, which fall back to
279
283
 
280
284
  Pads or truncates an embedding array to the requested length.
281
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.
288
+
282
289
  ### `prepareTextForEmbedding(fields)`
283
290
 
284
291
  Combines title, description, tags, and content into the text sent to the
package/docs/INDEXING.md CHANGED
@@ -25,7 +25,6 @@ await indexContent({
25
25
  tableName: "articles",
26
26
  embeddingOptions: {
27
27
  provider: "local",
28
- dimensions: 768,
29
28
  },
30
29
  });
31
30
  ```
@@ -34,6 +33,11 @@ That keeps the implementation simple, but it also means a failed rebuild can
34
33
  leave the index partially repopulated.
35
34
 
36
35
  Changing an embedding provider or dimension count requires a full re-embed.
36
+ For existing local indexes created with the older padded-local behavior, create
37
+ or recreate a 384-dimensional table before rebuilding. Those older local
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.
40
+
37
41
  For Gemini specifically, indexes created with the retired `text-embedding-004`
38
42
  model must be rebuilt for `gemini-embedding-2` even when staying at 768
39
43
  dimensions, because the model and query/document formatting both changed. If
@@ -72,7 +76,7 @@ database calls or embedding generation.
72
76
 
73
77
  ## Runtime Notes
74
78
 
75
- - local embeddings may download a model on the first run
79
+ - local embeddings may download and cache a model on the first run
76
80
  - Node users need `@libsql/client` installed alongside the package
77
81
  - the repository validates both the npm package build and `deno check`, but the
78
82
  indexing flow itself still depends on filesystem access
@@ -26,7 +26,6 @@ export const POST: APIRoute = async ({ request }) => {
26
26
  limit,
27
27
  embeddingOptions: {
28
28
  provider: "local",
29
- dimensions: 768,
30
29
  },
31
30
  });
32
31
 
@@ -79,7 +78,6 @@ export async function POST(request: NextRequest) {
79
78
  limit,
80
79
  embeddingOptions: {
81
80
  provider: "local",
82
- dimensions: 768,
83
81
  },
84
82
  });
85
83
 
@@ -135,12 +133,16 @@ const embeddingProvider =
135
133
  | "gemini"
136
134
  | "openai"
137
135
  | undefined;
138
- const embeddingDimensions =
139
- embeddingProvider === "cloudflare" || embeddingProvider === "mistral"
140
- ? 1024
141
- : embeddingProvider === "gemini"
142
- ? 3072
143
- : 768;
136
+
137
+ const dimensionsByProvider = {
138
+ local: 384,
139
+ cloudflare: 1024,
140
+ mistral: 1024,
141
+ gemini: 3072,
142
+ openai: 1536,
143
+ } as const;
144
+
145
+ const embeddingDimensions = dimensionsByProvider[embeddingProvider ?? "local"];
144
146
 
145
147
  await createTable(client, "articles", embeddingDimensions);
146
148
 
package/docs/PROVIDERS.md CHANGED
@@ -29,8 +29,9 @@ interface EmbeddingOptions {
29
29
  ```
30
30
 
31
31
  - `provider` defaults to `"local"`
32
- - `dimensions` defaults to `768` for the library's default local provider.
33
- Provider-specific defaults can differ; Gemini defaults to `3072`.
32
+ - `dimensions` defaults to `384` for the library's default local provider.
33
+ Provider-specific defaults can differ; Cloudflare and Mistral use `1024`,
34
+ and Gemini defaults to `3072`.
34
35
  - `maxLength` defaults to `8000`
35
36
  - `intent` can be `"document"` or `"query"`; indexing defaults to
36
37
  `"document"` and search defaults to `"query"` unless explicitly set
@@ -87,7 +88,8 @@ vectors plus provider, model, dimensions, and intent. The compatibility helpers
87
88
 
88
89
  Cloudflare, Mistral, Gemini, and OpenAI clients are scoped to their current
89
90
  options. They are not cached globally across different credentials or
90
- configurations. The local Xenova model can be cached by model name.
91
+ configurations. The local Hugging Face Transformers pipeline is loaded lazily
92
+ and cached by model name.
91
93
 
92
94
  Hosted provider failures are reported with bounded provider/status/request-id
93
95
  context and without raw upstream bodies, credentials, Authorization headers, or
@@ -98,24 +100,26 @@ full URLs with query strings.
98
100
  Provider value: `local`
99
101
 
100
102
  The local provider loads `Xenova/all-MiniLM-L6-v2` through
101
- `@xenova/transformers`.
103
+ `@huggingface/transformers`.
102
104
 
103
105
  ```ts
104
106
  embeddingOptions: {
105
107
  provider: "local",
106
- dimensions: 768,
107
108
  }
108
109
  ```
109
110
 
110
111
  Notes:
111
112
 
112
- - the model emits 384 dimensions and `libsql-search` pads or truncates to your
113
- requested size
114
- - metadata reports the requested output dimensions
113
+ - the model emits 384 dimensions, and local vectors are validated at exactly
114
+ 384 finite numbers
115
+ - `dimensions: 384` is accepted explicitly; any other local dimension is
116
+ rejected before the runtime is imported or loaded
117
+ - metadata reports 384 dimensions
115
118
  - batch metadata is `{ mode: "sequential" }`
116
- - the first run downloads the model and can take longer on a fresh machine
119
+ - the first run downloads and caches the model and can take longer on a fresh
120
+ machine
117
121
  - no API key is required
118
- - this remains the default provider for backward compatibility and offline use
122
+ - this remains the default provider for offline use
119
123
 
120
124
  ## Cloudflare Workers AI
121
125
 
@@ -227,10 +231,9 @@ Behavior:
227
231
 
228
232
  ## Dimension Guidelines
229
233
 
230
- - `local` defaults to `768`
234
+ - `local` is fixed at `384`
231
235
  - `cloudflare` is fixed at `1024`
232
236
  - `mistral` is fixed at `1024`
233
- - local embeddings are padded from 384 to your target size
234
237
  - Gemini defaults to `3072` and accepts explicit dimensions from `128` through
235
238
  `3072`; use `768`, `1536`, or `3072` unless you have a specific reason
236
239
  - OpenAI can be used at 1536 or 3072, or another supported OpenAI dimension
@@ -245,3 +248,14 @@ because both the model and query/document input formatting changed. If you move
245
248
  to the new 3072-dimensional default, create a new table or recreate the vector
246
249
  table first; `indexContent()` clears rows but does not change the `F32_BLOB`
247
250
  width. A separate table is safer because rebuilds are not transactional.
251
+
252
+ Existing local indexes created with the older padded-local behavior usually have
253
+ `F32_BLOB(768)` rows containing the 384 model values followed by zero padding.
254
+ The current local contract stores the native 384-dimensional model output. To
255
+ migrate, create or recreate a `F32_BLOB(384)` table and run a full re-index
256
+ before querying it. Using the same model ID avoids an intentional model-space
257
+ change, but bit-identical vectors are not promised across runtime, model
258
+ revision, dtype, pooling, or normalization changes; validate search quality and
259
+ re-index when those details change.
260
+
261
+ Routine unit tests mock the local runtime and do not download the model.
@@ -1,11 +1,12 @@
1
1
  # Troubleshooting: Transitive `sharp` Install Errors
2
2
 
3
- `libsql-search` does not directly depend on `sharp`. If you see an install error
4
- mentioning `sharp`, it is coming from another dependency in your application or
5
- toolchain.
3
+ `libsql-search` does not directly import `sharp`, but local embeddings use
4
+ `@huggingface/transformers`, which currently brings in `sharp` as a transitive
5
+ runtime dependency. If you see an install error mentioning `sharp`, it is
6
+ usually a native-package install or approval issue.
6
7
 
7
- This page exists because the error can show up in environments that also use
8
- `libsql-search`, and it is easy to misattribute the failure to this package.
8
+ This page exists because the error can show up before your application reaches
9
+ any `libsql-search` code.
9
10
 
10
11
  ## Typical Error
11
12
 
@@ -48,7 +49,7 @@ in `pnpm-workspace.yaml` with `onlyBuiltDependencies`.
48
49
 
49
50
  ## Relation To `libsql-search`
50
51
 
51
- - local embeddings use `@xenova/transformers`
52
+ - local embeddings use `@huggingface/transformers`
52
53
  - the first local embedding run may download a model at runtime
53
54
  - that runtime model download is separate from a pnpm native-module install
54
55
  failure
@@ -15,3 +15,5 @@ Common operational checks:
15
15
  - after upgrading an existing Gemini index, fully re-embed with
16
16
  `gemini-embedding-2`; for 3072-dimensional Gemini indexes, recreate the table
17
17
  or use a new table name before rebuilding
18
+ - after upgrading an existing local 768-dimensional padded index, create or
19
+ recreate a 384-dimensional table and fully re-index before querying it
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "libsql-search",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "Semantic search for static sites using libSQL/Turso with multi-provider embeddings",
5
5
  "type": "module",
6
6
  "packageManager": "pnpm@10.34.5",
@@ -63,7 +63,7 @@
63
63
  "@libsql/client": "^0.15.0"
64
64
  },
65
65
  "dependencies": {
66
- "@xenova/transformers": "^2.17.2",
66
+ "@huggingface/transformers": "4.2.0",
67
67
  "gray-matter": "^4.0.3"
68
68
  },
69
69
  "devDependencies": {