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/docs/PROVIDERS.md CHANGED
@@ -1,25 +1,35 @@
1
1
  # Embedding Providers
2
2
 
3
- `libsql-search` currently supports five embedding providers:
3
+ Use this page to choose an embedding provider, confirm the table width it needs, and understand what crosses a network boundary.
4
+
5
+ `libsql-search` supports these provider values:
4
6
 
5
7
  - `local`
6
8
  - `cloudflare`
7
9
  - `mistral`
8
10
  - `gemini`
9
11
  - `openai`
12
+ - `openai-compatible`
10
13
 
11
- Use the same provider and dimensions for both indexing and querying. A mismatch
12
- between stored vectors and query vectors will break search quality or fail at
13
- query time.
14
+ ## Shared Behavior
14
15
 
15
- ## Shared Options
16
+ All providers share the same `EmbeddingOptions` surface:
16
17
 
17
18
  ```ts
18
19
  interface EmbeddingOptions {
19
- provider?: "local" | "cloudflare" | "mistral" | "gemini" | "openai";
20
+ provider?:
21
+ | "local"
22
+ | "cloudflare"
23
+ | "mistral"
24
+ | "gemini"
25
+ | "openai"
26
+ | "openai-compatible";
20
27
  apiKey?: string;
21
28
  accountId?: string;
22
29
  apiToken?: string;
30
+ baseUrl?: string;
31
+ model?: string;
32
+ batchSize?: number;
23
33
  dimensions?: number;
24
34
  maxLength?: number;
25
35
  intent?: "document" | "query";
@@ -28,79 +38,37 @@ interface EmbeddingOptions {
28
38
  }
29
39
  ```
30
40
 
