| 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() |
|
|