libsql-search 0.2.0 → 0.2.2
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/dist/index.cjs +39 -21
- package/dist/index.esm.js +39 -21
- package/package.json +4 -4
package/dist/index.cjs
CHANGED
|
@@ -74,8 +74,13 @@ function getOpenAIModel(dimensions) {
|
|
|
74
74
|
function hasOwnProperty(value, property) {
|
|
75
75
|
return Object.prototype.hasOwnProperty.call(value, property);
|
|
76
76
|
}
|
|
77
|
-
function redactErrorText(value) {
|
|
78
|
-
|
|
77
|
+
function redactErrorText(value, exactSecrets = []) {
|
|
78
|
+
let raw = value instanceof Error ? value.message : String(value);
|
|
79
|
+
for (const secret of exactSecrets) {
|
|
80
|
+
if (secret.length > 0) {
|
|
81
|
+
raw = raw.split(secret).join("[redacted]");
|
|
82
|
+
}
|
|
83
|
+
}
|
|
79
84
|
return raw.replace(/Bearer\s+[A-Za-z0-9._~+/=-]+/gi, "Bearer [redacted]").replace(/([?&](?:api[_-]?key|key|token)=)[^&\s]+/gi, "$1[redacted]").replace(/(authorization\s*[:=]\s*)[^\s,}]+/gi, "$1[redacted]").replace(/https?:\/\/[^\s)]+/gi, (match) => {
|
|
80
85
|
try {
|
|
81
86
|
const url = new URL(match);
|
|
@@ -85,14 +90,14 @@ function redactErrorText(value) {
|
|
|
85
90
|
}
|
|
86
91
|
}).slice(0, 300);
|
|
87
92
|
}
|
|
88
|
-
function providerError(provider, message, cause) {
|
|
89
|
-
const safeCause = cause === void 0 ? "" : `: ${redactErrorText(cause).trim()}`;
|
|
93
|
+
function providerError(provider, message, cause, exactSecrets = []) {
|
|
94
|
+
const safeCause = cause === void 0 ? "" : `: ${redactErrorText(cause, exactSecrets).trim()}`;
|
|
90
95
|
return new Error(`${provider} embedding error: ${message}${safeCause}`);
|
|
91
96
|
}
|
|
92
97
|
function getResponseHeader(response, name) {
|
|
93
98
|
return response.headers.get(name) ?? void 0;
|
|
94
99
|
}
|
|
95
|
-
async function withTimeout(provider, operation, timeoutMs, run, parentSignal) {
|
|
100
|
+
async function withTimeout(provider, operation, timeoutMs, run, parentSignal, exactSecrets = []) {
|
|
96
101
|
if (parentSignal?.aborted) {
|
|
97
102
|
throw providerError(provider, `${operation} was aborted`);
|
|
98
103
|
}
|
|
@@ -124,7 +129,7 @@ async function withTimeout(provider, operation, timeoutMs, run, parentSignal) {
|
|
|
124
129
|
if (error === abortError) {
|
|
125
130
|
throw error;
|
|
126
131
|
}
|
|
127
|
-
throw providerError(provider, `${operation} failed`, error);
|
|
132
|
+
throw providerError(provider, `${operation} failed`, error, exactSecrets);
|
|
128
133
|
} finally {
|
|
129
134
|
clearTimeout(timeout);
|
|
130
135
|
parentSignal?.removeEventListener("abort", onParentAbort);
|
|
@@ -182,6 +187,21 @@ function validateEmbeddingVector(embedding, expectedDimensions, provider, itemIn
|
|
|
182
187
|
}
|
|
183
188
|
return embedding;
|
|
184
189
|
}
|
|
190
|
+
function throwIfAborted(provider, operation, signal) {
|
|
191
|
+
if (signal.aborted) {
|
|
192
|
+
throw providerError(provider, `${operation} was aborted`);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
async function embedSequentially(provider, operation, texts, signal, embedOne) {
|
|
196
|
+
const results = [];
|
|
197
|
+
for (const text of texts) {
|
|
198
|
+
throwIfAborted(provider, operation, signal);
|
|
199
|
+
const embedding = await embedOne(text, signal);
|
|
200
|
+
throwIfAborted(provider, operation, signal);
|
|
201
|
+
results.push(embedding);
|
|
202
|
+
}
|
|
203
|
+
return results;
|
|
204
|
+
}
|
|
185
205
|
function createProviderMetadata(provider, dimensions) {
|
|
186
206
|
switch (provider) {
|
|
187
207
|
case "local":
|
|
@@ -247,18 +267,16 @@ class LocalEmbeddingProvider {
|
|
|
247
267
|
"local",
|
|
248
268
|
"model inference",
|
|
249
269
|
this.#timeoutMs,
|
|
250
|
-
async () => {
|
|
270
|
+
async (signal) => {
|
|
251
271
|
const model = await getLocalEmbeddingModel(this.metadata.model);
|
|
252
|
-
|
|
253
|
-
for (const text of texts) {
|
|
272
|
+
return embedSequentially("local", "model inference", texts, signal, async (text) => {
|
|
254
273
|
const output = await model(text, {
|
|
255
274
|
pooling: "mean",
|
|
256
275
|
normalize: true
|
|
257
276
|
});
|
|
258
277
|
const embedding = Array.from(output.data);
|
|
259
|
-
|
|
260
|
-
}
|
|
261
|
-
return results;
|
|
278
|
+
return padEmbedding(embedding, this.metadata.dimensions);
|
|
279
|
+
});
|
|
262
280
|
},
|
|
263
281
|
options.signal
|
|
264
282
|
);
|
|
@@ -288,22 +306,21 @@ class GeminiEmbeddingProvider {
|
|
|
288
306
|
"gemini",
|
|
289
307
|
"API request",
|
|
290
308
|
this.#timeoutMs,
|
|
291
|
-
async () => {
|
|
309
|
+
async (signal) => {
|
|
292
310
|
const { GoogleGenerativeAI } = await import('@google/generative-ai');
|
|
293
311
|
const genAI = new GoogleGenerativeAI(this.#apiKey);
|
|
294
312
|
const model = genAI.getGenerativeModel({ model: this.metadata.model });
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
const result = await model.embedContent(text);
|
|
313
|
+
return embedSequentially("gemini", "API request", texts, signal, async (text, itemSignal) => {
|
|
314
|
+
const result = await model.embedContent(text, { signal: itemSignal });
|
|
298
315
|
const values = result.embedding?.values;
|
|
299
316
|
if (!Array.isArray(values)) {
|
|
300
317
|
throw new Error("Gemini response did not include embedding values");
|
|
301
318
|
}
|
|
302
|
-
|
|
303
|
-
}
|
|
304
|
-
return results;
|
|
319
|
+
return values;
|
|
320
|
+
});
|
|
305
321
|
},
|
|
306
|
-
options.signal
|
|
322
|
+
options.signal,
|
|
323
|
+
[this.#apiKey]
|
|
307
324
|
);
|
|
308
325
|
return createEmbeddingBatchResult(
|
|
309
326
|
this.metadata,
|
|
@@ -367,7 +384,8 @@ class OpenAIEmbeddingProvider {
|
|
|
367
384
|
return parsed;
|
|
368
385
|
});
|
|
369
386
|
},
|
|
370
|
-
options.signal
|
|
387
|
+
options.signal,
|
|
388
|
+
[this.#apiKey]
|
|
371
389
|
);
|
|
372
390
|
return createEmbeddingBatchResult(
|
|
373
391
|
this.metadata,
|
package/dist/index.esm.js
CHANGED
|
@@ -72,8 +72,13 @@ function getOpenAIModel(dimensions) {
|
|
|
72
72
|
function hasOwnProperty(value, property) {
|
|
73
73
|
return Object.prototype.hasOwnProperty.call(value, property);
|
|
74
74
|
}
|
|
75
|
-
function redactErrorText(value) {
|
|
76
|
-
|
|
75
|
+
function redactErrorText(value, exactSecrets = []) {
|
|
76
|
+
let raw = value instanceof Error ? value.message : String(value);
|
|
77
|
+
for (const secret of exactSecrets) {
|
|
78
|
+
if (secret.length > 0) {
|
|
79
|
+
raw = raw.split(secret).join("[redacted]");
|
|
80
|
+
}
|
|
81
|
+
}
|
|
77
82
|
return raw.replace(/Bearer\s+[A-Za-z0-9._~+/=-]+/gi, "Bearer [redacted]").replace(/([?&](?:api[_-]?key|key|token)=)[^&\s]+/gi, "$1[redacted]").replace(/(authorization\s*[:=]\s*)[^\s,}]+/gi, "$1[redacted]").replace(/https?:\/\/[^\s)]+/gi, (match) => {
|
|
78
83
|
try {
|
|
79
84
|
const url = new URL(match);
|
|
@@ -83,14 +88,14 @@ function redactErrorText(value) {
|
|
|
83
88
|
}
|
|
84
89
|
}).slice(0, 300);
|
|
85
90
|
}
|
|
86
|
-
function providerError(provider, message, cause) {
|
|
87
|
-
const safeCause = cause === void 0 ? "" : `: ${redactErrorText(cause).trim()}`;
|
|
91
|
+
function providerError(provider, message, cause, exactSecrets = []) {
|
|
92
|
+
const safeCause = cause === void 0 ? "" : `: ${redactErrorText(cause, exactSecrets).trim()}`;
|
|
88
93
|
return new Error(`${provider} embedding error: ${message}${safeCause}`);
|
|
89
94
|
}
|
|
90
95
|
function getResponseHeader(response, name) {
|
|
91
96
|
return response.headers.get(name) ?? void 0;
|
|
92
97
|
}
|
|
93
|
-
async function withTimeout(provider, operation, timeoutMs, run, parentSignal) {
|
|
98
|
+
async function withTimeout(provider, operation, timeoutMs, run, parentSignal, exactSecrets = []) {
|
|
94
99
|
if (parentSignal?.aborted) {
|
|
95
100
|
throw providerError(provider, `${operation} was aborted`);
|
|
96
101
|
}
|
|
@@ -122,7 +127,7 @@ async function withTimeout(provider, operation, timeoutMs, run, parentSignal) {
|
|
|
122
127
|
if (error === abortError) {
|
|
123
128
|
throw error;
|
|
124
129
|
}
|
|
125
|
-
throw providerError(provider, `${operation} failed`, error);
|
|
130
|
+
throw providerError(provider, `${operation} failed`, error, exactSecrets);
|
|
126
131
|
} finally {
|
|
127
132
|
clearTimeout(timeout);
|
|
128
133
|
parentSignal?.removeEventListener("abort", onParentAbort);
|
|
@@ -180,6 +185,21 @@ function validateEmbeddingVector(embedding, expectedDimensions, provider, itemIn
|
|
|
180
185
|
}
|
|
181
186
|
return embedding;
|
|
182
187
|
}
|
|
188
|
+
function throwIfAborted(provider, operation, signal) {
|
|
189
|
+
if (signal.aborted) {
|
|
190
|
+
throw providerError(provider, `${operation} was aborted`);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
async function embedSequentially(provider, operation, texts, signal, embedOne) {
|
|
194
|
+
const results = [];
|
|
195
|
+
for (const text of texts) {
|
|
196
|
+
throwIfAborted(provider, operation, signal);
|
|
197
|
+
const embedding = await embedOne(text, signal);
|
|
198
|
+
throwIfAborted(provider, operation, signal);
|
|
199
|
+
results.push(embedding);
|
|
200
|
+
}
|
|
201
|
+
return results;
|
|
202
|
+
}
|
|
183
203
|
function createProviderMetadata(provider, dimensions) {
|
|
184
204
|
switch (provider) {
|
|
185
205
|
case "local":
|
|
@@ -245,18 +265,16 @@ class LocalEmbeddingProvider {
|
|
|
245
265
|
"local",
|
|
246
266
|
"model inference",
|
|
247
267
|
this.#timeoutMs,
|
|
248
|
-
async () => {
|
|
268
|
+
async (signal) => {
|
|
249
269
|
const model = await getLocalEmbeddingModel(this.metadata.model);
|
|
250
|
-
|
|
251
|
-
for (const text of texts) {
|
|
270
|
+
return embedSequentially("local", "model inference", texts, signal, async (text) => {
|
|
252
271
|
const output = await model(text, {
|
|
253
272
|
pooling: "mean",
|
|
254
273
|
normalize: true
|
|
255
274
|
});
|
|
256
275
|
const embedding = Array.from(output.data);
|
|
257
|
-
|
|
258
|
-
}
|
|
259
|
-
return results;
|
|
276
|
+
return padEmbedding(embedding, this.metadata.dimensions);
|
|
277
|
+
});
|
|
260
278
|
},
|
|
261
279
|
options.signal
|
|
262
280
|
);
|
|
@@ -286,22 +304,21 @@ class GeminiEmbeddingProvider {
|
|
|
286
304
|
"gemini",
|
|
287
305
|
"API request",
|
|
288
306
|
this.#timeoutMs,
|
|
289
|
-
async () => {
|
|
307
|
+
async (signal) => {
|
|
290
308
|
const { GoogleGenerativeAI } = await import('@google/generative-ai');
|
|
291
309
|
const genAI = new GoogleGenerativeAI(this.#apiKey);
|
|
292
310
|
const model = genAI.getGenerativeModel({ model: this.metadata.model });
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
const result = await model.embedContent(text);
|
|
311
|
+
return embedSequentially("gemini", "API request", texts, signal, async (text, itemSignal) => {
|
|
312
|
+
const result = await model.embedContent(text, { signal: itemSignal });
|
|
296
313
|
const values = result.embedding?.values;
|
|
297
314
|
if (!Array.isArray(values)) {
|
|
298
315
|
throw new Error("Gemini response did not include embedding values");
|
|
299
316
|
}
|
|
300
|
-
|
|
301
|
-
}
|
|
302
|
-
return results;
|
|
317
|
+
return values;
|
|
318
|
+
});
|
|
303
319
|
},
|
|
304
|
-
options.signal
|
|
320
|
+
options.signal,
|
|
321
|
+
[this.#apiKey]
|
|
305
322
|
);
|
|
306
323
|
return createEmbeddingBatchResult(
|
|
307
324
|
this.metadata,
|
|
@@ -365,7 +382,8 @@ class OpenAIEmbeddingProvider {
|
|
|
365
382
|
return parsed;
|
|
366
383
|
});
|
|
367
384
|
},
|
|
368
|
-
options.signal
|
|
385
|
+
options.signal,
|
|
386
|
+
[this.#apiKey]
|
|
369
387
|
);
|
|
370
388
|
return createEmbeddingBatchResult(
|
|
371
389
|
this.metadata,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "libsql-search",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
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",
|
|
@@ -74,13 +74,13 @@
|
|
|
74
74
|
"@rollup/plugin-commonjs": "^28.0.7",
|
|
75
75
|
"@rollup/plugin-node-resolve": "^16.0.3",
|
|
76
76
|
"@types/node": "^20.19.21",
|
|
77
|
-
"@vitest/coverage-v8": "
|
|
78
|
-
"@vitest/ui": "^
|
|
77
|
+
"@vitest/coverage-v8": "4.1.11",
|
|
78
|
+
"@vitest/ui": "^4.1.11",
|
|
79
79
|
"rollup": "^4.62.5",
|
|
80
80
|
"rollup-plugin-dts": "^6.5.1",
|
|
81
81
|
"rollup-plugin-esbuild": "6.2.1",
|
|
82
82
|
"tslib": "^2.8.1",
|
|
83
83
|
"typescript": "^5.9.3",
|
|
84
|
-
"vitest": "^
|
|
84
|
+
"vitest": "^4.1.11"
|
|
85
85
|
}
|
|
86
86
|
}
|