ai-engineering-project / src /embedding /hf_embedding_service.py
GitHub Action
Clean deployment without binary files
f884e6e
"""
HuggingFace Embedding Service using Inference API
"""
import logging
import os
from typing import List
import requests
logger = logging.getLogger(__name__)
class HFEmbeddingService:
"""HuggingFace Embedding Service using Inference API"""
def __init__(self, model_name: str = "intfloat/multilingual-e5-large"):
self.model_name = model_name
self.api_url = f"https://router.huggingface.co/hf-inference/models/{model_name}"
self.hf_token = os.getenv("HF_TOKEN")
if not self.hf_token:
logger.warning("⚠️ HF_TOKEN not found - service will return empty embeddings")
self.headers = {}
else:
self.headers = {
"Authorization": f"Bearer {self.hf_token}",
"Content-Type": "application/json",
}
logger.info(f"βœ… HF Embedding Service initialized: {model_name}")
def get_embeddings(self, texts: List[str]) -> List[List[float]]:
"""Get embeddings for texts"""
if not texts:
return []
# Return empty embeddings if no token (for testing)
if not self.hf_token:
logger.warning("⚠️ No HF_TOKEN - returning empty embeddings")
return [[0.0] * 1024 for _ in texts]
try:
payload = {"inputs": texts}
response = requests.post(self.api_url, headers=self.headers, json=payload, timeout=30)
if response.status_code == 200:
try:
embeddings = response.json()
# Validate that embeddings is a list of lists
if isinstance(embeddings, list) and all(isinstance(emb, list) for emb in embeddings):
logger.debug(f"βœ… Generated {len(embeddings)} embeddings")
return embeddings
else:
logger.error(f"❌ Invalid embedding format: {type(embeddings)}")
return [[0.0] * 1024 for _ in texts]
except (ValueError, TypeError) as e:
logger.error(f"❌ JSON decode error: {e}")
return [[0.0] * 1024 for _ in texts]
else:
logger.error(f"❌ HF API error: {response.status_code}")
# Return empty embeddings on error
return [[0.0] * 1024 for _ in texts]
except Exception as e:
logger.error(f"❌ Embedding error: {e}")
# Return empty embeddings on error
return [[0.0] * 1024 for _ in texts]
def get_embedding(self, text: str) -> List[float]:
"""Get embedding for single text"""
embeddings = self.get_embeddings([text])
return embeddings[0] if embeddings else [0.0] * 1024
def embed_text(self, text: str) -> List[float]:
"""Get embedding for single text (SearchService compatibility)"""
return self.get_embedding(text)
def get_embedding_dimension(self) -> int:
"""Get embedding dimension"""
return 1024 # Known for multilingual-e5-large
def health_check(self) -> bool:
"""Health check"""
try:
if not self.hf_token:
return False
test_embedding = self.get_embedding("test")
return len(test_embedding) == 1024
except Exception:
return False