"""Provider factory and resolver.""" from __future__ import annotations import os from pathlib import Path from llm.core.interface import LLMProvider from llm.core.types import ProviderType from llm.providers.claude import ClaudeProvider from llm.providers.openai import OpenAIProvider from llm.providers.ollama import OllamaProvider _PROVIDER_MAP: dict[ProviderType, type[LLMProvider]] = { ProviderType.CLAUDE: ClaudeProvider, ProviderType.OPENAI: OpenAIProvider, ProviderType.OLLAMA: OllamaProvider, } LLM_ENV_FILE = ".llm.env" def _strip_env_value(value: str) -> str: value = value.strip() if len(value) >= 2 and value[0] == value[-1] and value[0] in {"'", '"'}: return value[1:-1] return value def _read_saved_llm_config(env_path: str | Path = LLM_ENV_FILE) -> dict[str, str]: path = Path(env_path) if not path.is_file(): return {} config: dict[str, str] = {} for line in path.read_text().splitlines(): stripped = line.strip() if not stripped or stripped.startswith("#") or "=" not in stripped: continue key, value = stripped.split("=", 1) config[key.strip()] = _strip_env_value(value) return config def _resolve_provider_type(provider_type: ProviderType | str | None) -> ProviderType | str: if provider_type is not None: return provider_type env_provider = os.environ.get("LLM_PROVIDER") if env_provider: return _strip_env_value(env_provider).lower() saved_config = _read_saved_llm_config() return saved_config.get("LLM_PROVIDER", "claude").lower() def get_provider(provider_type: ProviderType | str | None = None, **kwargs: str) -> LLMProvider: provider_type = _resolve_provider_type(provider_type) if isinstance(provider_type, str): try: provider_type = ProviderType(provider_type) except ValueError: raise ValueError(f"Unknown provider type: {provider_type}. Valid types: {[p.value for p in ProviderType]}") provider_cls = _PROVIDER_MAP.get(provider_type) if not provider_cls: raise ValueError(f"No provider registered for type: {provider_type}") return provider_cls(**kwargs) def register_provider(provider_type: ProviderType, provider_cls: type[LLMProvider]) -> None: _PROVIDER_MAP[provider_type] = provider_cls