Coverage for src / documint_mcp / embeddings.py: 0%
40 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-03-24 23:27 -0400
« prev ^ index » next coverage.py v7.13.5, created at 2026-03-24 23:27 -0400
1"""Voyage AI embeddings and reranking for RAG.
3Optional enhancement layer — everything degrades gracefully to FTS-only
4if the voyageai package is not installed or the API key is not set.
5"""
6from __future__ import annotations
8import logging
9import os
11logger = logging.getLogger(__name__)
13_HAS_VOYAGE = False
14_client = None
16try:
17 import voyageai # noqa: PLC0415
19 _HAS_VOYAGE = True
20except ImportError:
21 pass
24def get_client(): # noqa: ANN201
25 """Return a cached Voyage AI client, or None if unavailable."""
26 global _client # noqa: PLW0603
27 if not _HAS_VOYAGE:
28 return None
29 if _client is None:
30 key = os.getenv("VOYAGE_API_KEY", "")
31 if not key:
32 return None
33 _client = voyageai.Client(api_key=key)
34 return _client
37def embed(
38 texts: list[str], model: str = "voyage-3-lite"
39) -> list[list[float]] | None:
40 """Embed a list of texts using Voyage AI.
42 Returns None if Voyage is unavailable or the call fails.
43 """
44 client = get_client()
45 if not client:
46 return None
47 try:
48 result = client.embed(texts, model=model)
49 return result.embeddings
50 except Exception as exc: # noqa: BLE001
51 logger.debug("Voyage embed failed: %s", exc)
52 return None
55def rerank(
56 query: str,
57 documents: list[str],
58 model: str = "rerank-2",
59 top_k: int = 3,
60) -> list[str] | None:
61 """Rerank documents for a query using Voyage AI.
63 Returns reranked document strings, or None if Voyage is unavailable.
64 """
65 client = get_client()
66 if not client:
67 return None
68 try:
69 result = client.rerank(query, documents, model=model, top_k=top_k)
70 return [documents[r.index] for r in result.results]
71 except Exception as exc: # noqa: BLE001
72 logger.debug("Voyage rerank failed: %s", exc)
73 return None