31
- - `provider` defaults to `"local"`
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`.
35
- - `maxLength` defaults to `8000`
36
- - `intent` can be `"document"` or `"query"`; indexing defaults to
37
- `"document"` and search defaults to `"query"` unless explicitly set
38
- - `timeoutMs` defaults to `30000`
39
- - `apiKey` is used by Mistral, Gemini, and OpenAI and falls back to
40
- `MISTRAL_API_KEY`, `GEMINI_API_KEY`, or `OPENAI_API_KEY`
41
- - `accountId` and `apiToken` are used by Cloudflare and fall back to
42
- `CLOUDFLARE_ACCOUNT_ID` and `CLOUDFLARE_API_TOKEN`
43
-
44
- ## Provider Contract
45
-
46
- Each provider exposes immutable metadata:
47
-
48
- ```ts
49
- interface EmbeddingProviderMetadata {
50
- name: "local" | "cloudflare" | "mistral" | "gemini" | "openai";
51
- model: string;
52
- dimensions: number;
53
- batch: {
54
- mode: "native" | "sequential";
55
- maxSize?: number;
56
- };
57
- }
58
- ```
41
+ Shared defaults and rules:
59
42
 
60
- Use `getEmbeddingProviderMetadata(options)` or
61
- `createEmbeddingProvider(options).metadata` to inspect the effective model,
62
- dimensions, and batch behavior. Metadata inspection does not require hosted
63
- provider credentials.
64
-
65
- Batch modes:
66
-
67
- - `"native"` means the upstream provider accepts the batch in one request
68
- - `"sequential"` means the library accepts a batch and processes items one at a
69
- time
70
- - when `maxSize` is present, it is a hard maximum enforced before provider or
71
- network work
72
-
73
- `generateEmbeddings(texts, options)` returns vectors in the same order as the
74
- input texts. Provider responses are validated before database writes:
75
-
76
- - result count must match input count
77
- - each vector must match the provider's effective dimensions
78
- - every vector value must be a finite number
79
- - indexed batch responses must contain unique contiguous indices and are
80
- reordered before being returned
43
+ - `provider` defaults to `local`
44
+ - `maxLength` defaults to `8000` code units
45
+ - `timeoutMs` defaults to `30000`
46
+ - `indexContent()` defaults to `intent: "document"`
47
+ - `search()` defaults to `intent: "query"`
48
+ - `getEmbeddingProviderMetadata()` reports the effective provider, model, dimensions, and batch mode without making a hosted call
49
+ - use one embedding space per table: if provider, dimensions, model selection, endpoint, or formatting contract changes, build a new table and reindex
81
50
 
82
- Empty batches return `[]` without loading a local model, creating hosted clients,
83
- or making network calls.
51
+ Intent behavior is intentionally narrow today:
84
52
 
85
- Lower-level provider clients return an `EmbeddingBatchResult` with the validated
86
- vectors plus provider, model, dimensions, and intent. The compatibility helpers
87
- `generateEmbedding()` and `generateEmbeddings()` return only arrays.
53
+ - only the current Gemini adapter changes the formatted payload for `"document"` versus `"query"`
54
+ - all other providers still carry `intent` metadata through the API, but they embed the same text string either way
88
55
 
89
- Cloudflare, Mistral, Gemini, and OpenAI clients are scoped to their current
90
- options. They are not cached globally across different credentials or
91
- configurations. The local Hugging Face Transformers pipeline is loaded lazily
92
- and cached by model name.
56
+ The `model` option is only used by `openai-compatible`.
93
57
 
94
- Hosted provider failures are reported with bounded provider/status/request-id
95
- context and without raw upstream bodies, credentials, Authorization headers, or
96
- full URLs with query strings.
58
+ ## Provider Matrix
97
59
 
98
- ## Local
60
+ | Provider | Literal | Upstream model used by this adapter | Dimensions | Credentials | Batching | Network and privacy boundary | Cost and table planning |
61
+ | --- | --- | --- | --- | --- | --- | --- | --- |
62
+ | Local | `local` | `Xenova/all-MiniLM-L6-v2` | Fixed `384` | None | Sequential in-process | No hosted API call. First use may download model artifacts and cache them locally. | No hosted API bill. Table must be `F32_BLOB(384)`. |
63
+ | Cloudflare Workers AI | `cloudflare` | `@cf/baai/bge-m3` | Fixed `1024` | `accountId` and `apiToken`, or `CLOUDFLARE_ACCOUNT_ID` and `CLOUDFLARE_API_TOKEN` | Native batch in one request | Indexed and queried text is sent to Cloudflare. | Check Cloudflare pricing before large rebuilds. Table must be `F32_BLOB(1024)`. |
64
+ | Mistral | `mistral` | `mistral-embed` | Fixed `1024` | `apiKey`, or `MISTRAL_API_KEY` | Native batch in one request | Indexed and queried text is sent to Mistral. | Check Mistral pricing before rebuilds. Table must be `F32_BLOB(1024)`. |
65
+ | Gemini | `gemini` | `gemini-embedding-2` | Default `3072`; allowed integers `128-3072` | `apiKey`, or `GEMINI_API_KEY` | Sequential SDK request per input | Indexed and queried text is sent to Google. The adapter currently rewrites payload text by intent. | Check Gemini pricing before rebuilds. Table width must match the chosen dimension count exactly. |
66
+ | OpenAI | `openai` | `text-embedding-3-small` when `dimensions <= 1536`, otherwise `text-embedding-3-large` | Default `768`; any positive integer accepted locally and forwarded as `dimensions` | `apiKey`, or `OPENAI_API_KEY` | Native batch in one request, max `2048` inputs | Indexed and queried text is sent to OpenAI. | Check OpenAI pricing before rebuilds. Table width must match the chosen dimension count exactly. |
67
+ | OpenAI-compatible | `openai-compatible` | Your configured `model` | Required positive integer; no default | `baseUrl`, `model`, and `dimensions` are required. `apiKey` is optional and never falls back to env. | Metadata reports `native`; outbound requests are chunked sequentially at `batchSize`, default `32` | Boundary depends on the operator behind `baseUrl`. Treat `baseUrl` as a trusted server-side setting and review HTTPS, SSRF, logging, and retention controls yourself. | Table width must match the configured `dimensions`. Any endpoint or model change should use a new table plus full reindex. |
99
68
 
100
- Provider value: `local`
69
+ ## Provider Notes
101
70
 
102
- The local provider loads `Xenova/all-MiniLM-L6-v2` through
103
- `@huggingface/transformers`.
71
+ ### Local
104
72
 
105
73
  ```ts
