""" Sentence Transformer embeddings for semantic similarity. Uses MPS (Apple Silicon GPU) when available. """ import numpy as np _embedding_model = None def get_embedding_model(device: str = None): """Load Sentence Transformer model (cached singleton).""" global _embedding_model if _embedding_model is not None: return _embedding_model try: from sentence_transformers import SentenceTransformer import torch if device is None: device = "mps" if torch.backends.mps.is_available() else "cpu" _embedding_model = SentenceTransformer("all-MiniLM-L6-v2", device=device) return _embedding_model except ImportError: return None def embedding_cosine_similarity(q1: str, q2: str, model=None) -> float: """ Compute cosine similarity between question embeddings. Returns 0.0 if model unavailable. """ if model is None: model = get_embedding_model() if model is None: return 0.0 embeddings = model.encode([q1, q2]) a, b = embeddings[0], embeddings[1] return float(np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b) + 1e-9))