dharandhamo's picture
fix: update to active nvidia embedding model, increase api timeouts, and add qdrant cloud indexing support
a3264f4
Raw
History Blame Contribute Delete
2.98 kB
"""Nvidia NIM Embedding API wrapper with batching and retry logic."""
from __future__ import annotations
import asyncio
import httpx
from loguru import logger
from tenacity import retry, stop_after_attempt, wait_exponential
from app.config import settings
from app.utils.helpers import batch_list
# Batch size for embedding requests
_EMBED_BATCH_SIZE = 20
@retry(
stop=stop_after_attempt(3),
wait=wait_exponential(multiplier=1, min=2, max=10),
reraise=True,
)
async def _embed_content_sync(
texts: list[str],
input_type: str,
) -> list[list[float]]:
"""Embed a batch of texts via Nvidia API using httpx.
Args:
texts: List of text strings to embed.
input_type: Either 'passage' or 'query'.
Returns:
List of embedding vectors (each a list of floats).
"""
url = "https://integrate.api.nvidia.com/v1/embeddings"
headers = {
"Authorization": f"Bearer {settings.nvidia_api_key}",
"Content-Type": "application/json",
}
payload = {
"input": texts,
"model": settings.embedding_model,
"input_type": input_type,
"encoding_format": "float",
"truncate": "NONE",
}
async with httpx.AsyncClient(timeout=httpx.Timeout(120.0, connect=30.0)) as client:
response = await client.post(url, headers=headers, json=payload)
response.raise_for_status()
data = response.json()
return [item["embedding"] for item in data["data"]]
async def embed_chunks(texts: list[str]) -> list[list[float]]:
"""Embed document chunks for indexing using Nvidia API.
Batches texts into groups of 20 and embeds them with
input_type='passage'. Includes exponential backoff retry.
Args:
texts: List of text chunks to embed.
Returns:
List of embedding vectors with dimension 2048.
"""
logger.info("Embedding {} chunks via Nvidia API", len(texts))
all_embeddings: list[list[float]] = []
batches = batch_list(texts, _EMBED_BATCH_SIZE)
for i, batch in enumerate(batches):
logger.debug("Embedding batch {}/{} ({} texts)", i + 1, len(batches), len(batch))
embeddings = await _embed_content_sync(
batch,
"passage",
)
all_embeddings.extend(embeddings)
logger.info(
"Embedding complete: {} vectors of dimension {}",
len(all_embeddings),
len(all_embeddings[0]) if all_embeddings else 0,
)
return all_embeddings
async def embed_query(query: str) -> list[float]:
"""Embed a single query string for retrieval using Nvidia API.
Uses input_type='query' for asymmetric search.
Args:
query: The user query text to embed.
Returns:
A single embedding vector with dimension 2048.
"""
logger.debug("Embedding query via Nvidia API: {}...", query[:80])
embeddings = await _embed_content_sync(
[query],
"query",
)
return embeddings[0]