Spaces:
Runtime error
Runtime error
File size: 3,577 Bytes
49f0cfb 7d37f11 0b42403 49f0cfb 0b42403 7d37f11 dc1b199 49f0cfb dc1b199 49f0cfb 7d37f11 dc1b199 0b42403 49f0cfb 7d37f11 49f0cfb 7d37f11 0b42403 dc1b199 0b42403 49f0cfb dc1b199 0b42403 dc1b199 49f0cfb dc1b199 7d37f11 732b14f 0b42403 dc1b199 0b42403 49f0cfb 0b42403 dc1b199 7d37f11 0b42403 | 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 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 | """Factory that returns a LangChain-compatible Embeddings object.
This replaces the custom EmbeddingClient interface with the standard
LangChain ``langchain_core.embeddings.Embeddings`` protocol, enabling
seamless integration with LangChain vector stores, retrievers, and chains.
Default priority (with ``prefer_local_embeddings=True``):
1. HuggingFace — ``langchain_huggingface.HuggingFaceEmbeddings`` (free, on-device).
2. OpenAI fallback only if HuggingFace import fails AND an API key is present.
3. Fake — deterministic ``FakeEmbeddings`` for unit tests only.
Setting ``prefer_local_embeddings=False`` restores the legacy behaviour
(OpenAI first if a key is configured, HuggingFace as fallback).
"""
import logging
from langchain_core.embeddings import Embeddings
from app.config import settings
logger = logging.getLogger(__name__)
_instance: Embeddings | None = None
def _build_huggingface() -> Embeddings | None:
"""Try to construct a HuggingFace embeddings client, returning ``None`` on failure."""
try:
from langchain_huggingface import HuggingFaceEmbeddings
except ImportError:
logger.warning(
"langchain-huggingface not installed — cannot use local embeddings. "
"Run: pip install langchain-huggingface sentence-transformers"
)
return None
logger.info(
"Using LangChain HuggingFaceEmbeddings (model=%s, free/local)",
settings.local_embedding_model,
)
return HuggingFaceEmbeddings(
model_name=settings.local_embedding_model,
model_kwargs={"device": "cpu"},
encode_kwargs={"normalize_embeddings": True},
)
def _build_openai() -> Embeddings | None:
"""Try to construct an OpenAI embeddings client, returning ``None`` on failure."""
if not settings.openai_api_key:
return None
try:
from langchain_openai import OpenAIEmbeddings
except ImportError:
return None
logger.info("Using LangChain OpenAIEmbeddings (model=%s)", settings.embedding_model)
return OpenAIEmbeddings(
model=settings.embedding_model,
openai_api_key=settings.openai_api_key,
)
def get_embedding_client() -> Embeddings:
"""Return a singleton LangChain :class:`Embeddings` instance.
The client is constructed once and reused — critical for the
HuggingFace backend which loads a ~90 MB model at construction time.
Default order (``prefer_local_embeddings=True``):
1. :class:`langchain_huggingface.HuggingFaceEmbeddings` (free, local).
2. :class:`langchain_openai.OpenAIEmbeddings` (only if HF unavailable).
3. :class:`langchain_community.embeddings.FakeEmbeddings` as a test fallback.
Set ``prefer_local_embeddings=False`` to invert the first two.
Returns:
A configured LangChain :class:`Embeddings` object.
"""
global _instance
if _instance is not None:
return _instance
if getattr(settings, "prefer_local_embeddings", False):
_instance = _build_huggingface() or _build_openai()
else:
_instance = _build_openai() or _build_huggingface()
if _instance is None:
logger.warning(
"No embedding backend available — falling back to FakeEmbeddings (TEST ONLY)."
)
from langchain_community.embeddings import FakeEmbeddings
_instance = FakeEmbeddings(size=384)
return _instance
def reset_embedding_client() -> None:
"""Reset the cached singleton — useful for tests or hot config reloads."""
global _instance
_instance = None
|