from __future__ import annotations import asyncio from functools import lru_cache from typing import List from sentence_transformers import SentenceTransformer from loguru import logger from app.core.config import get_settings settings = get_settings() class EmbeddingService: _model: SentenceTransformer | None = None def _load(self) -> SentenceTransformer: if self._model is None: logger.info(f"Loading embedding model: {settings.embedding_model}") self._model = SentenceTransformer(settings.embedding_model) return self._model async def embed_texts(self, texts: List[str]) -> List[List[float]]: loop = asyncio.get_running_loop() model = self._load() return await loop.run_in_executor( None, lambda: model.encode(texts, normalize_embeddings=True, show_progress_bar=False).tolist() ) async def embed_query(self, query: str) -> List[float]: vecs = await self.embed_texts([query]) return vecs[0] @lru_cache(maxsize=1) def get_embedding_service() -> EmbeddingService: return EmbeddingService()