Files

263 lines
7.7 KiB
Python

"""
模型列表服务
支持动态API获取和预定义模型列表
"""
import httpx
from typing import Dict, List
# 导入统一的日志前缀
try:
from ..utils.common import ERROR_PREFIX
except ImportError:
# 如果导入失败,使用默认值
ERROR_PREFIX = "✨-错误"
# --- 智谱AI预定义模型列表 ---
ZHIPU_MODELS = [
"glm-5.1",
"glm-5",
"glm-5-turbo",
"glm-5v-turbo",
"glm-4.7",
"glm-4.7-flash",
"glm-4.7-flashx",
"glm-4.6",
"glm-4.6v",
"glm-4.6v-flash",
"glm-4.5",
"glm-4.5-flash",
"glm-4.5-air",
"glm-4.5-airx",
"glm-4.5v",
"glm-4-plus",
"glm-4-flash",
"glm-4-flash-250414",
"glm-4-air",
"glm-4-air-250414",
"glm-z1-flash",
"glm-4v-plus",
"glm-4v-flash",
"glm-4v",
"glm-ocr",
"glm-4-long",
"glm-4-longwriter",
"glm-zero-preview",
"glm-4.1v-thinking-flash"
]
def get_models_from_service(base_url: str, api_key: str, service_type: str) -> Dict:
"""
从服务提供商动态获取模型列表
参数:
base_url: API基础URL
api_key: API密钥
service_type: 服务类型 ('openai_compatible', 'ollama', 'zhipu')
返回:
Dict: {
"success": bool,
"models": {"llm": [...], "vlm": [...]}, # success=True时
"error": str # success=False时的错误信息
}
"""
try:
# 检查必需参数
if not base_url:
return {
"success": False,
"error": "请填写Base URL"
}
# 智谱使用预定义列表,不需要API Key验证
if service_type == 'zhipu':
return _get_zhipu_models()
if service_type == 'openai_compatible' and not api_key:
return {
"success": False,
"error": "请填写API Key"
}
# 根据服务类型调用不同的获取方法
if service_type == 'ollama':
return _fetch_ollama_models(base_url)
else: # openai_compatible
return _fetch_openai_compatible_models(base_url, api_key)
except Exception as e:
print(f"{ERROR_PREFIX} 获取模型列表异常: {str(e)}")
return {
"success": False,
"error": f"获取失败: {str(e)}"
}
def _fetch_openai_compatible_models(base_url: str, api_key: str) -> Dict:
"""获取OpenAI兼容API的模型列表"""
try:
url = f"{base_url.rstrip('/')}/models"
headers = {
"Authorization": f"Bearer {api_key}",
"Accept": "application/json",
"Content-Type": "application/json"
}
response = httpx.get(url, headers=headers, timeout=10.0)
if response.status_code == 401:
return {
"success": False,
"error": "API Key错误,认证失败"
}
elif response.status_code == 404:
return {
"success": False,
"error": "API地址错误,未找到模型接口"
}
elif response.status_code != 200:
return {
"success": False,
"error": f"API返回错误 (HTTP {response.status_code})"
}
data = response.json()
models = data.get('data', [])
if not models:
return {
"success": False,
"error": "未找到任何可用模型"
}
# 提取模型ID并返回(LLM和VLM返回相同列表)
model_ids = [m['id'] for m in models if 'id' in m]
return {
"success": True,
"models": {
"llm": model_ids.copy(),
"vlm": model_ids.copy()
}
}
except httpx.TimeoutException:
return {
"success": False,
"error": "请求超时,请检查网络连接"
}
except httpx.ConnectError:
return {
"success": False,
"error": "无法连接到服务,请检查Base URL"
}
except Exception as e:
print(f"{ERROR_PREFIX} 获取OpenAI兼容模型失败: {str(e)}")
return {
"success": False,
"error": f"获取模型列表失败: {str(e)}"
}
def _fetch_ollama_models(base_url: str) -> Dict:
"""获取Ollama的模型列表"""
try:
# Ollama 原生 API 在根路径,需要移除可能存在的 /v1 后缀
clean_url = base_url.rstrip('/')
if clean_url.endswith('/v1'):
clean_url = clean_url[:-3]
url = f"{clean_url}/api/tags"
# 添加必需的请求头(参考 ollama-python SDK)
import platform
headers = {
'Accept': 'application/json',
'Content-Type': 'application/json',
'User-Agent': f'comfyui-prompt-assistant/1.0 ({platform.machine()} {platform.system().lower()}) Python/{platform.python_version()}'
}
# 创建禁用代理的客户端,避免系统代理干扰localhost请求
# 这是502错误的常见原因:系统代理无法正确转发localhost请求
with httpx.Client(proxy=None, trust_env=False) as client:
response = client.get(url, headers=headers, timeout=10.0)
if response.status_code == 404:
return {
"success": False,
"error": "Ollama服务未启动或Base URL错误"
}
elif response.status_code == 400:
error_detail = response.text[:500] if response.text else "无响应体"
return {
"success": False,
"error": f"Ollama返回400错误。详情: {error_detail}"
}
elif response.status_code == 502:
return {
"success": False,
"error": f"Ollama返回错误 (HTTP 502): 网关错误,请检查Ollama服务是否正常运行"
}
elif response.status_code != 200:
return {
"success": False,
"error": f"Ollama返回错误 (HTTP {response.status_code}): {response.text[:200]}"
}
data = response.json()
models = data.get('models', [])
if not models:
return {
"success": False,
"error": "未找到任何Ollama模型"
}
# 提取模型名称并返回(LLM和VLM返回相同列表)
model_names = [m['name'] for m in models if 'name' in m]
return {
"success": True,
"models": {
"llm": model_names.copy(),
"vlm": model_names.copy()
}
}
except httpx.TimeoutException:
return {
"success": False,
"error": "请求超时,Ollama可能未启动"
}
except httpx.ConnectError as e:
print(f"{ERROR_PREFIX} 无法连接到Ollama服务: {str(e)}")
return {
"success": False,
"error": "无法连接到Ollama服务"
}
except Exception as e:
print(f"{ERROR_PREFIX} 获取Ollama模型失败: {str(e)}")
import traceback
traceback.print_exc()
return {
"success": False,
"error": f"获取Ollama模型失败: {str(e)}"
}
def _get_zhipu_models() -> Dict:
"""
获取智谱AI的预定义模型列表
智谱AI暂不提供公开的模型列表API,使用预定义列表
"""
return {
"success": True,
"models": {
"llm": ZHIPU_MODELS.copy(),
"vlm": ZHIPU_MODELS.copy()
}
}