File size: 3,559 Bytes
8812f42
dbb6988
 
 
dfd32d8
b249c92
dfd32d8
 
dbb6988
 
 
 
 
 
 
 
 
dfd32d8
 
 
8812f42
dbb6988
3c6d6b3
dbb6988
 
dfd32d8
dbb6988
 
 
8812f42
 
 
dfd32d8
 
 
 
8812f42
 
dfd32d8
 
 
 
 
 
 
 
 
 
 
b249c92
 
dbb6988
b249c92
 
 
 
 
 
 
 
dbb6988
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
from typing import List, Optional
import numpy as np
from loguru import logger
import httpx
from .config import get_settings
from .utils import timing_decorator_async, timing_decorator_sync, call_endpoint_with_retry
from .llm import LLMClient
from .gemini_client import GeminiClient

class EmbeddingClient:
    def __init__(self):
        """
        Khởi tạo EmbeddingClient.
        Input: None
        Output: EmbeddingClient instance.
        """
        self._client = httpx.AsyncClient()
        settings = get_settings()
        self.provider = getattr(settings, 'embedding_provider', 'default')
        self.model = getattr(settings, 'embedding_model', 'models/embedding-001')
        self.gemini_client: Optional[GeminiClient] = GeminiClient() if self.provider == 'gemini' else None

    @timing_decorator_async
    async def create_embedding(self, text: str) -> List[float]:
        """
        Tạo embedding vector từ text bằng dịch vụ embedding (ví dụ OpenAI hoặc Gemini).
        Input: text (str)
        Output: list[float] embedding vector.
        """
        if self.provider == 'gemini':
            if not self.gemini_client:
                raise RuntimeError("GeminiClient is not initialized")
            try:
                # GeminiClient.create_embedding là hàm sync, chạy trong executor
                import asyncio
                loop = asyncio.get_event_loop()
                gemini_client = self.gemini_client  # type: ignore
                embedding = await loop.run_in_executor(None, lambda: gemini_client.create_embedding(text, model=self.model))
                # Kiểm tra kiểu dữ liệu trả về
                if isinstance(embedding, list):
                    preview = f"{embedding[:10]}...{embedding[-10:]}" if len(embedding) > 20 else str(embedding)
                    logger.info(f"[DEBUG] Embedding API response: {preview}")
                    return embedding
                else:
                    logger.error(f"[DEBUG] Unknown embedding type: {type(embedding)} - value: {embedding}")
                    raise RuntimeError(f"Embedding returned unexpected type: {type(embedding)}")
            except Exception as e:
                logger.error(f"Error creating embedding with Gemini: {e}")
                raise
        url = "https://vietcat-vietnameseembeddingv2.hf.space/embed"
        payload = {"text": text}
        try:
            response = await call_endpoint_with_retry(self._client, url, payload)
            if response is not None:
                data = response.json()
                logger.info(f"[DEBUG] Embedding API response: {data['embedding'][:10]}...{data['embedding'][-10:]}")
                return data["embedding"]
            else:
                logger.error("Embedding API response is None")
                raise RuntimeError("Embedding API response is None")
        except Exception as e:
            logger.error(f"Error creating embedding: {e}")
            raise

    def cosine_similarity(self, embedding1: List[float], embedding2: List[float]) -> float:
        """
        Tính cosine similarity giữa hai embedding.
        Input: embedding1 (list[float]), embedding2 (list[float])
        Output: float (giá trị similarity)
        """
        try:
            a = np.array(embedding1)
            b = np.array(embedding2)
            return float(np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b)))
        except Exception as e:
            logger.error(f"Error calculating similarity: {e}")
            return 0.0