Repository navigation
Expand file tree
/
Copy pathembedder.js
More file actions
57 lines (49 loc) · 2.07 KB
/
Copy pathembedder.js
File metadata and controls
57 lines (49 loc) · 2.07 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
'use strict';
/**
* Lazy-loaded CPU embedding generator.
*
* EMBED_PROVIDER=bge (default): @xenova/transformers, mean pool — GHA/cloud.
* EMBED_PROVIDER=qwen: @huggingface/transformers, last_token — VPS local ONNX
* (matches stack/backfill-qwen; documents: no Instruct prefix).
*/
const log = require('./log');
const cfg = require('./config');
let extractorPromise = null;
async function getExtractor() {
if (!extractorPromise) {
extractorPromise = (async () => {
const provider = cfg.EMBED_PROVIDER;
log.info(`[MODEL] Loading embedding model (${provider}): ${cfg.EMBED_MODEL}`);
if (provider === 'qwen') {
const { env, pipeline } = await import('@huggingface/transformers');
env.cacheDir = cfg.EMBED_CACHE_DIR;
const extractor = await pipeline('feature-extraction', cfg.EMBED_MODEL, {
dtype: cfg.EMBED_DTYPE,
});
log.info('[MODEL] Qwen model ready (last_token pooling)');
return { provider: 'qwen', extractor };
}
const { env, pipeline } = await import('@xenova/transformers');
env.cacheDir = cfg.EMBED_CACHE_DIR;
const extractor = await pipeline('feature-extraction', cfg.EMBED_MODEL);
log.info('[MODEL] BGE model ready (mean pooling)');
return { provider: 'bge', extractor };
})();
}
return extractorPromise;
}
/** Embed a single document text -> number[] (normalized). */
async function embed(text) {
const { provider, extractor } = await getExtractor();
if (provider === 'qwen') {
const output = await extractor(text, { pooling: 'last_token', normalize: true });
const data = Array.from(output.data);
if (data.length !== cfg.VECTOR_DIM) {
throw new Error(`Qwen embed dim ${data.length}, expected ${cfg.VECTOR_DIM}`);
}
return data;
}
const output = await extractor(text, { pooling: 'mean', normalize: true });
return Array.from(output.data);
}
module.exports = { embed };