@lobu/embeddings 7.0.0 → 7.1.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/dist/embedding-utils.d.ts.map +1 -1
- package/dist/embedding-utils.js +7 -8
- package/dist/embedding-utils.js.map +1 -1
- package/dist/embeddings.d.ts +1 -9
- package/dist/embeddings.d.ts.map +1 -1
- package/dist/embeddings.js +20 -41
- package/dist/embeddings.js.map +1 -1
- package/dist/openai.d.ts +16 -0
- package/dist/openai.d.ts.map +1 -1
- package/dist/openai.js +95 -16
- package/dist/openai.js.map +1 -1
- package/dist/server.js +54 -11
- package/dist/server.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"embedding-utils.d.ts","sourceRoot":"","sources":["../src/embedding-utils.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"embedding-utils.d.ts","sourceRoot":"","sources":["../src/embedding-utils.ts"],"names":[],"mappings":"AAAA,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE,GAAG,MAAM,EAAE,EAAE,CAQtE;AAED,wBAAgB,2BAA2B,CACzC,SAAS,EAAE,MAAM,EAAE,EACnB,kBAAkB,EAAE,MAAM,EAC1B,OAAO,EAAE,MAAM,GACd,IAAI,CAMN"}
|
package/dist/embedding-utils.js
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
function l2Normalize(vec) {
|
|
2
|
-
const norm = Math.sqrt(vec.reduce((sum, x) => sum + x * x, 0));
|
|
3
|
-
if (norm === 0) {
|
|
4
|
-
return vec;
|
|
5
|
-
}
|
|
6
|
-
return vec.map((x) => x / norm);
|
|
7
|
-
}
|
|
8
1
|
export function normalizeEmbeddings(embeddings) {
|
|
9
|
-
return embeddings.map((
|
|
2
|
+
return embeddings.map((vec) => {
|
|
3
|
+
const norm = Math.sqrt(vec.reduce((sum, x) => sum + x * x, 0));
|
|
4
|
+
if (norm === 0) {
|
|
5
|
+
return vec;
|
|
6
|
+
}
|
|
7
|
+
return vec.map((x) => x / norm);
|
|
8
|
+
});
|
|
10
9
|
}
|
|
11
10
|
export function validateEmbeddingDimensions(embedding, expectedDimensions, context) {
|
|
12
11
|
if (embedding.length !== expectedDimensions) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"embedding-utils.js","sourceRoot":"","sources":["../src/embedding-utils.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"embedding-utils.js","sourceRoot":"","sources":["../src/embedding-utils.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,mBAAmB,CAAC,UAAsB;IACxD,OAAO,UAAU,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE;QAC5B,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QAC/D,IAAI,IAAI,KAAK,CAAC,EAAE,CAAC;YACf,OAAO,GAAG,CAAC;QACb,CAAC;QACD,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IAClC,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,2BAA2B,CACzC,SAAmB,EACnB,kBAA0B,EAC1B,OAAe;IAEf,IAAI,SAAS,CAAC,MAAM,KAAK,kBAAkB,EAAE,CAAC;QAC5C,MAAM,IAAI,KAAK,CACb,GAAG,OAAO,qCAAqC,SAAS,CAAC,MAAM,cAAc,kBAAkB,GAAG,CACnG,CAAC;IACJ,CAAC;AACH,CAAC"}
|
package/dist/embeddings.d.ts
CHANGED
|
@@ -1,12 +1,4 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Local embedding generation with @xenova/transformers.
|
|
3
|
-
*/
|
|
4
|
-
export declare const DEFAULT_MODEL_NAME = "Xenova/bge-base-en-v1.5";
|
|
5
1
|
export declare const DEFAULT_DIMENSIONS = 768;
|
|
6
|
-
export declare function
|
|
7
|
-
model: string;
|
|
8
|
-
dimensions: number;
|
|
9
|
-
};
|
|
10
|
-
export declare function generateLocalEmbedding(text: string): Promise<number[]>;
|
|
2
|
+
export declare function getLocalModelName(): string;
|
|
11
3
|
export declare function batchGenerateLocalEmbeddings(texts: string[], batchSize?: number): Promise<number[][]>;
|
|
12
4
|
//# sourceMappingURL=embeddings.d.ts.map
|
package/dist/embeddings.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"embeddings.d.ts","sourceRoot":"","sources":["../src/embeddings.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"embeddings.d.ts","sourceRoot":"","sources":["../src/embeddings.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,kBAAkB,MAAM,CAAC;AAYtC,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C;AAyBD,wBAAsB,4BAA4B,CAChD,KAAK,EAAE,MAAM,EAAE,EACf,SAAS,GAAE,MAA2B,GACrC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CA2BrB"}
|
package/dist/embeddings.js
CHANGED
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Local embedding generation with @xenova/transformers.
|
|
3
|
-
*/
|
|
4
1
|
import { pipeline, env as transformersEnv, } from '@xenova/transformers';
|
|
5
|
-
|
|
2
|
+
const DEFAULT_MODEL_NAME = 'Xenova/bge-base-en-v1.5';
|
|
6
3
|
export const DEFAULT_DIMENSIONS = 768;
|
|
7
4
|
const DEFAULT_BATCH_SIZE = 32;
|
|
8
5
|
transformersEnv.cacheDir = process.env.TRANSFORMERS_CACHE || '~/.cache/huggingface/transformers/';
|
|
@@ -11,36 +8,27 @@ let extractorPromise = null;
|
|
|
11
8
|
function getModelName() {
|
|
12
9
|
return process.env.EMBEDDINGS_MODEL || DEFAULT_MODEL_NAME;
|
|
13
10
|
}
|
|
14
|
-
export function
|
|
15
|
-
return
|
|
11
|
+
export function getLocalModelName() {
|
|
12
|
+
return getModelName();
|
|
16
13
|
}
|
|
17
14
|
async function getExtractor() {
|
|
18
|
-
if (
|
|
19
|
-
|
|
20
|
-
console.log(`[EmbeddingsService] Loading model: ${modelName}...`);
|
|
21
|
-
const startTime = Date.now();
|
|
22
|
-
// Don't cache a rejected promise — a transient model-load failure would
|
|
23
|
-
// otherwise permanently brick the embeddings backend until process restart.
|
|
24
|
-
extractorPromise = pipeline('feature-extraction', modelName, {
|
|
25
|
-
quantized: true,
|
|
26
|
-
}).catch((err) => {
|
|
27
|
-
extractorPromise = null;
|
|
28
|
-
throw err;
|
|
29
|
-
});
|
|
30
|
-
const extractor = await extractorPromise;
|
|
31
|
-
const loadTime = Date.now() - startTime;
|
|
32
|
-
console.log(`[EmbeddingsService] Model loaded in ${loadTime}ms`);
|
|
33
|
-
return extractor;
|
|
15
|
+
if (extractorPromise) {
|
|
16
|
+
return extractorPromise;
|
|
34
17
|
}
|
|
35
|
-
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
18
|
+
const modelName = getModelName();
|
|
19
|
+
console.log(`[EmbeddingsService] Loading model: ${modelName}...`);
|
|
20
|
+
const startTime = Date.now();
|
|
21
|
+
// Don't cache a rejected promise — a transient model-load failure would
|
|
22
|
+
// otherwise permanently brick the embeddings backend until process restart.
|
|
23
|
+
extractorPromise = pipeline('feature-extraction', modelName, {
|
|
24
|
+
quantized: true,
|
|
25
|
+
}).catch((err) => {
|
|
26
|
+
extractorPromise = null;
|
|
27
|
+
throw err;
|
|
42
28
|
});
|
|
43
|
-
|
|
29
|
+
const extractor = await extractorPromise;
|
|
30
|
+
console.log(`[EmbeddingsService] Model loaded in ${Date.now() - startTime}ms`);
|
|
31
|
+
return extractor;
|
|
44
32
|
}
|
|
45
33
|
export async function batchGenerateLocalEmbeddings(texts, batchSize = DEFAULT_BATCH_SIZE) {
|
|
46
34
|
if (texts.length === 0) {
|
|
@@ -51,23 +39,14 @@ export async function batchGenerateLocalEmbeddings(texts, batchSize = DEFAULT_BA
|
|
|
51
39
|
for (let i = 0; i < texts.length; i += batchSize) {
|
|
52
40
|
const batch = texts.slice(i, i + batchSize);
|
|
53
41
|
// Pass the whole batch as an array — transformers.js runs a single padded,
|
|
54
|
-
// vectorized ONNX forward pass instead of N separate ones
|
|
55
|
-
// is single-threaded, so the old `Promise.all` over per-text calls executed
|
|
56
|
-
// serially with full per-call tokenization/tensor-alloc overhead).
|
|
42
|
+
// vectorized ONNX forward pass instead of N separate ones.
|
|
57
43
|
const output = await extractor(batch, {
|
|
58
44
|
pooling: 'cls',
|
|
59
45
|
normalize: true,
|
|
60
46
|
});
|
|
61
|
-
// CLS-pooled + L2-normalized output is [batchSize, dim]; slice the flat
|
|
62
|
-
// Float32Array into per-row vectors. Prefer the tensor's reported dims;
|
|
63
|
-
// fall back to dividing the flat length by the batch size.
|
|
64
47
|
const flat = output.data;
|
|
65
48
|
const dims = output.dims;
|
|
66
|
-
const dim = dims && dims.length >= 2
|
|
67
|
-
? dims[dims.length - 1]
|
|
68
|
-
: batch.length > 0
|
|
69
|
-
? flat.length / batch.length
|
|
70
|
-
: 0;
|
|
49
|
+
const dim = dims && dims.length >= 2 ? dims[dims.length - 1] : flat.length / batch.length;
|
|
71
50
|
for (let row = 0; row < batch.length; row++) {
|
|
72
51
|
results.push(Array.from(flat.slice(row * dim, (row + 1) * dim)));
|
|
73
52
|
}
|
package/dist/embeddings.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"embeddings.js","sourceRoot":"","sources":["../src/embeddings.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"embeddings.js","sourceRoot":"","sources":["../src/embeddings.ts"],"names":[],"mappings":"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"}
|
package/dist/openai.d.ts
CHANGED
|
@@ -1,3 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thrown when the upstream OpenAI-compatible API returns a non-2xx response.
|
|
3
|
+
* Carries the upstream HTTP status so callers can map it appropriately.
|
|
4
|
+
*/
|
|
5
|
+
export declare class OpenAIEmbeddingsHTTPError extends Error {
|
|
6
|
+
readonly status: number;
|
|
7
|
+
constructor(status: number, message: string);
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Thrown when the local AbortController fires before the upstream response,
|
|
11
|
+
* so the HTTP layer can map it to 504 Gateway Timeout.
|
|
12
|
+
*/
|
|
13
|
+
export declare class OpenAIEmbeddingsTimeoutError extends Error {
|
|
14
|
+
readonly timeoutMs: number;
|
|
15
|
+
constructor(timeoutMs: number);
|
|
16
|
+
}
|
|
1
17
|
export declare function generateOpenAIEmbeddings(config: {
|
|
2
18
|
texts: string[];
|
|
3
19
|
apiUrl: string;
|
package/dist/openai.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openai.d.ts","sourceRoot":"","sources":["../src/openai.ts"],"names":[],"mappings":"AAOA,wBAAsB,wBAAwB,CAAC,MAAM,EAAE;IACrD,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,kBAAkB,EAAE,MAAM,CAAC;IAC3B,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,
|
|
1
|
+
{"version":3,"file":"openai.d.ts","sourceRoot":"","sources":["../src/openai.ts"],"names":[],"mappings":"AAOA;;;GAGG;AACH,qBAAa,yBAA0B,SAAQ,KAAK;IAClD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;gBACZ,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;CAK5C;AAED;;;GAGG;AACH,qBAAa,4BAA6B,SAAQ,KAAK;IACrD,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;gBACf,SAAS,EAAE,MAAM;CAK9B;AAkBD,wBAAsB,wBAAwB,CAAC,MAAM,EAAE;IACrD,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,kBAAkB,EAAE,MAAM,CAAC;IAC3B,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAuFtB"}
|
package/dist/openai.js
CHANGED
|
@@ -1,9 +1,53 @@
|
|
|
1
1
|
import { normalizeEmbeddings, validateEmbeddingDimensions } from './embedding-utils.js';
|
|
2
|
+
/**
|
|
3
|
+
* Thrown when the upstream OpenAI-compatible API returns a non-2xx response.
|
|
4
|
+
* Carries the upstream HTTP status so callers can map it appropriately.
|
|
5
|
+
*/
|
|
6
|
+
export class OpenAIEmbeddingsHTTPError extends Error {
|
|
7
|
+
status;
|
|
8
|
+
constructor(status, message) {
|
|
9
|
+
super(message);
|
|
10
|
+
this.name = 'OpenAIEmbeddingsHTTPError';
|
|
11
|
+
this.status = status;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Thrown when the local AbortController fires before the upstream response,
|
|
16
|
+
* so the HTTP layer can map it to 504 Gateway Timeout.
|
|
17
|
+
*/
|
|
18
|
+
export class OpenAIEmbeddingsTimeoutError extends Error {
|
|
19
|
+
timeoutMs;
|
|
20
|
+
constructor(timeoutMs) {
|
|
21
|
+
super(`OpenAI embeddings request timed out after ${timeoutMs}ms`);
|
|
22
|
+
this.name = 'OpenAIEmbeddingsTimeoutError';
|
|
23
|
+
this.timeoutMs = timeoutMs;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Strip anything that looks like an API key / bearer token from an upstream
|
|
28
|
+
* error body before it leaves the process. Some "OpenAI-compatible" endpoints
|
|
29
|
+
* echo the Authorization header in error payloads.
|
|
30
|
+
*/
|
|
31
|
+
function sanitizeUpstreamError(text, apiKey) {
|
|
32
|
+
let cleaned = text;
|
|
33
|
+
if (apiKey) {
|
|
34
|
+
cleaned = cleaned.split(apiKey).join('[redacted]');
|
|
35
|
+
}
|
|
36
|
+
cleaned = cleaned
|
|
37
|
+
.replace(/\b(sk|sk-proj|rk|pk|api[_-]?key)[-_][A-Za-z0-9_-]{12,}/gi, '[redacted]')
|
|
38
|
+
.replace(/\bbearer\s+[A-Za-z0-9._-]+/gi, 'bearer [redacted]');
|
|
39
|
+
return cleaned.slice(0, 300);
|
|
40
|
+
}
|
|
2
41
|
export async function generateOpenAIEmbeddings(config) {
|
|
3
42
|
const controller = new AbortController();
|
|
4
|
-
|
|
43
|
+
let timedOut = false;
|
|
44
|
+
const timeout = setTimeout(() => {
|
|
45
|
+
timedOut = true;
|
|
46
|
+
controller.abort();
|
|
47
|
+
}, config.timeoutMs);
|
|
48
|
+
let response;
|
|
5
49
|
try {
|
|
6
|
-
|
|
50
|
+
response = await fetch(config.apiUrl, {
|
|
7
51
|
method: 'POST',
|
|
8
52
|
headers: {
|
|
9
53
|
'Content-Type': 'application/json',
|
|
@@ -15,25 +59,60 @@ export async function generateOpenAIEmbeddings(config) {
|
|
|
15
59
|
}),
|
|
16
60
|
signal: controller.signal,
|
|
17
61
|
});
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
62
|
+
}
|
|
63
|
+
catch (err) {
|
|
64
|
+
if (timedOut) {
|
|
65
|
+
throw new OpenAIEmbeddingsTimeoutError(config.timeoutMs);
|
|
66
|
+
}
|
|
67
|
+
throw err;
|
|
68
|
+
}
|
|
69
|
+
finally {
|
|
70
|
+
clearTimeout(timeout);
|
|
71
|
+
}
|
|
72
|
+
if (!response.ok) {
|
|
73
|
+
// A truncated/aborted upstream may make response.text() throw; still emit
|
|
74
|
+
// a typed error with the status.
|
|
75
|
+
let errorText = '';
|
|
76
|
+
try {
|
|
77
|
+
errorText = await response.text();
|
|
21
78
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
throw new Error('OpenAI embeddings response missing data array');
|
|
79
|
+
catch {
|
|
80
|
+
errorText = '<unreadable response body>';
|
|
25
81
|
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
82
|
+
throw new OpenAIEmbeddingsHTTPError(response.status, `OpenAI embeddings error (${response.status}): ${sanitizeUpstreamError(errorText, config.apiKey)}`);
|
|
83
|
+
}
|
|
84
|
+
let payload;
|
|
85
|
+
try {
|
|
86
|
+
payload = (await response.json());
|
|
87
|
+
}
|
|
88
|
+
catch (err) {
|
|
89
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
90
|
+
throw new Error(`OpenAI embeddings response was not valid JSON: ${detail}`);
|
|
91
|
+
}
|
|
92
|
+
if (!Array.isArray(payload.data)) {
|
|
93
|
+
throw new Error('OpenAI embeddings response missing data array');
|
|
94
|
+
}
|
|
95
|
+
if (payload.data.length !== config.texts.length) {
|
|
96
|
+
throw new Error(`OpenAI embeddings response returned ${payload.data.length} embeddings for ${config.texts.length} texts`);
|
|
97
|
+
}
|
|
98
|
+
// OpenAI does not guarantee response ordering — items carry an `index` field
|
|
99
|
+
// so callers can reorder. Reorder before returning to keep vectors aligned.
|
|
100
|
+
const embeddings = new Array(payload.data.length);
|
|
101
|
+
for (const item of payload.data) {
|
|
102
|
+
if (typeof item?.index !== 'number' ||
|
|
103
|
+
item.index < 0 ||
|
|
104
|
+
item.index >= embeddings.length ||
|
|
105
|
+
!Array.isArray(item.embedding)) {
|
|
106
|
+
throw new Error('OpenAI embeddings response item missing index/embedding');
|
|
29
107
|
}
|
|
30
|
-
|
|
31
|
-
|
|
108
|
+
if (embeddings[item.index] !== undefined) {
|
|
109
|
+
throw new Error(`OpenAI embeddings response has duplicate index ${item.index}`);
|
|
32
110
|
}
|
|
33
|
-
|
|
111
|
+
embeddings[item.index] = item.embedding;
|
|
34
112
|
}
|
|
35
|
-
|
|
36
|
-
|
|
113
|
+
for (const embedding of embeddings) {
|
|
114
|
+
validateEmbeddingDimensions(embedding, config.expectedDimensions, 'OpenAI embeddings response');
|
|
37
115
|
}
|
|
116
|
+
return config.normalize ? normalizeEmbeddings(embeddings) : embeddings;
|
|
38
117
|
}
|
|
39
118
|
//# sourceMappingURL=openai.js.map
|
package/dist/openai.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openai.js","sourceRoot":"","sources":["../src/openai.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,2BAA2B,EAAE,MAAM,sBAAsB,CAAC;AAOxF,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAAC,MAQ9C;IACC,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;IACzC,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,MAAM,CAAC,SAAS,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"openai.js","sourceRoot":"","sources":["../src/openai.ts"],"names":[],"mappings":"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"}
|
package/dist/server.js
CHANGED
|
@@ -1,21 +1,45 @@
|
|
|
1
|
+
import { timingSafeEqual } from 'node:crypto';
|
|
1
2
|
import { serve } from '@hono/node-server';
|
|
2
3
|
import { Hono } from 'hono';
|
|
3
4
|
import { validateEmbeddingDimensions } from './embedding-utils.js';
|
|
4
|
-
import { batchGenerateLocalEmbeddings,
|
|
5
|
-
import { generateOpenAIEmbeddings } from './openai.js';
|
|
5
|
+
import { batchGenerateLocalEmbeddings, getLocalModelName } from './embeddings.js';
|
|
6
|
+
import { OpenAIEmbeddingsTimeoutError, generateOpenAIEmbeddings, } from './openai.js';
|
|
6
7
|
const DEFAULT_PORT = 8790;
|
|
7
8
|
const DEFAULT_TIMEOUT_MS = 30000;
|
|
8
9
|
const DEFAULT_DIMENSIONS = 768;
|
|
9
10
|
const DEFAULT_BATCH_SIZE = 32;
|
|
11
|
+
// Hard DoS limits per request.
|
|
12
|
+
const MAX_TEXTS_PER_REQUEST = 256;
|
|
13
|
+
const MAX_TEXT_BYTES = 32 * 1024;
|
|
14
|
+
// Allowlist for client-supplied model identifiers — rejects whitespace, path
|
|
15
|
+
// segments, and shell metacharacters.
|
|
16
|
+
const MODEL_NAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._/:-]{0,127}$/;
|
|
10
17
|
const app = new Hono();
|
|
11
18
|
function resolveNumber(value, fallback) {
|
|
12
19
|
const parsed = Number.parseInt(value || '', 10);
|
|
13
20
|
return Number.isFinite(parsed) ? parsed : fallback;
|
|
14
21
|
}
|
|
22
|
+
function scrubSecrets(message) {
|
|
23
|
+
const apiKey = process.env.EMBEDDINGS_API_KEY;
|
|
24
|
+
const serviceToken = process.env.EMBEDDINGS_SERVICE_TOKEN;
|
|
25
|
+
let cleaned = message;
|
|
26
|
+
if (apiKey) {
|
|
27
|
+
cleaned = cleaned.split(apiKey).join('[redacted]');
|
|
28
|
+
}
|
|
29
|
+
if (serviceToken) {
|
|
30
|
+
cleaned = cleaned.split(serviceToken).join('[redacted]');
|
|
31
|
+
}
|
|
32
|
+
return cleaned
|
|
33
|
+
.replace(/\b(sk|sk-proj|rk|pk|api[_-]?key)[-_][A-Za-z0-9_-]{12,}/gi, '[redacted]')
|
|
34
|
+
.replace(/\bbearer\s+[A-Za-z0-9._-]+/gi, 'bearer [redacted]');
|
|
35
|
+
}
|
|
15
36
|
function parseTexts(payload) {
|
|
16
37
|
if (!Array.isArray(payload.texts)) {
|
|
17
38
|
return { error: 'texts must be an array of strings' };
|
|
18
39
|
}
|
|
40
|
+
if (payload.texts.length > MAX_TEXTS_PER_REQUEST) {
|
|
41
|
+
return { error: `texts cannot contain more than ${MAX_TEXTS_PER_REQUEST} entries` };
|
|
42
|
+
}
|
|
19
43
|
const texts = [];
|
|
20
44
|
for (const value of payload.texts) {
|
|
21
45
|
if (typeof value !== 'string') {
|
|
@@ -25,6 +49,9 @@ function parseTexts(payload) {
|
|
|
25
49
|
if (!trimmed) {
|
|
26
50
|
return { error: 'texts cannot contain empty strings' };
|
|
27
51
|
}
|
|
52
|
+
if (Buffer.byteLength(trimmed, 'utf8') > MAX_TEXT_BYTES) {
|
|
53
|
+
return { error: `each text must be at most ${MAX_TEXT_BYTES} bytes` };
|
|
54
|
+
}
|
|
28
55
|
texts.push(trimmed);
|
|
29
56
|
}
|
|
30
57
|
if (texts.length === 0) {
|
|
@@ -32,6 +59,16 @@ function parseTexts(payload) {
|
|
|
32
59
|
}
|
|
33
60
|
return { texts };
|
|
34
61
|
}
|
|
62
|
+
// Constant-time bearer-token compare. timingSafeEqual requires equal-length
|
|
63
|
+
// buffers; reject mismatched lengths up front (length itself isn't secret).
|
|
64
|
+
function tokensMatch(provided, expected) {
|
|
65
|
+
const a = Buffer.from(provided, 'utf8');
|
|
66
|
+
const b = Buffer.from(expected, 'utf8');
|
|
67
|
+
if (a.length !== b.length) {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
return timingSafeEqual(a, b);
|
|
71
|
+
}
|
|
35
72
|
function requireAuth(request) {
|
|
36
73
|
const token = process.env.EMBEDDINGS_SERVICE_TOKEN;
|
|
37
74
|
if (!token) {
|
|
@@ -39,14 +76,14 @@ function requireAuth(request) {
|
|
|
39
76
|
}
|
|
40
77
|
const header = request.headers.get('authorization') || '';
|
|
41
78
|
const match = header.match(/^Bearer\s+(.+)$/i);
|
|
42
|
-
if (!match || match[1]
|
|
79
|
+
if (!match || !tokensMatch(match[1], token)) {
|
|
43
80
|
return 'Unauthorized';
|
|
44
81
|
}
|
|
45
82
|
return null;
|
|
46
83
|
}
|
|
47
84
|
app.get('/health', (c) => {
|
|
48
85
|
const backend = (process.env.EMBEDDINGS_BACKEND || 'local').toLowerCase();
|
|
49
|
-
const model = backend === 'openai' ? process.env.EMBEDDINGS_MODEL :
|
|
86
|
+
const model = backend === 'openai' ? process.env.EMBEDDINGS_MODEL : getLocalModelName();
|
|
50
87
|
return c.json({ ok: true, backend, model });
|
|
51
88
|
});
|
|
52
89
|
app.post('/api/embeddings', async (c) => {
|
|
@@ -58,7 +95,7 @@ app.post('/api/embeddings', async (c) => {
|
|
|
58
95
|
try {
|
|
59
96
|
payload = (await c.req.json());
|
|
60
97
|
}
|
|
61
|
-
catch
|
|
98
|
+
catch {
|
|
62
99
|
return c.json({ error: 'Invalid JSON payload' }, 400);
|
|
63
100
|
}
|
|
64
101
|
const parsed = parseTexts(payload);
|
|
@@ -80,6 +117,10 @@ app.post('/api/embeddings', async (c) => {
|
|
|
80
117
|
if (!model) {
|
|
81
118
|
return c.json({ error: 'EMBEDDINGS_MODEL is required for openai backend' }, 500);
|
|
82
119
|
}
|
|
120
|
+
// Client-supplied model strings are validated; env-var fallback is trusted.
|
|
121
|
+
if (payload.model && !MODEL_NAME_PATTERN.test(payload.model)) {
|
|
122
|
+
return c.json({ error: 'invalid model identifier' }, 400);
|
|
123
|
+
}
|
|
83
124
|
const embeddings = await generateOpenAIEmbeddings({
|
|
84
125
|
texts: parsed.texts,
|
|
85
126
|
apiUrl,
|
|
@@ -96,22 +137,24 @@ app.post('/api/embeddings', async (c) => {
|
|
|
96
137
|
});
|
|
97
138
|
}
|
|
98
139
|
const batchSize = resolveNumber(process.env.EMBEDDINGS_BATCH_SIZE, DEFAULT_BATCH_SIZE);
|
|
99
|
-
const embeddings = parsed.texts
|
|
100
|
-
? [await generateLocalEmbedding(parsed.texts[0])]
|
|
101
|
-
: await batchGenerateLocalEmbeddings(parsed.texts, batchSize);
|
|
140
|
+
const embeddings = await batchGenerateLocalEmbeddings(parsed.texts, batchSize);
|
|
102
141
|
for (const embedding of embeddings) {
|
|
103
142
|
validateEmbeddingDimensions(embedding, expectedDimensions, 'Local embeddings response');
|
|
104
143
|
}
|
|
105
|
-
const model = getLocalModelInfo().model;
|
|
106
144
|
return c.json({
|
|
107
|
-
model,
|
|
145
|
+
model: getLocalModelName(),
|
|
108
146
|
dimensions: expectedDimensions,
|
|
109
147
|
embeddings,
|
|
110
148
|
});
|
|
111
149
|
}
|
|
112
150
|
catch (error) {
|
|
113
|
-
const
|
|
151
|
+
const rawMessage = error instanceof Error ? error.message : 'Embedding generation failed';
|
|
152
|
+
// Defense-in-depth scrub before logging/returning.
|
|
153
|
+
const message = scrubSecrets(rawMessage);
|
|
114
154
|
console.error('[EmbeddingsService] Error:', message);
|
|
155
|
+
if (error instanceof OpenAIEmbeddingsTimeoutError) {
|
|
156
|
+
return c.json({ error: message }, 504);
|
|
157
|
+
}
|
|
115
158
|
return c.json({ error: message }, 500);
|
|
116
159
|
}
|
|
117
160
|
});
|
package/dist/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC1C,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,2BAA2B,EAAE,MAAM,sBAAsB,CAAC;AACnE,OAAO,
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|