CodeReview/backend/app/services/llm/factory.py

219 lines
6.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
LLM工厂类 - 统一创建和管理LLM适配器
使用 LiteLLM 作为主要适配器,支持大多数 LLM 提供商。
对于 API 格式特殊的提供商百度、MiniMax、豆包使用原生适配器。
"""
from typing import Dict, List
from .types import LLMConfig, LLMProvider, DEFAULT_MODELS
from .base_adapter import BaseLLMAdapter
from .adapters import (
LiteLLMAdapter,
BaiduAdapter,
MinimaxAdapter,
DoubaoAdapter,
)
# 必须使用原生适配器的提供商API 格式特殊)
NATIVE_ONLY_PROVIDERS = {
LLMProvider.BAIDU,
LLMProvider.MINIMAX,
LLMProvider.DOUBAO,
}
class LLMFactory:
"""LLM工厂类"""
_adapters: Dict[str, BaseLLMAdapter] = {}
@classmethod
def create_adapter(cls, config: LLMConfig) -> BaseLLMAdapter:
"""创建LLM适配器实例"""
cache_key = cls._get_cache_key(config)
# 从缓存中获取
if cache_key in cls._adapters:
return cls._adapters[cache_key]
# 创建新的适配器实例
adapter = cls._instantiate_adapter(config)
# 缓存实例
cls._adapters[cache_key] = adapter
return adapter
@classmethod
def _instantiate_adapter(cls, config: LLMConfig) -> BaseLLMAdapter:
"""根据提供商类型实例化适配器"""
# 如果未指定模型,使用默认模型
if not config.model:
config.model = DEFAULT_MODELS.get(config.provider, "gpt-4o-mini")
# 对于必须使用原生适配器的提供商
if config.provider in NATIVE_ONLY_PROVIDERS:
return cls._create_native_adapter(config)
# 其他提供商使用 LiteLLM
if LiteLLMAdapter.supports_provider(config.provider):
return LiteLLMAdapter(config)
# 不支持的提供商
raise ValueError(f"不支持的LLM提供商: {config.provider}")
@classmethod
def _create_native_adapter(cls, config: LLMConfig) -> BaseLLMAdapter:
"""创建原生适配器(仅用于 API 格式特殊的提供商)"""
native_adapter_map = {
LLMProvider.BAIDU: BaiduAdapter,
LLMProvider.MINIMAX: MinimaxAdapter,
LLMProvider.DOUBAO: DoubaoAdapter,
}
adapter_class = native_adapter_map.get(config.provider)
if not adapter_class:
raise ValueError(f"不支持的原生适配器提供商: {config.provider}")
return adapter_class(config)
@classmethod
def _get_cache_key(cls, config: LLMConfig) -> str:
"""生成缓存键"""
api_key_prefix = config.api_key[:8] if config.api_key else "no-key"
return f"{config.provider.value}:{config.model}:{api_key_prefix}"
@classmethod
def clear_cache(cls) -> None:
"""清除缓存"""
cls._adapters.clear()
@classmethod
def get_supported_providers(cls) -> List[LLMProvider]:
"""获取支持的提供商列表"""
return list(LLMProvider)
@classmethod
def get_default_model(cls, provider: LLMProvider) -> str:
"""获取提供商的默认模型"""
return DEFAULT_MODELS.get(provider, "gpt-4o-mini")
@classmethod
def get_available_models(cls, provider: LLMProvider) -> List[str]:
"""获取提供商的可用模型列表 (2025年最新)"""
models = {
LLMProvider.GEMINI: [
"gemini-3-pro",
"gemini-3.0-deep-think",
"gemini-2.5-flash",
"gemini-2.5-pro",
"gemini-2.5-flash-lite",
"gemini-2.5-flash-live-api",
"veo-3.1",
"veo-3.1-fast",
],
LLMProvider.OPENAI: [
"gpt-5",
"gpt-5.1",
"gpt-5.1-instant",
"gpt-5.1-codex-max",
"gpt-4o",
"gpt-4o-mini",
"gpt-4.5",
"o4-mini",
"o3",
"o3-mini",
"gpt-oss-120b",
"gpt-oss-20b",
],
LLMProvider.CLAUDE: [
"claude-opus-4.5",
"claude-sonnet-4.5",
"claude-haiku-4.5",
"claude-sonnet-4",
"claude-opus-4",
"claude-3.7-sonnet",
"claude-3.5-sonnet",
"claude-3.5-haiku",
"claude-3-opus",
],
LLMProvider.QWEN: [
"qwen3-max-instruct",
"qwen3-235b-a22b",
"qwen3-turbo",
"qwen3-32b",
"qwen3-4b",
"qwen3-embedding-8b",
"qwen-image",
"qwen-vl",
"qwen-audio",
],
LLMProvider.DEEPSEEK: [
"deepseek-v3.1-terminus",
"deepseek-r1-70b",
"deepseek-r1-zero",
"deepseek-v3.2-exp",
"deepseek-chat",
"deepseek-reasoner",
"deepseek-ocr",
],
LLMProvider.ZHIPU: [
"glm-4.6",
"glm-4.6-reap-218b",
"glm-4.5",
"glm-4.5v",
"glm-4.5-air-106b",
"glm-4-flash",
"glm-4v-flash",
"glm-4.1v-thinking",
],
LLMProvider.MOONSHOT: [
"kimi-k2",
"kimi-k2-thinking",
"kimi-k2-instruct-0905",
"kimi-k1.5",
"kimi-vl",
"kimi-dev-72b",
"kimi-researcher",
"kimi-linear",
],
LLMProvider.BAIDU: [
"ernie-4.5",
"ernie-4.5-21b-a3b-thinking",
"ernie-4.0-8k",
"ernie-3.5-8k",
"ernie-vl",
],
LLMProvider.MINIMAX: [
"minimax-m2",
"minimax-01-text",
"minimax-01-vl",
"minimax-m1",
"speech-2.6",
"hailuo-02",
"music-1.5",
],
LLMProvider.DOUBAO: [
"doubao-1.6-pro",
"doubao-1.5-pro",
"doubao-seed-code",
"doubao-seed-1.6",
"doubao-vision-language",
],
LLMProvider.OLLAMA: [
"llama3.3-70b",
"qwen3-8b",
"gemma3-27b",
"dolphin-3.0-llama3.1-8b",
"cogito-v1",
"deepseek-r1",
"gpt-oss-120b",
"llama3.1-405b",
"mistral-nemo",
"phi-3",
],
}
return models.get(provider, [])