""" Ollama-based embedding client for text vectorization Uses Ollama's embedding API instead of local sentence-transformers. This eliminates the need for PyTorch and heavy ML dependencies. """ import logging import httpx from typing import List, Optional from src.config import get_settings logger = logging.getLogger(__name__) settings = get_settings() class OllamaEmbeddingClient: """Client for generating text embeddings using Ollama""" def __init__( self, model_name: Optional[str] = None, base_url: Optional[str] = None, timeout: int = 30 ): """ Initialize Ollama embedding client Args: model_name: Embedding model name (default: nomic-embed-text) base_url: Ollama base URL (default from settings) timeout: Request timeout in seconds """ self.model_name = model_name or settings.embedding_model self.base_url = (base_url or settings.ollama_base_url).rstrip("/") self.timeout = timeout self.dimension = settings.embedding_dimension logger.info(f"Initializing OllamaEmbeddingClient with model: {self.model_name}") logger.info(f"Ollama URL: {self.base_url}") async def embed_text(self, text: str) -> List[float]: """ Generate embedding for a single text using Ollama Args: text: Input text to embed Returns: List of floats representing the embedding vector """ try: async with httpx.AsyncClient(timeout=self.timeout) as client: response = await client.post( f"{self.base_url}/api/embeddings", json={ "model": self.model_name, "prompt": text } ) response.raise_for_status() result = response.json() return result["embedding"] except Exception as e: logger.error(f"Error generating embedding via Ollama: {e}") raise async def embed_batch(self, texts: List[str]) -> List[List[float]]: """ Generate embeddings for multiple texts Args: texts: List of input texts Returns: List of embedding vectors """ embeddings = [] for text in texts: embedding = await self.embed_text(text) embeddings.append(embedding) return embeddings def get_dimension(self) -> int: """ Get embedding dimension Returns: Embedding vector dimension """ return self.dimension # Global instance _embedding_client: Optional[OllamaEmbeddingClient] = None def get_embedding_client() -> OllamaEmbeddingClient: """ Get or create global Ollama embedding client instance Returns: OllamaEmbeddingClient instance """ global _embedding_client if _embedding_client is None: _embedding_client = OllamaEmbeddingClient() return _embedding_client async def embed_text_async(text: str) -> List[float]: """ Async wrapper for embedding text Args: text: Input text Returns: Embedding vector """ client = get_embedding_client() return await client.embed_text(text) async def embed_batch_async(texts: List[str]) -> List[List[float]]: """ Async wrapper for batch embedding Args: texts: List of input texts Returns: List of embedding vectors """ client = get_embedding_client() return await client.embed_batch(texts)