chore: CRM-NEW baseline
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
'use strict';
|
||||
|
||||
const { Pool } = require('pg');
|
||||
|
||||
const cache = new Map();
|
||||
const CACHE_MAX = 200;
|
||||
|
||||
function createEmbeddingClient(cfg, opts = {}) {
|
||||
const pool = new Pool({ connectionString: opts.pgUrl || cfg.ragPgUrl, max: 2, connectionTimeoutMillis: 5000 });
|
||||
const model = cfg.embModel || 'text-embedding-v4';
|
||||
const dim = cfg.embDim || 1024;
|
||||
const baseUrl = String(cfg.embBaseUrl || '').replace(/\/+$/, '');
|
||||
const apiKey = cfg.embApiKey;
|
||||
const translateModel = cfg.embTranslateModel || 'qwen-turbo';
|
||||
|
||||
async function embedQuery(text) {
|
||||
const key = String(text || '').slice(0, 200);
|
||||
if (cache.has(key)) return cache.get(key);
|
||||
if (!baseUrl || !apiKey) throw new Error('Embedding provider is not configured');
|
||||
const res = await fetch(`${baseUrl}/embeddings`, {
|
||||
method: 'POST',
|
||||
headers: { Authorization: `Bearer ${apiKey}`, 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ model, input: text, encoding_format: 'float', dimensions: dim }),
|
||||
signal: AbortSignal.timeout(15000),
|
||||
});
|
||||
const json = await res.json();
|
||||
if (!res.ok || !json.data || !json.data[0] || !json.data[0].embedding) {
|
||||
throw new Error('embedQuery failed: ' + JSON.stringify(json).slice(0, 200));
|
||||
}
|
||||
const vec = json.data[0].embedding;
|
||||
if (cache.size >= CACHE_MAX) cache.delete(cache.keys().next().value);
|
||||
cache.set(key, vec);
|
||||
return vec;
|
||||
}
|
||||
|
||||
function vecStr(v) {
|
||||
return '[' + v.join(',') + ']';
|
||||
}
|
||||
|
||||
async function translateQueryToEnglish(query) {
|
||||
try {
|
||||
const res = await fetch(`${baseUrl}/chat/completions`, {
|
||||
method: 'POST',
|
||||
headers: { Authorization: `Bearer ${apiKey}`, 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
model: translateModel,
|
||||
max_tokens: 80,
|
||||
messages: [
|
||||
{ role: 'system', content: 'Translate the user message into a concise English search query for a B2B jewelry wholesaler FAQ. Output ONLY the English translation, no explanation, no quotes.' },
|
||||
{ role: 'user', content: query },
|
||||
],
|
||||
}),
|
||||
signal: AbortSignal.timeout(15000),
|
||||
});
|
||||
const json = await res.json();
|
||||
const out = (((json.choices || [])[0] || {}).message || {}).content || '';
|
||||
return String(out).trim().replace(/^["']|["']$/g, '');
|
||||
} catch (err) {
|
||||
console.error(JSON.stringify({ ts: new Date().toISOString(), warn: `kb translate failed: ${err.message}` }));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return { pool, embedQuery, vecStr, translateQueryToEnglish, model, dim };
|
||||
}
|
||||
|
||||
module.exports = { createEmbeddingClient };
|
||||
Reference in New Issue
Block a user