"""Backend factory — centralized creation of LLM backends. Replaces scattered if/elif backend selection across mcp.py, routes.py, web.py. """ from __future__ import annotations import logging from core.types import LLMBackend logger = logging.getLogger(__name__) class BackendFactory: """Registry and factory for LLM backends.""" _registry: dict[str, type[LLMBackend]] = {} @classmethod def register(cls, name: str, backend_cls: type[LLMBackend]) -> None: cls._registry[name] = backend_cls @classmethod def create(cls, name: str, **kwargs) -> LLMBackend: if name not in cls._registry: raise ValueError(f"Unknown backend: {name!r}. Available: {list(cls._registry.keys())}") return cls._registry[name](**kwargs) @classmethod def create_safe(cls, name: str, **kwargs) -> LLMBackend: """Create backend, falling back to mock on failure.""" try: return cls.create(name, **kwargs) except Exception as exc: logger.warning("Backend %r unavailable (%s), falling back to mock", name, exc) return cls.create("mock") def _register_defaults() -> None: """Register all built-in backends. Called at module load.""" from core.backends import MockBackend BackendFactory.register("mock", MockBackend) try: from core.backends import AnthropicBackend BackendFactory.register("anthropic", AnthropicBackend) except Exception: pass try: from core.backends import OpenAIBackend BackendFactory.register("openai", OpenAIBackend) except Exception: pass try: from core.backends import GeminiBackend BackendFactory.register("gemini", GeminiBackend) except Exception: pass _register_defaults()