fix: 增强LLMRequest类的异常处理,添加统一的模型异常处理方法

This commit is contained in:
墨梓柒
2025-07-28 19:54:37 +08:00
parent 3a11c69b8c
commit f0b9e8919a

View File

@@ -14,6 +14,18 @@ install(extra_lines=3)
logger = get_logger("model_utils") logger = get_logger("model_utils")
# 导入具体的异常类型用于精确的异常处理
try:
from .exceptions import NetworkConnectionError, ReqAbortException, RespNotOkException, RespParseException
SPECIFIC_EXCEPTIONS_AVAILABLE = True
except ImportError:
logger.warning("无法导入具体异常类型,将使用通用异常处理")
NetworkConnectionError = Exception
ReqAbortException = Exception
RespNotOkException = Exception
RespParseException = Exception
SPECIFIC_EXCEPTIONS_AVAILABLE = False
# 新架构导入 - 使用延迟导入以支持fallback模式 # 新架构导入 - 使用延迟导入以支持fallback模式
try: try:
from .model_manager import ModelManager from .model_manager import ModelManager
@@ -349,6 +361,76 @@ class LLMRequest:
reasoning = match[1].strip() if match else "" reasoning = match[1].strip() if match else ""
return content, reasoning return content, reasoning
def _handle_model_exception(self, e: Exception, operation: str) -> None:
"""
统一的模型异常处理方法
根据异常类型提供更精确的错误信息和处理策略
Args:
e: 捕获的异常
operation: 操作类型(用于日志记录)
"""
operation_desc = {
"image": "图片响应生成",
"voice": "语音识别",
"text": "文本响应生成",
"embedding": "向量嵌入获取"
}
op_name = operation_desc.get(operation, operation)
if SPECIFIC_EXCEPTIONS_AVAILABLE:
# 使用具体异常类型进行精确处理
if isinstance(e, NetworkConnectionError):
logger.error(f"模型 {self.model_name} {op_name}失败: 网络连接错误")
raise RuntimeError("网络连接异常请检查网络连接状态或API服务器地址是否正确") from e
elif isinstance(e, ReqAbortException):
logger.error(f"模型 {self.model_name} {op_name}失败: 请求被中断")
raise RuntimeError("请求被中断或取消,请稍后重试") from e
elif isinstance(e, RespNotOkException):
logger.error(f"模型 {self.model_name} {op_name}失败: HTTP响应错误 {e.status_code}")
# 重新抛出原始异常,保留详细的状态码信息
raise e
elif isinstance(e, RespParseException):
logger.error(f"模型 {self.model_name} {op_name}失败: 响应解析错误")
raise RuntimeError("API响应格式异常请检查模型配置或联系管理员") from e
else:
# 未知异常,使用通用处理
logger.error(f"模型 {self.model_name} {op_name}失败: 未知错误 {type(e).__name__}: {str(e)}")
self._handle_generic_exception(e, op_name)
else:
# 如果无法导入具体异常,使用通用处理
logger.error(f"模型 {self.model_name} {op_name}失败: {str(e)}")
self._handle_generic_exception(e, op_name)
def _handle_generic_exception(self, e: Exception, operation: str) -> None:
"""
通用异常处理(向后兼容的错误字符串匹配)
Args:
e: 捕获的异常
operation: 操作描述
"""
error_str = str(e)
# 基于错误消息内容的分类处理
if "401" in error_str or "API key" in error_str or "认证" in error_str:
raise RuntimeError("API key 错误,认证失败,请检查 config/model_config.toml 中的 API key 配置是否正确") from e
elif "429" in error_str or "频繁" in error_str or "rate limit" in error_str:
raise RuntimeError("请求过于频繁,请稍后再试") from e
elif "500" in error_str or "503" in error_str or "服务器" in error_str:
raise RuntimeError("服务器负载过高模型回复失败QAQ") from e
elif "413" in error_str or "payload" in error_str.lower() or "过大" in error_str:
raise RuntimeError("请求体过大,请尝试压缩图片或减少输入内容") from e
elif "timeout" in error_str.lower() or "超时" in error_str:
raise RuntimeError("请求超时,请检查网络连接或稍后重试") from e
else:
raise RuntimeError(f"模型 {self.model_name} {operation}失败: {str(e)}") from e
# === 主要API方法 === # === 主要API方法 ===
# 这些方法提供与新架构的桥接 # 这些方法提供与新架构的桥接
@@ -414,16 +496,10 @@ class LLMRequest:
return content, reasoning_content return content, reasoning_content
except Exception as e: except Exception as e:
logger.error(f"模型 {self.model_name} 图片响应生成失败: {str(e)}") self._handle_model_exception(e, "image")
# 向后兼容的异常处理 # 这行代码永远不会执行因为_handle_model_exception总是抛出异常
if "401" in str(e) or "API key" in str(e): # 但是为了满足类型检查的要求,我们添加一个不可达的返回语句
raise RuntimeError("API key 错误,认证失败,请检查 config/model_config.toml 中的 API key 配置是否正确") from e return "", "" # pragma: no cover
elif "429" in str(e):
raise RuntimeError("请求过于频繁,请稍后再试") from e
elif "500" in str(e) or "503" in str(e):
raise RuntimeError("服务器负载过高模型回复失败QAQ") from e
else:
raise RuntimeError(f"模型 {self.model_name} API请求失败: {str(e)}") from e
async def generate_response_for_voice(self, voice_bytes: bytes) -> Tuple: async def generate_response_for_voice(self, voice_bytes: bytes) -> Tuple:
""" """
@@ -453,16 +529,9 @@ class LLMRequest:
return (response.content,) if response.content else ("",) return (response.content,) if response.content else ("",)
except Exception as e: except Exception as e:
logger.error(f"模型 {self.model_name} 语音识别失败: {str(e)}") self._handle_model_exception(e, "voice")
# 向后兼容的异常处理 # 不可达的返回语句,仅用于满足类型检查
if "401" in str(e) or "API key" in str(e): return ("",) # pragma: no cover
raise RuntimeError("API key 错误,认证失败,请检查 config/model_config.toml 中的 API key 配置是否正确") from e
elif "429" in str(e):
raise RuntimeError("请求过于频繁,请稍后再试") from e
elif "500" in str(e) or "503" in str(e):
raise RuntimeError("服务器负载过高模型回复失败QAQ") from e
else:
raise RuntimeError(f"模型 {self.model_name} API请求失败: {str(e)}") from e
async def generate_response_async(self, prompt: str, **kwargs) -> Union[str, Tuple]: async def generate_response_async(self, prompt: str, **kwargs) -> Union[str, Tuple]:
""" """
@@ -523,16 +592,9 @@ class LLMRequest:
return content, (reasoning_content, self.model_name) return content, (reasoning_content, self.model_name)
except Exception as e: except Exception as e:
logger.error(f"模型 {self.model_name} 生成响应失败: {str(e)}") self._handle_model_exception(e, "text")
# 向后兼容的异常处理 # 不可达的返回语句,仅用于满足类型检查
if "401" in str(e) or "API key" in str(e): return "", ("", self.model_name) # pragma: no cover
raise RuntimeError("API key 错误,认证失败,请检查 config/model_config.toml 中的 API key 配置是否正确") from e
elif "429" in str(e):
raise RuntimeError("请求过于频繁,请稍后再试") from e
elif "500" in str(e) or "503" in str(e):
raise RuntimeError("服务器负载过高模型回复失败QAQ") from e
else:
raise RuntimeError(f"模型 {self.model_name} API请求失败: {str(e)}") from e
async def get_embedding(self, text: str) -> Union[list, None]: async def get_embedding(self, text: str) -> Union[list, None]:
""" """
@@ -583,15 +645,12 @@ class LLMRequest:
return None return None
except Exception as e: except Exception as e:
logger.error(f"模型 {self.model_name} 获取embedding失败: {str(e)}") # 对于embedding请求我们记录错误但不抛出异常而是返回None
# 向后兼容的异常处理 # 这是为了保持与原有行为的兼容性
if "401" in str(e) or "API key" in str(e): try:
raise RuntimeError("API key 错误,认证失败,请检查 config/model_config.toml 中的 API key 配置是否正确") from e self._handle_model_exception(e, "embedding")
elif "429" in str(e): except RuntimeError:
raise RuntimeError("请求过于频繁,请稍后再试") from e # 捕获_handle_model_exception抛出的RuntimeError转换为警告日志
elif "500" in str(e) or "503" in str(e):
raise RuntimeError("服务器负载过高模型回复失败QAQ") from e
else:
logger.warning(f"模型 {self.model_name} embedding请求失败返回None: {str(e)}") logger.warning(f"模型 {self.model_name} embedding请求失败返回None: {str(e)}")
return None return None