from __future__ import annotations import hashlib import math class EmbeddingService: """Deterministic local embeddings so Chroma can persist vectors without a remote embedding API.""" def __init__(self, dimensions: int = 256) -> None: self.dimensions = dimensions def embed_documents(self, texts: list[str]) -> list[list[float]]: return [self.embed_query(text) for text in texts] def embed_query(self, text: str) -> list[float]: vector = [0.0] * self.dimensions tokens = self._tokenize(text) if not tokens: return vector for token in tokens: digest = hashlib.sha256(token.encode("utf-8")).digest() bucket = int.from_bytes(digest[:4], "big") % self.dimensions sign = 1.0 if digest[4] % 2 == 0 else -1.0 vector[bucket] += sign norm = math.sqrt(sum(value * value for value in vector)) or 1.0 return [value / norm for value in vector] @staticmethod def _tokenize(text: str) -> list[str]: return [token.strip().lower() for token in text.replace("\n", " ").split() if token.strip()]