106
74
  embeddingOptions: {
@@ -108,26 +76,16 @@ embeddingOptions: {
108
76
  }
109
77
  ```
110
78
 
111
- Notes:
112
-
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
118
- - batch metadata is `{ mode: "sequential" }`
119
- - the first run downloads and caches the model and can take longer on a fresh
120
- machine
121
- - no API key is required
122
- - this remains the default provider for offline use
79
+ - fixed at `384` dimensions
80
+ - rejects any other `dimensions` value before loading the runtime
81
+ - uses `@huggingface/transformers` lazily and caches the local pipeline by model name
123
82
 
124
- ## Cloudflare Workers AI
83
+ References:
125
84
 
126
- Provider value: `cloudflare`
85
+ - [Transformers.js in Node.js](https://huggingface.co/docs/transformers.js/en/tutorials/node)
86
+ - [Transformers.js environment and cache controls](https://huggingface.co/docs/transformers.js/en/api/env)
127
87
 
128
- Cloudflare is the recommended hosted provider for low-cost Markdown search.
129
- It uses Workers AI `@cf/baai/bge-m3` through Cloudflare's OpenAI-compatible
130
- embeddings endpoint.
88
+ ### Cloudflare Workers AI
131
89
 
132
90
  ```ts
133
91
  embeddingOptions: {
@@ -137,25 +95,16 @@ embeddingOptions: {
137
95
  }
138
96
  ```
139
97
 
140
- Behavior:
141
-
142
- - if `accountId` is omitted, the library reads `CLOUDFLARE_ACCOUNT_ID`
143
- - if `apiToken` is omitted, the library reads `CLOUDFLARE_API_TOKEN`
144
- - blank Cloudflare credentials are treated as missing
145
- - `@cf/baai/bge-m3` returns 1024 dimensions
146
- - metadata reports `@cf/baai/bge-m3` and 1024 dimensions without requiring
147
- credentials
148
- - batch metadata is `{ mode: "native" }`
149
- - response items are reordered by provider-supplied index before being returned
150
- - Cloudflare does not accept custom dimensions in this provider; use
151
- `createTable(client, "articles", 1024)` for Cloudflare-backed indexes
98
+ - fixed at `1024` dimensions
99
+ - uses the account-scoped Workers AI embeddings endpoint
100
+ - blank credentials are treated as missing
152
101
 
153
- ## Mistral
102
+ References:
154
103
 
155
- Provider value: `mistral`
104
+ - [Cloudflare `@cf/baai/bge-m3`](https://developers.cloudflare.com/workers-ai/models/bge-m3/)
105
+ - [Cloudflare Workers AI pricing](https://developers.cloudflare.com/workers-ai/platform/pricing/)
156
106
 
157
- Mistral uses the hosted `mistral-embed` model through
158
- `https://api.mistral.ai/v1/embeddings`.
107
+ ### Mistral
159
108
 
160
109
  ```ts
161
110
  embeddingOptions: {
@@ -164,24 +113,17 @@ embeddingOptions: {
164
113
  }
165
114
  ```
166
115
 
167
- Behavior:
116
+ - fixed at `1024` dimensions
117
+ - sends `encoding_format: "float"`
118
+ - expects indexed upstream responses
168
119
 
169
- - if `apiKey` is omitted, the library reads `MISTRAL_API_KEY`
170
- - blank Mistral credentials are treated as missing
171
- - `mistral-embed` returns 1024 dimensions
172
- - metadata reports `mistral-embed` and 1024 dimensions without requiring
173
- credentials
174
- - batch metadata is `{ mode: "native" }`
175
- - request bodies send `encoding_format: "float"`
176
- - response items are reordered by provider-supplied index before being returned
177
- - Mistral does not accept custom dimensions in this provider; use
178
- `createTable(client, "articles", 1024)` for Mistral-backed indexes
120
+ References:
179
121
 
180
- ## Gemini
122
+ - [Mistral embeddings guide](https://docs.mistral.ai/studio/knowledge-rag/embeddings/text_embeddings)
123
+ - [Mistral embeddings API reference](https://docs.mistral.ai/api/endpoint/embeddings)
124
+ - [Mistral model overview for `mistral-embed`](https://docs.mistral.ai/models/mistral-embed-23-12)
181
125
 
182
- Provider value: `gemini`
183
-
184
- Gemini uses Google `gemini-embedding-2` through `@google/genai`.
126
+ ### Gemini
185
127
 
186
128
  ```ts
187
129
  embeddingOptions: {
@@ -191,26 +133,18 @@ embeddingOptions: {
191
133
  }
192
134
  ```
193
135
 
194
- Behavior:
195
-
196
- - if `apiKey` is omitted, the library reads `GEMINI_API_KEY`
197
- - blank Gemini credentials are treated as missing
198
- - Gemini defaults to 3072 dimensions
199
- - explicit Gemini dimensions must be integers from 128 through 3072
200
- - 768, 1536, and 3072 are recommended practical sizes
201
- - metadata reports `gemini-embedding-2` and the effective dimensions
202
- - batch metadata is `{ mode: "sequential" }`
203
- - the library sends one SDK request per input and verifies one vector per input
204
- - document inputs are formatted as `title: none | text: ...`
205
- - query inputs are formatted as `task: search result | query: ...`
206
- - the current implementation does not expose custom model selection
136
+ - defaults to `3072` dimensions
137
+ - accepts only integer dimensions from `128` through `3072`
138
+ - currently formats document inputs as `title: none | text: ...`
139
+ - currently formats query inputs as `task: search result | query: ...`
140
+ - sends one SDK request per input, even when you call `generateEmbeddings()`
207
141
 
208
- ## OpenAI
142
+ References:
209
143
 
210
- Provider value: `openai`
144
+ - [Gemini embeddings guide](https://ai.google.dev/gemini-api/docs/embeddings)
145
+ - [Gemini pricing](https://ai.google.dev/gemini-api/docs/pricing)
211
146
 
212
- OpenAI uses `text-embedding-3-small` when `dimensions <= 1536` and
213
- `text-embedding-3-large` when `dimensions > 1536`.
147
+ ### OpenAI
214
148
 
215
149
  ```ts
216
150
  embeddingOptions: {
@@ -220,42 +154,45 @@ embeddingOptions: {
220
154
  }
221
155
  ```
222
156
 
223
- Behavior:
224
-
225
- - if `apiKey` is omitted, the library reads `OPENAI_API_KEY`
226
- - the request sends the `dimensions` value to the OpenAI embeddings API
227
- - metadata reports `text-embedding-3-small` when `dimensions <= 1536` and
228
- `text-embedding-3-large` when `dimensions > 1536`
229
- - batch metadata is `{ mode: "native", maxSize: 2048 }`
230
- - use the same dimension count in `createTable()`
231
-
232
- ## Dimension Guidelines
233
-
234
- - `local` is fixed at `384`
235
- - `cloudflare` is fixed at `1024`
236
- - `mistral` is fixed at `1024`
237
- - Gemini defaults to `3072` and accepts explicit dimensions from `128` through
238
- `3072`; use `768`, `1536`, or `3072` unless you have a specific reason
239
- - OpenAI can be used at 1536 or 3072, or another supported OpenAI dimension
240
- value you explicitly set
241
-
242
- If you switch provider or dimensions for an existing table, recreate the table
243
- or rebuild the index into a separate table so stored vectors stay consistent.
244
-
245
- Existing Gemini indexes created with `text-embedding-004` must be fully
246
- re-embedded for `gemini-embedding-2`, even if you keep `dimensions: 768`,
247
- because both the model and query/document input formatting changed. If you move
248
- to the new 3072-dimensional default, create a new table or recreate the vector
249
- table first; `indexContent()` clears rows but does not change the `F32_BLOB`
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.
157
+ - defaults to `768` dimensions
158
+ - uses `text-embedding-3-small` through `1536`
159
+ - uses `text-embedding-3-large` above `1536`
160
+ - rejects batches above `2048` inputs before any network request
161
+
162
+ References:
163
+
164
+ - [OpenAI embeddings guide](https://developers.openai.com/api/docs/guides/embeddings)
165
+ - [OpenAI embeddings API reference](https://developers.openai.com/api/reference/resources/embeddings/methods/create/)
166
+ - [OpenAI data controls](https://developers.openai.com/api/docs/guides/your-data#default-usage-policies-by-endpoint)
167
+ - [OpenAI models overview](https://developers.openai.com/api/docs/models)
168
+ - [OpenAI API pricing](https://openai.com/api/pricing/)
169
+
170
+ ### OpenAI-compatible
171
+
172
+ ```ts
173
+ embeddingOptions: {
174
+ provider: "openai-compatible",
175
+ baseUrl: process.env.EMBEDDING_BASE_URL,
176
+ model: process.env.EMBEDDING_MODEL,
177
+ dimensions: Number(process.env.EMBEDDING_DIMENSIONS),
178
+ apiKey: process.env.EMBEDDING_API_KEY,
179
+ batchSize: 32,
180
+ }
181
+ ```
182
+
183
+ - `baseUrl`, `model`, and `dimensions` are required
184
+ - `baseUrl` must be an absolute `http` or `https` URL without URL credentials, query strings, or fragments
185
+ - the library normalizes `baseUrl` to an `/embeddings` endpoint
186
+ - `batchSize` defaults to `32` and controls outbound chunking
187
+ - `apiKey` is optional and never falls back to `OPENAI_API_KEY`
188
+
189
+ References:
190
+
191
+ - [Text Embeddings Inference quick tour](https://huggingface.co/docs/text-embeddings-inference/quick_tour)
192
+ - [Text Embeddings Inference CLI arguments](https://huggingface.co/docs/text-embeddings-inference/cli_arguments)
193
+
194
+ ## Next Steps
195
+
196
+ - [Integration examples](./INTEGRATIONS.md) for end-to-end configuration flows
197
+ - [Migration guide](./MIGRATIONS.md) before changing widths, models, or endpoints
198
+ - [Testing guide](./TESTING.md) for CI-safe provider coverage
package/docs/README.md CHANGED
@@ -1,16 +1,14 @@
1
1
  # Documentation
2
2
 
3
3
  This directory holds the longer-form reference material for `libsql-search`.
4
- Start with the page that matches the job you are doing:
5
4
 
6
- - [Provider guide](./PROVIDERS.md): local, Cloudflare, Mistral, Gemini, and
7
- OpenAI embedding options, dimensions, and API key behavior
5
+ - [Provider selection and configuration](./PROVIDERS.md): compare local, hosted, and custom embedding providers before you build an index
6
+ - [Integration examples](./INTEGRATIONS.md): reusable provider flow plus Astro and Next.js examples
7
+ - [Migration and reindexing guide](./MIGRATIONS.md): table-width changes, provider/model swaps, and safe cutovers
8
8
  - [API reference](./API.md): exported functions, option shapes, and result data
9
- - [Integration examples](./INTEGRATIONS.md): Astro and Next.js server-side usage
10
- - [Indexing and operations](./INDEXING.md): content layout, rebuild scripts,
11
- search quality tips, and indexing gotchas
12
- - [Troubleshooting](./TROUBLESHOOTING.md): known install/runtime issues
9
+ - [Indexing and operations](./INDEXING.md): content layout, rebuild behavior, and search quality notes
10
+ - [Testing guidance](./TESTING.md): CI-safe mocks and no-live-call policy
11
+ - [Troubleshooting](./TROUBLESHOOTING.md): known install and runtime issues
13
12
  - [Releasing](./RELEASING.md): maintainer release workflow
14
13
 
15
- For the shortest first-use path, go back to the repository
16
- [README](../README.md).
14
+ For the shortest first-use path, go back to the repository [README](../README.md).
@@ -0,0 +1,138 @@
1
+ # Testing Guidance
2
+
3
+ Routine unit tests and CI should not make live embedding-provider calls and should not require real provider credentials.
4
+
5
+ ## Repository Policy
6
+
7
+ - no live provider keys in unit tests or CI
8
+ - no routine network calls to hosted embedding providers in CI
9
+ - validate option handling and response parsing with mocks first
10
+ - assert failures happen before network calls when configuration is invalid
11
+
12
+ The current test suite follows that pattern in `tests/embeddings.test.ts` and [`tests/huggingface-transformers.mock.ts`](../tests/huggingface-transformers.mock.ts).
13
+
14
+ ## Local Provider Mocks
15
+
16
+ The local provider should use a lightweight Transformers.js mock instead of downloading the real model during routine tests.
17
+
18
+ ```ts
19
+ import {
20
+ huggingFaceTransformersMock,
21
+ resetHuggingFaceTransformersMock,
22
+ } from "./huggingface-transformers.mock.js";
23
+
24
+ beforeEach(() => {
25
+ resetHuggingFaceTransformersMock();
26
+ });
27
+ ```
28
+
29
+ The repository source file is `huggingface-transformers.mock.ts`. The example keeps the `.js` import suffix because this repo's ESM TypeScript source uses explicit `.js` relative imports that resolve after compilation.
30
+
31
+ Test the contract you care about:
32
+
33
+ - the library requests `Xenova/all-MiniLM-L6-v2`
34
+ - the call uses `pooling: "mean"` and `normalize: true`
35
+ - non-`384` local dimensions fail before runtime loading
36
+
37
+ ## HTTP Provider Mocks
38
+
39
+ Cloudflare, Mistral, OpenAI, and `openai-compatible` should use `fetch` mocks.
40
+
41
+ ```ts
42
+ import { vi } from "vitest";
43
+
44
+ const fetchMock = vi.fn().mockResolvedValue({
45
+ ok: true,
46
+ headers: new Headers(),
47
+ json: async () => ({
48
+ data: [
49
+ { index: 0, embedding: [1, 2] },
50
+ { index: 1, embedding: [3, 4] },
51
+ ],
52
+ }),
53
+ });
54
+
55
+ vi.stubGlobal("fetch", fetchMock);
56
+ ```
57
+
58
+ Useful assertions:
59
+
60
+ - request body includes the expected `model`
61
+ - OpenAI includes `dimensions`
62
+ - Mistral and `openai-compatible` include `encoding_format: "float"`
63
+ - `openai-compatible` chunking honors `batchSize`
64
+ - Cloudflare and Mistral reorder indexed responses correctly
65
+ - invalid config fails before `fetch` is called
66
+
67
+ ## Gemini SDK Mocks
68
+
69
+ Gemini uses the `@google/genai` SDK, so routine tests should mock the SDK client rather than calling Google.
70
+
71
+ ```ts
72
+ vi.mock("@google/genai", () => ({
73
+ GoogleGenAI: class {
74
+ readonly models = {
75
+ embedContent: vi.fn(async () => ({
76
+ embeddings: [{ values: [1, 2, 3] }],
77
+ })),
78
+ };
79
+ },
80
+ }));
81
+ ```
82
+
83
+ Useful assertions:
84
+
85
+ - the adapter sends `gemini-embedding-2`
86
+ - document intent formats text as `title: none | text: ...`
87
+ - query intent formats text as `task: search result | query: ...`
88
+ - explicit dimensions are forwarded as `outputDimensionality`
89
+ - invalid dimensions fail before SDK work
90
+
91
+ ## Environment Cleanup
92
+
93
+ Tests that touch provider credentials should save and restore environment variables so one case cannot leak into another:
94
+
95
+ ```ts
96
+ let originalOpenAIKey: string | undefined;
97
+
98
+ beforeEach(() => {
99
+ originalOpenAIKey = process.env.OPENAI_API_KEY;
100
+ delete process.env.OPENAI_API_KEY;
101
+ });
102
+
103
+ afterEach(() => {
104
+ if (originalOpenAIKey === undefined) {
105
+ delete process.env.OPENAI_API_KEY;
106
+ } else {
107
+ process.env.OPENAI_API_KEY = originalOpenAIKey;
108
+ }
109
+ });
110
+ ```
111
+
112
+ Apply the same pattern to `GEMINI_API_KEY`, `MISTRAL_API_KEY`, `CLOUDFLARE_ACCOUNT_ID`, and `CLOUDFLARE_API_TOKEN`.
113
+
114
+ ## Validation-before-network Coverage
115
+
116
+ Prefer tests that prove bad inputs fail locally:
117
+
118
+ - unknown provider
119
+ - missing provider credentials
120
+ - blank credentials where trimming is expected
121
+ - invalid local dimensions
122
+ - invalid Gemini dimensions
123
+ - invalid `openai-compatible` `baseUrl`
124
+ - invalid `openai-compatible` `batchSize`
125
+ - OpenAI batches above `2048`
126
+
127
+ These checks keep CI fast and prove the library rejects bad inputs before it ships them to a provider.
128
+
129
+ ## Optional Live Smoke Tests
130
+
131
+ If you want live provider smoke coverage, keep it outside routine CI:
132
+
133
+ - run it only when a developer explicitly opts in
134
+ - use dedicated throwaway credentials and test content
135
+ - isolate it from unit-test jobs
136
+ - expect provider cost and external data transfer
137
+
138
+ This repository does not require or expect live provider smoke tests for normal pull-request validation.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "libsql-search",
3
- "version": "0.6.0",
3
+ "version": "0.7.1",
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",