Coverage for src/local_deep_research/embeddings/providers/base.py: 92%
36 statements
« prev ^ index » next coverage.py v7.15.1, created at 2026-07-19 23:35 +0000
« prev ^ index » next coverage.py v7.15.1, created at 2026-07-19 23:35 +0000
1"""Base class for embedding providers."""
3from abc import ABC, abstractmethod
4from typing import Any, Dict, List, Optional
6from langchain_core.embeddings import Embeddings
8from ...security.egress.classification import Exposure
11class BaseEmbeddingProvider(ABC):
12 """
13 Abstract base class for embedding providers.
15 All embedding providers should inherit from this class and implement
16 the required methods. This provides a consistent interface similar to
17 the LLM provider system.
18 """
20 # Override these in subclasses
21 provider_name = "base" # Display name for logs/UI
22 provider_key = "BASE" # Unique identifier (uppercase)
23 requires_api_key = False # Whether this provider requires an API key
24 supports_local = False # Whether this runs locally
25 default_model = None # Default embedding model
26 # Egress exposure (ADR-0007): declares whether this is an exposing
27 # inference sink (cloud = exposing, local = contained). Declarative default
28 # only — the egress resolver classifies the provider's ACTUAL endpoint at
29 # run time (run_classification.embeddings_label via evaluate_embeddings), so
30 # a URL-configurable provider is refined then; this documents intent.
31 egress_exposure = Exposure.EXPOSING
33 @classmethod
34 @abstractmethod
35 def create_embeddings(
36 cls,
37 model: Optional[str] = None,
38 settings_snapshot: Optional[Dict[str, Any]] = None,
39 **kwargs,
40 ) -> Embeddings:
41 """
42 Create an embeddings instance for this provider.
44 Args:
45 model: Name of the embedding model to use
46 settings_snapshot: Optional settings snapshot for thread-safe access
47 **kwargs: Additional provider-specific parameters
49 Returns:
50 A LangChain Embeddings instance
52 Raises:
53 ValueError: If required configuration is missing
54 ImportError: If required dependencies are not installed
55 """
56 pass
58 @classmethod
59 @abstractmethod
60 def is_available(
61 cls, settings_snapshot: Optional[Dict[str, Any]] = None
62 ) -> bool:
63 """
64 Check if this embedding provider is available and properly configured.
66 Args:
67 settings_snapshot: Optional settings snapshot for thread-safe access
69 Returns:
70 True if the provider can be used, False otherwise
71 """
72 pass
74 @classmethod
75 def get_available_models(
76 cls, settings_snapshot: Optional[Dict[str, Any]] = None
77 ) -> List[Dict[str, Any]]:
78 """
79 Get list of available embedding models for this provider.
81 Implementations should return every model the backend reports.
82 Filtering by name is unreliable — users may load custom or
83 renamed embedding models — so leave the choice to the user and
84 only tag entries when a real capability signal is available.
86 Args:
87 settings_snapshot: Optional settings snapshot
89 Returns:
90 List of dicts with ``value`` and ``label`` string keys for
91 each model. May include an optional ``is_embedding`` (bool)
92 key when the provider can detect embedding capability from
93 the backend (e.g. Ollama's ``/api/show`` capabilities).
94 """
95 return []
97 @classmethod
98 def is_embedding_model(
99 cls,
100 model: str,
101 settings_snapshot: Optional[Dict[str, Any]] = None,
102 ) -> Optional[bool]:
103 """
104 Check whether a specific model supports embeddings.
106 Providers that can distinguish embedding models from chat/LLM models
107 should override this method.
109 Args:
110 model: Model identifier
111 settings_snapshot: Optional settings snapshot
113 Returns:
114 True if the model supports embeddings, False if it does not,
115 None if the provider cannot determine this.
116 """
117 return None
119 @classmethod
120 def get_model_info(cls, model: str) -> Optional[Dict[str, Any]]:
121 """
122 Get information about a specific model.
124 Args:
125 model: Model identifier
127 Returns:
128 Dict with model metadata (dimensions, description, etc.) or None
129 """
130 return None
132 @classmethod
133 def validate_config(
134 cls, settings_snapshot: Optional[Dict[str, Any]] = None
135 ) -> tuple[bool, Optional[str]]:
136 """
137 Validate the provider configuration.
139 Args:
140 settings_snapshot: Optional settings snapshot
142 Returns:
143 Tuple of (is_valid, error_message)
144 """
145 if not cls.is_available(settings_snapshot):
146 return (
147 False,
148 f"{cls.provider_name} is not available or not configured",
149 )
150 return True, None
152 @classmethod
153 def get_provider_info(cls) -> Dict[str, Any]:
154 """
155 Get metadata about this provider.
157 Returns:
158 Dict with provider information
159 """
160 return {
161 "name": cls.provider_name,
162 "key": cls.provider_key,
163 "requires_api_key": cls.requires_api_key,
164 "supports_local": cls.supports_local,
165 "default_model": cls.default_model,
166 }