Manages LLM providers, models, and client creation. This is a clean, refactored implementation that: - Uses Pydantic models for type safety - Delegates to specialized classes for concerns - Contains ZERO hardcoded model names - Provides a simple, intuitive API
| 30 | |
| 31 | |
| 32 | class ModelManager: |
| 33 | """ |
| 34 | Manages LLM providers, models, and client creation. |
| 35 | |
| 36 | This is a clean, refactored implementation that: |
| 37 | - Uses Pydantic models for type safety |
| 38 | - Delegates to specialized classes for concerns |
| 39 | - Contains ZERO hardcoded model names |
| 40 | - Provides a simple, intuitive API |
| 41 | """ |
| 42 | |
| 43 | def __init__(self) -> None: |
| 44 | """Initialize ModelManager with chuk_llm configuration.""" |
| 45 | self._chuk_config = None |
| 46 | self._active_provider: str | None = None |
| 47 | self._active_model: str | None = None |
| 48 | self._custom_providers: dict[str, RuntimeProviderConfig] = {} |
| 49 | self._client_factory = ClientFactory() |
| 50 | |
| 51 | self._initialize_chuk_llm() |
| 52 | self._load_custom_providers() |
| 53 | # Note: Discovery is NOT triggered automatically - call refresh_models() if needed |
| 54 | |
| 55 | # ── Initialization ──────────────────────────────────────────────────────── |
| 56 | |
| 57 | def _initialize_chuk_llm(self) -> None: |
| 58 | """Initialize chuk_llm configuration.""" |
| 59 | try: |
| 60 | from chuk_llm.configuration import get_config |
| 61 | |
| 62 | self._chuk_config = get_config() |
| 63 | logger.debug("Loaded chuk_llm configuration") |
| 64 | |
| 65 | # Use configured default provider (from defaults.py) |
| 66 | if self._chuk_config: |
| 67 | self._active_provider = DEFAULT_PROVIDER # type: ignore[unreachable] |
| 68 | # Defer model resolution to avoid circular dependencies during __init__ |
| 69 | self._active_model = None |
| 70 | |
| 71 | except Exception as e: |
| 72 | logger.error(f"Failed to initialize chuk_llm: {e}") |
| 73 | # Minimal fallback - use configured default |
| 74 | self._chuk_config = None |
| 75 | self._active_provider = DEFAULT_PROVIDER |
| 76 | self._active_model = None # Will be determined on first use |
| 77 | |
| 78 | def _load_custom_providers(self) -> None: |
| 79 | """Load custom providers from preferences.""" |
| 80 | try: |
| 81 | from mcp_cli.utils.preferences import get_preference_manager |
| 82 | |
| 83 | prefs = get_preference_manager() |
| 84 | custom_providers = prefs.get_custom_providers() |
| 85 | |
| 86 | for name, provider_data in custom_providers.items(): |
| 87 | # Convert dict to Pydantic model |
| 88 | config = RuntimeProviderConfig( |
| 89 | name=name, |
no outgoing calls