import { config } from './config.js'; const sleep = (ms) => new Promise(r => setTimeout(r, ms)); export async function embed(texts, kind = 'passage') { const inputs = (Array.isArray(texts) ? texts : [texts]).map(t => `${kind}: ${t}`); const url = `${config.hfApiUrl}/hf-inference/models/${config.embedModel}/pipeline/feature-extraction`; // Retry: o endpoint hf-inference às vezes dá 503/504 (modelo frio) — tenta de novo. let lastErr = ''; for (let attempt = 1; attempt <= 4; attempt++) { try { const res = await fetch(url, { method: 'POST', headers: { Authorization: `Bearer ${config.hfToken}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ inputs, options: { wait_for_model: true } }), }); if (res.ok) return res.json(); lastErr = `HF embed ${res.status}`; } catch (e) { lastErr = `HF embed: ${e.message}`; } if (attempt < 4) await sleep(attempt * 1500); // backoff: 1.5s, 3s, 4.5s } throw new Error(`${lastErr} (após 4 tentativas)`); } export function cosine(a, b) { let dot = 0, na = 0, nb = 0; for (let i = 0; i < a.length; i++) { dot += a[i]*b[i]; na += a[i]*a[i]; nb += b[i]*b[i]; } return dot / (Math.sqrt(na) * Math.sqrt(nb) + 1e-8); }