2026-02-27 19:37:02 +03:30
|
|
|
"""
|
2026-03-19 22:54:29 +03:30
|
|
|
سرویس تعبیهسازی متن — از Adapter Pattern برای سوئیچ بین providers استفاده میکند
|
2026-02-27 19:37:02 +03:30
|
|
|
"""
|
2026-05-05 21:02:12 +03:30
|
|
|
import logging
|
|
|
|
|
import time
|
|
|
|
|
|
2026-03-19 22:54:29 +03:30
|
|
|
from .api_provider import get_embedding_client
|
|
|
|
|
from .config import RAGConfig, load_rag_config
|
2026-05-05 21:02:12 +03:30
|
|
|
from .observability import classify_exception, log_event, observe_operation, record_metric
|
2026-02-27 19:37:02 +03:30
|
|
|
|
2026-04-24 01:23:56 +03:30
|
|
|
logger = logging.getLogger(__name__)
|
2026-02-27 19:37:02 +03:30
|
|
|
|
|
|
|
|
def embed_texts(
|
|
|
|
|
texts: list[str],
|
|
|
|
|
config: RAGConfig | None = None,
|
|
|
|
|
model: str | None = None,
|
|
|
|
|
dimensions: int | None = None,
|
|
|
|
|
) -> list[list[float]]:
|
|
|
|
|
"""
|
|
|
|
|
تعبیهسازی لیست متنها با Avalai.
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
texts: لیست رشتههای ورودی
|
|
|
|
|
config: تنظیمات RAG (پیشفرض: load_rag_config)
|
|
|
|
|
model: نام مدل (override از config)
|
|
|
|
|
dimensions: تعداد ابعاد (فقط برای مدلهای پشتیبانیکننده)
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
لیست وکتورها
|
|
|
|
|
"""
|
|
|
|
|
if not texts:
|
2026-05-05 21:02:12 +03:30
|
|
|
record_metric("rag.embedding.empty_input", operation="embed_texts")
|
2026-02-27 19:37:02 +03:30
|
|
|
return []
|
|
|
|
|
|
|
|
|
|
cfg = config or load_rag_config()
|
2026-03-19 22:54:29 +03:30
|
|
|
client = get_embedding_client(cfg)
|
2026-02-27 19:37:02 +03:30
|
|
|
model_name = model or cfg.embedding.model
|
2026-05-05 21:02:12 +03:30
|
|
|
provider = cfg.embedding.provider or "unknown"
|
2026-02-27 19:37:02 +03:30
|
|
|
batch_size = cfg.embedding.batch_size
|
|
|
|
|
|
|
|
|
|
all_embeddings: list[list[float]] = []
|
|
|
|
|
extra = {}
|
|
|
|
|
if dimensions is not None:
|
|
|
|
|
extra["dimensions"] = dimensions
|
|
|
|
|
|
2026-05-05 21:02:12 +03:30
|
|
|
with observe_operation(source="rag.embedding", provider=provider, operation="embed_texts"):
|
|
|
|
|
for i in range(0, len(texts), batch_size):
|
|
|
|
|
batch = texts[i : i + batch_size]
|
|
|
|
|
started_at = time.monotonic()
|
|
|
|
|
try:
|
|
|
|
|
resp = client.embeddings.create(
|
|
|
|
|
model=model_name,
|
|
|
|
|
input=batch,
|
|
|
|
|
**extra,
|
|
|
|
|
)
|
|
|
|
|
except Exception as exc:
|
|
|
|
|
failure = classify_exception(exc)
|
|
|
|
|
log_event(
|
|
|
|
|
level=logging.ERROR,
|
|
|
|
|
message="embedding batch request failed",
|
|
|
|
|
source="rag.embedding",
|
|
|
|
|
provider=provider,
|
|
|
|
|
operation="embed_batch",
|
|
|
|
|
result_status="error",
|
|
|
|
|
duration_ms=(time.monotonic() - started_at) * 1000,
|
|
|
|
|
error_code=failure.error_code,
|
|
|
|
|
batch_size=len(batch),
|
|
|
|
|
model=model_name,
|
|
|
|
|
)
|
|
|
|
|
raise
|
|
|
|
|
for item in sorted(resp.data, key=lambda x: x.index):
|
|
|
|
|
all_embeddings.append(item.embedding)
|
|
|
|
|
log_event(
|
|
|
|
|
level=logging.INFO,
|
|
|
|
|
message="embedding batch request completed",
|
|
|
|
|
source="rag.embedding",
|
|
|
|
|
provider=provider,
|
|
|
|
|
operation="embed_batch",
|
|
|
|
|
result_status="success",
|
|
|
|
|
duration_ms=(time.monotonic() - started_at) * 1000,
|
|
|
|
|
batch_size=len(batch),
|
|
|
|
|
model=model_name,
|
|
|
|
|
)
|
2026-02-27 19:37:02 +03:30
|
|
|
|
|
|
|
|
return all_embeddings
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def embed_single(text: str, config: RAGConfig | None = None, **kwargs) -> list[float]:
|
|
|
|
|
"""تعبیهسازی یک متن. خروجی مستقیماً یک وکتور است."""
|
|
|
|
|
vecs = embed_texts([text], config=config, **kwargs)
|
|
|
|
|
return vecs[0] if vecs else []
|