Spaces:
Sleeping
Sleeping
| """LLM 工厂: 根据 settings.llm_provider 选择 provider. | |
| 新增 provider 的步骤: | |
| 1. 在 base.py 实现一个新类 (或复用 OpenAICompatibleLLM) | |
| 2. 在本文件 _PROVIDERS 加一行 | |
| 3. 在 .env.example 暴露新 env | |
| """ | |
| from __future__ import annotations | |
| import logging | |
| from functools import lru_cache | |
| from app.config import settings | |
| from app.core.errors import LLMUnavailableError | |
| from app.llm.base import AbstractLLM | |
| from app.llm.minimax import OpenAICompatibleLLM | |
| logger = logging.getLogger(__name__) | |
| def _build_minimax() -> OpenAICompatibleLLM: | |
| key = settings.minimax_api_key.get_secret_value() | |
| if not key: | |
| raise LLMUnavailableError( | |
| "MINIMAX_API_KEY is empty. Set it in .env or HF Space secrets.", | |
| code="llm_api_key_missing", | |
| ) | |
| return OpenAICompatibleLLM( | |
| api_key=key, | |
| base_url=settings.minimax_base_url, | |
| model=settings.minimax_model, | |
| provider_name="minimax", | |
| ) | |
| def _build_openai() -> OpenAICompatibleLLM: | |
| key = settings.openai_api_key.get_secret_value() | |
| if not key: | |
| raise LLMUnavailableError( | |
| "OPENAI_API_KEY is empty.", | |
| code="llm_api_key_missing", | |
| ) | |
| return OpenAICompatibleLLM( | |
| api_key=key, | |
| base_url="https://api.openai.com/v1", | |
| # 允许用 env 覆盖 model (openai_model), 没设则用 gpt-4o-mini | |
| model=getattr(settings, "openai_model", "gpt-4o-mini"), | |
| provider_name="openai", | |
| ) | |
| def _build_qwen() -> OpenAICompatibleLLM: | |
| key = settings.qwen_api_key.get_secret_value() | |
| if not key: | |
| raise LLMUnavailableError( | |
| "QWEN_API_KEY is empty.", | |
| code="llm_api_key_missing", | |
| ) | |
| return OpenAICompatibleLLM( | |
| api_key=key, | |
| # 阿里百炼 OpenAI 兼容端点 | |
| base_url="https://dashscope.aliyuncs.com/compatible-mode/v1", | |
| model=getattr(settings, "qwen_model", "qwen-max"), | |
| provider_name="qwen", | |
| ) | |
| def _build_anthropic() -> AbstractLLM: | |
| """占位: Anthropic SDK 原生协议, 暂未实现, 留 hook.""" | |
| raise NotImplementedError( | |
| "Anthropic provider not yet implemented. " | |
| "Use openai-compatible via OpenRouter or implement anthropic.AsyncAnthropic wrapper." | |
| ) | |
| _PROVIDERS = { | |
| "minimax": _build_minimax, | |
| "openai": _build_openai, | |
| "qwen": _build_qwen, | |
| "anthropic": _build_anthropic, | |
| } | |
| def get_llm() -> AbstractLLM: | |
| """单例 LLM. 在 lifespan 中调用 + 缓存.""" | |
| builder = _PROVIDERS.get(settings.llm_provider) | |
| if builder is None: | |
| raise LLMUnavailableError( | |
| f"Unknown llm_provider: {settings.llm_provider}", | |
| code="llm_provider_unknown", | |
| ) | |
| logger.info("Initializing LLM provider: %s", settings.llm_provider) | |
| return builder() | |
| async def close_llm() -> None: | |
| """关闭 LLM 客户端. lifespan shutdown 时调用.""" | |
| try: | |
| llm = get_llm() | |
| except Exception: # noqa: BLE001 | |
| return | |
| await llm.aclose() | |
| get_llm.cache_clear() | |