ai-chatbot / app /llm /factory.py
appQQQ's picture
chore: upload app/llm/factory.py
8178bce verified
Raw
History Blame
3.07 kB
"""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,
}
@lru_cache(maxsize=1)
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()