flexigo-support-bot / app /embeddings.py
victor34593993's picture
deploy flexigo support bot
187966e verified
Raw
History Blame
1.78 kB
"""Local multilingual embeddings via fastembed (ONNX, no torch).
Default model: sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2
(384-dim, multilingual incl. ES/PT/EN). If an e5 model is configured, the
"query: " / "passage: " prefixes it expects are applied automatically.
Inference runs in a worker thread; the model is loaded lazily and cached.
"""
from __future__ import annotations
import math
import anyio
from app.config import get_settings
_model = None
def _get_model():
global _model
if _model is None:
from fastembed import TextEmbedding
_model = TextEmbedding(
model_name=get_settings().embedding_model, cache_dir=".fastembed_cache"
)
return _model
def _embed_sync(texts: list[str]) -> list[list[float]]:
model = _get_model()
return [[float(x) for x in vec] for vec in model.embed(texts)]
def _apply_prefix(kind: str, texts: list[str]) -> list[str]:
# e5 family expects "query: " / "passage: " prefixes; others must not.
if "e5" in get_settings().embedding_model.lower():
return [f"{kind}: {t}" for t in texts]
return texts
async def embed_texts(texts: list[str], *, kind: str = "passage") -> list[list[float]]:
if not texts:
return []
return await anyio.to_thread.run_sync(_embed_sync, _apply_prefix(kind, texts))
async def embed_query(text: str) -> list[float]:
result = await embed_texts([text], kind="query")
return result[0]
def cosine(a: list[float], b: list[float]) -> float:
if not a or not b:
return 0.0
dot = sum(x * y for x, y in zip(a, b, strict=False))
na = math.sqrt(sum(x * x for x in a))
nb = math.sqrt(sum(y * y for y in b))
if na == 0 or nb == 0:
return 0.0
return dot / (na * nb)