import hashlib import math import os from backend.core.config import settings class HashEmbedder: """Small deterministic fallback embedder for local MVP retrieval.""" def __init__(self, dimensions: int = 384): self.dimensions = dimensions def embed(self, text: str) -> list[float]: vector = [0.0] * self.dimensions for token in text.lower().split(): digest = hashlib.sha1(token.encode("utf-8")).digest() idx = int.from_bytes(digest[:2], "big") % self.dimensions sign = 1.0 if digest[2] % 2 else -1.0 vector[idx] += sign norm = math.sqrt(sum(value * value for value in vector)) or 1.0 return [value / norm for value in vector] class HuggingFaceEmbedder: def __init__(self, model_name: str, dimensions: int = 384): self.model_name = model_name self.dimensions = dimensions if settings.hf_api_key: os.environ["HUGGINGFACEHUB_API_TOKEN"] = settings.hf_api_key try: from sentence_transformers import SentenceTransformer except ImportError as exc: raise ImportError( "sentence-transformers is required for Hugging Face embeddings. Install it or set use_local_embeddings=True." ) from exc self.model = SentenceTransformer(model_name) def embed(self, text: str) -> list[float]: embeddings = self.model.encode([text], normalize_embeddings=True) return embeddings[0].tolist() def _build_embedder() -> object: if settings.use_local_embeddings: return HashEmbedder(dimensions=settings.embedding_dimensions) try: return HuggingFaceEmbedder(settings.embedding_model, dimensions=settings.embedding_dimensions) except Exception: return HashEmbedder(dimensions=settings.embedding_dimensions) embedder = _build_embedder()