DeepBoner / src /clients /registry.py
VibecoderMcSwaggins's picture
refactor(orchestrator): Introduce WorkflowState dataclass (Priority 7) (#129)
e85ccf5 unverified
raw
history blame
2.56 kB
"""
Client Provider Registry for unified provider selection.
Implements the Strategy Pattern to decouple client creation from the factory.
"""
from typing import Any, ClassVar, Protocol
import structlog
from agent_framework import BaseChatClient
from src.utils.config import Settings
from src.utils.exceptions import ConfigurationError
logger = structlog.get_logger()
class ClientProvider(Protocol):
"""Protocol for LLM client providers."""
@property
def name(self) -> str:
"""Provider name (e.g., 'openai', 'huggingface')."""
...
def can_handle(
self, provider_name: str | None, api_key: str | None, settings: Settings
) -> bool:
"""Determine if this provider should handle the request."""
...
def create(
self,
settings: Settings,
api_key: str | None = None,
model_id: str | None = None,
**kwargs: Any,
) -> BaseChatClient:
"""Create the client instance."""
...
class ProviderRegistry:
"""Registry for managing available LLM providers."""
_providers: ClassVar[list[ClientProvider]] = []
@classmethod
def register(cls, provider: ClientProvider) -> None:
"""Register a new provider strategy."""
cls._providers.append(provider)
@classmethod
def clear(cls) -> None:
"""Clear all registered providers (useful for testing)."""
cls._providers.clear()
@classmethod
def get_client(
cls,
settings: Settings,
provider: str | None = None,
api_key: str | None = None,
model_id: str | None = None,
**kwargs: Any,
) -> BaseChatClient:
"""
Find and execute the appropriate provider strategy.
Args:
settings: Application settings
provider: Explicit provider name
api_key: Optional API key
model_id: Optional model ID
**kwargs: Additional arguments for the client
Returns:
Configured BaseChatClient
Raises:
ValueError: If no provider can handle the request
"""
# Normalize provider name
normalized_provider = provider.lower() if provider else None
for p in cls._providers:
if p.can_handle(normalized_provider, api_key, settings):
logger.info(f"Using {p.name} Chat Client")
return p.create(settings, api_key, model_id, **kwargs)
raise ConfigurationError(f"No suitable provider found for provider={provider}")