Download services/embedding.py from Punit1/Enterprise-AI-Copilot: direct link, hf CLI and curl.
- Browser
- Download file 4.16 kB
-
https://huggingface.co/spaces/Punit1/Enterprise-AI-Copilot/resolve/main/services/embedding.py
- Command line
-
hf download hf://spaces/Punit1/Enterprise-AI-Copilot/services/embedding.py
-
curl -L -o embedding.py https://huggingface.co/spaces/Punit1/Enterprise-AI-Copilot/resolve/main/services/embedding.py
4.16 kB
| """ | |
| Embedding Service | |
| ================= | |
| Generates dense vector embeddings using sentence-transformers (100% free, runs locally). | |
| Model: all-MiniLM-L6-v2 (384-dim, fast, great quality for enterprise text) | |
| Singleton pattern: model loads once on first call and stays in memory. | |
| Thread-safe for concurrent FastAPI requests. | |
| """ | |
| from __future__ import annotations | |
| import asyncio | |
| import threading | |
| from typing import List, Optional | |
| import numpy as np | |
| import structlog | |
| logger = structlog.get_logger(__name__) | |
| class EmbeddingService: | |
| """ | |
| Wraps sentence-transformers SentenceTransformer for async-compatible embedding. | |
| Falls back gracefully if the model is not yet downloaded (first run). | |
| """ | |
| def __init__(self, model_name: str = "all-MiniLM-L6-v2"): | |
| self.model_name = model_name | |
| self._model = None | |
| self._lock = threading.Lock() | |
| self._dim: Optional[int] = None | |
| def _load_model(self): | |
| """Lazy-load the embedding model (downloads on first use).""" | |
| if self._model is None: | |
| with self._lock: | |
| if self._model is None: | |
| logger.info( | |
| "Loading embedding model", | |
| model=self.model_name, | |
| note="Downloading on first run, ~90MB", | |
| ) | |
| from sentence_transformers import SentenceTransformer | |
| self._model = SentenceTransformer(self.model_name) | |
| # Warm up with a test sentence | |
| test = self._model.encode(["test"], show_progress_bar=False) | |
| self._dim = test.shape[1] | |
| logger.info( | |
| "Embedding model ready", | |
| model=self.model_name, | |
| dim=self._dim, | |
| ) | |
| def dim(self) -> int: | |
| """Return embedding dimensions (loaded lazily).""" | |
| if self._dim is None: | |
| self._load_model() | |
| return self._dim # type: ignore | |
| def encode_sync( | |
| self, | |
| texts: List[str], | |
| batch_size: int = 64, | |
| normalize: bool = True, | |
| ) -> np.ndarray: | |
| """ | |
| Synchronous encoding β for use in background tasks. | |
| Args: | |
| texts: List of strings to embed | |
| batch_size: Batch size for encoding (larger = faster on GPU) | |
| normalize: L2-normalize embeddings (recommended for cosine similarity) | |
| Returns: | |
| numpy array of shape (len(texts), dim) | |
| """ | |
| self._load_model() | |
| embeddings = self._model.encode( # type: ignore | |
| texts, | |
| batch_size=batch_size, | |
| normalize_embeddings=normalize, | |
| show_progress_bar=False, | |
| ) | |
| return embeddings | |
| async def encode( | |
| self, | |
| texts: List[str], | |
| batch_size: int = 64, | |
| normalize: bool = True, | |
| ) -> np.ndarray: | |
| """ | |
| Async encoding β runs encoding in a thread pool to avoid blocking the event loop. | |
| Args: | |
| texts: List of strings to embed | |
| batch_size: Batch size for encoding | |
| normalize: L2-normalize embeddings | |
| Returns: | |
| numpy array of shape (len(texts), dim) | |
| """ | |
| loop = asyncio.get_event_loop() | |
| return await loop.run_in_executor( | |
| None, lambda: self.encode_sync(texts, batch_size, normalize) | |
| ) | |
| async def encode_query(self, query: str) -> np.ndarray: | |
| """Encode a single query string and return 1D array.""" | |
| result = await self.encode([query]) | |
| return result[0] | |
| async def encode_documents(self, texts: List[str]) -> np.ndarray: | |
| """ | |
| Encode a list of document chunks. Adds a prefix per MTEB best practice. | |
| """ | |
| prefixed = [f"passage: {t}" for t in texts] | |
| return await self.encode(prefixed) | |
| # ββ Singleton instance βββββββββββββββββββββββββββββββββββββββββ | |
| embedding_service = EmbeddingService(model_name="all-MiniLM-L6-v2") | |