""" LLM 模型管理服务 提供获取不同 LLM 提供商可用模型列表的功能 """ from typing import List, Dict, Any, Optional import requests class LLMModelService: """LLM 模型管理服务""" @staticmethod def get_openai_models(api_key: str, base_url: Optional[str] = None) -> List[str]: """ 获取 OpenAI 兼容 API 的模型列表 Args: api_key: API Key base_url: API 基础 URL,默认为 OpenAI 官方 API Returns: 模型名称列表 """ try: # 默认使用 OpenAI 官方 API if not base_url: base_url = "https://api.openai.com/v1" # 规范化 base_url:确保有协议前缀 base_url = base_url.strip() if not base_url.startswith(('http://', 'https://')): base_url = 'https://' + base_url # 移除末尾的斜杠和常见 endpoint 路径 base_url = base_url.rstrip('/') # 移除可能已经存在的 endpoint 路径 for endpoint in ['/chat/completions', '/completions', '/embeddings', '/models']: if base_url.endswith(endpoint): base_url = base_url[:-len(endpoint)] break # 调用 models API try: response = requests.get( f"{base_url}/models", headers={ "Authorization": f"Bearer {api_key}", "Content-Type": "application/json" }, timeout=10 ) except requests.exceptions.InvalidSchema as e: raise Exception(f"URL 格式错误: {base_url}/models - 请确保 URL 以 http:// 或 https:// 开头") except requests.exceptions.ConnectionError as e: raise Exception(f"无法连接到 API: {base_url}/models - 请检查网络连接和 API 地址") except requests.exceptions.Timeout as e: raise Exception(f"请求超时: {base_url}/models - 请检查网络连接") if response.status_code != 200: raise Exception(f"HTTP {response.status_code}: {response.text}") data = response.json() models = [model['id'] for model in data.get('data', [])] # 返回所有模型,不做过滤 return models except Exception as e: raise Exception(f"获取 OpenAI 模型列表失败: {str(e)}") @staticmethod def get_anthropic_models(api_key: str) -> List[str]: """ 获取 Anthropic Claude 模型列表 Args: api_key: API Key Returns: 模型名称列表 """ try: # Anthropic 没有公开的模型列表 API,返回已知模型 return [ "claude-3-5-sonnet-20241022", "claude-3-5-haiku-20241022", "claude-3-opus-20240229", "claude-3-sonnet-20240229", "claude-3-haiku-20240307", "claude-2.1", "claude-2.0", "claude-instant-1.2" ] except Exception as e: raise Exception(f"获取 Anthropic 模型列表失败: {str(e)}") @staticmethod def get_ollama_models(base_url: str = "http://localhost:11434") -> List[str]: """ 获取 Ollama 本地模型列表 Args: base_url: Ollama API 地址 Returns: 模型名称列表 """ try: response = requests.get( f"{base_url}/api/tags", timeout=10 ) if response.status_code != 200: raise Exception(f"HTTP {response.status_code}: {response.text}") data = response.json() models = [model['name'] for model in data.get('models', [])] return models except Exception as e: raise Exception(f"获取 Ollama 模型列表失败: {str(e)}") @staticmethod def detect_provider(api_url: str) -> str: """ 根据 API URL 检测提供商类型 Args: api_url: API 地址 Returns: 提供商类型: 'openai', 'anthropic', 'ollama', 'unknown' """ api_url_lower = api_url.lower() if 'openai' in api_url_lower or 'api.openai.com' in api_url_lower: return 'openai' elif 'anthropic' in api_url_lower or 'api.anthropic.com' in api_url_lower: return 'anthropic' elif 'ollama' in api_url_lower or 'localhost:11434' in api_url_lower or '127.0.0.1:11434' in api_url_lower: return 'ollama' elif 'bigmodel' in api_url_lower or 'glm' in api_url_lower: # 智谱AI GLM - 兼容 OpenAI API return 'openai' elif 'siliconflow' in api_url_lower or 'silicon.cloud' in api_url_lower: # SiliconFlow 等兼容 OpenAI API 的服务 return 'openai' elif 'deepseek' in api_url_lower: # DeepSeek 等兼容 OpenAI API 的服务 return 'openai' else: # 默认尝试 OpenAI 兼容 API return 'openai' @staticmethod def get_models_by_provider( provider: str, api_key: str, api_url: Optional[str] = None ) -> List[str]: """ 根据提供商类型获取模型列表 Args: provider: 提供商类型 ('openai', 'anthropic', 'ollama') api_key: API Key api_url: API 地址(可选) Returns: 模型名称列表 """ if provider == 'openai': return LLMModelService.get_openai_models(api_key, api_url) elif provider == 'anthropic': return LLMModelService.get_anthropic_models(api_key) elif provider == 'ollama': base_url = api_url or "http://localhost:11434" # 移除 /v1 后缀(如果有) base_url = base_url.replace('/v1', '').replace('/v1/', '') return LLMModelService.get_ollama_models(base_url) else: raise Exception(f"不支持的提供商: {provider}")