# app/services/provider_factory.py from typing import Dict, List from lib.embedding_providers.base import BaseProvider from lib.embedding_providers.openai_provider import OpenAIProvider from lib.embedding_providers.openrouter_provider import OpenRouterProvider from lib.embedding_providers.claude_provider import ClaudeProvider from config import Settings class ProviderFactory: """ Factory class for creating and managing embedding/completion providers. """ SUPPORTED_PROVIDERS = ["openai", "openrouter", "claude"] @staticmethod def create_provider( provider_name: str, settings: Settings, embedding_model: str = None, completion_model: str = None ) -> BaseProvider: """ Create a provider instance based on the provider name. Args: provider_name: Name of the provider ('openai', 'openrouter', 'claude') settings: Application settings with API keys embedding_model: Optional specific embedding model to use completion_model: Optional specific completion model to use Returns: Configured provider instance Raises: ValueError: If provider is not supported or API key is missing """ provider_name = provider_name.lower() if provider_name not in ProviderFactory.SUPPORTED_PROVIDERS: raise ValueError( f"Unsupported provider: {provider_name}. " f"Supported providers: {ProviderFactory.SUPPORTED_PROVIDERS}" ) # OpenAI Provider if provider_name == "openai": if not settings.openai_api_key: raise ValueError("OpenAI API key not configured") return OpenAIProvider( api_key=settings.openai_api_key, embedding_model=embedding_model or settings.default_embedding_model, completion_model=completion_model or "gpt-4o-mini" ) # OpenRouter Provider elif provider_name == "openrouter": if not settings.openrouter_api_key: raise ValueError("OpenRouter API key not configured") return OpenRouterProvider( api_key=settings.openrouter_api_key, completion_model=completion_model or "anthropic/claude-3-5-sonnet", embedding_model=embedding_model or "openai/text-embedding-3-small", embedding_dimension=1536 ) # Claude Provider elif provider_name == "claude": if not settings.anthropic_api_key: raise ValueError("Claude (Anthropic) API key not configured") return ClaudeProvider( claude_api_key=settings.anthropic_api_key, openai_api_key=settings.openai_api_key, # For embeddings fallback completion_model=completion_model or settings.default_completion_model, embedding_model=embedding_model or settings.default_embedding_model ) @staticmethod def get_provider_status(settings: Settings) -> Dict[str, Dict]: """ Check the availability status of all providers. Args: settings: Application settings with API keys Returns: Dictionary mapping provider names to their status information """ status = {} for provider_name in ProviderFactory.SUPPORTED_PROVIDERS: try: # Try to create provider to check if it's properly configured provider = ProviderFactory.create_provider(provider_name, settings) status[provider_name] = { "available": True, "provider_name": provider.provider_name, "model": provider.model_name, "dimension": provider.dimension, "supports_embeddings": provider.supports_embeddings(), "supports_completions": provider.supports_completions() } except ValueError as e: status[provider_name] = { "available": False, "error": str(e) } return status @staticmethod def get_available_providers(settings: Settings) -> List[str]: """ Get list of available (properly configured) provider names. Args: settings: Application settings with API keys Returns: List of available provider names """ status = ProviderFactory.get_provider_status(settings) return [ name for name, info in status.items() if info.get("available", False) ]