fix: 增强LLMRequest类的异常处理,添加统一的模型异常处理方法
This commit is contained in:
@@ -14,6 +14,18 @@ install(extra_lines=3)
|
||||
|
||||
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模式
|
||||
try:
|
||||
from .model_manager import ModelManager
|
||||
@@ -349,6 +361,76 @@ class LLMRequest:
|
||||
reasoning = match[1].strip() if match else ""
|
||||
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方法 ===
|
||||
# 这些方法提供与新架构的桥接
|
||||
|
||||
@@ -414,16 +496,10 @@ class LLMRequest:
|
||||
return content, reasoning_content
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"模型 {self.model_name} 图片响应生成失败: {str(e)}")
|
||||
# 向后兼容的异常处理
|
||||
if "401" in str(e) or "API key" in str(e):
|
||||
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
|
||||
self._handle_model_exception(e, "image")
|
||||
# 这行代码永远不会执行,因为_handle_model_exception总是抛出异常
|
||||
# 但是为了满足类型检查的要求,我们添加一个不可达的返回语句
|
||||
return "", "" # pragma: no cover
|
||||
|
||||
async def generate_response_for_voice(self, voice_bytes: bytes) -> Tuple:
|
||||
"""
|
||||
@@ -453,16 +529,9 @@ class LLMRequest:
|
||||
return (response.content,) if response.content else ("",)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"模型 {self.model_name} 语音识别失败: {str(e)}")
|
||||
# 向后兼容的异常处理
|
||||
if "401" in str(e) or "API key" in str(e):
|
||||
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
|
||||
self._handle_model_exception(e, "voice")
|
||||
# 不可达的返回语句,仅用于满足类型检查
|
||||
return ("",) # pragma: no cover
|
||||
|
||||
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)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"模型 {self.model_name} 生成响应失败: {str(e)}")
|
||||
# 向后兼容的异常处理
|
||||
if "401" in str(e) or "API key" in str(e):
|
||||
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
|
||||
self._handle_model_exception(e, "text")
|
||||
# 不可达的返回语句,仅用于满足类型检查
|
||||
return "", ("", self.model_name) # pragma: no cover
|
||||
|
||||
async def get_embedding(self, text: str) -> Union[list, None]:
|
||||
"""
|
||||
@@ -583,15 +645,12 @@ class LLMRequest:
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"模型 {self.model_name} 获取embedding失败: {str(e)}")
|
||||
# 向后兼容的异常处理
|
||||
if "401" in str(e) or "API key" in str(e):
|
||||
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:
|
||||
# 对于embedding请求,我们记录错误但不抛出异常,而是返回None
|
||||
# 这是为了保持与原有行为的兼容性
|
||||
try:
|
||||
self._handle_model_exception(e, "embedding")
|
||||
except RuntimeError:
|
||||
# 捕获_handle_model_exception抛出的RuntimeError,转换为警告日志
|
||||
logger.warning(f"模型 {self.model_name} embedding请求失败,返回None: {str(e)}")
|
||||
return None
|
||||
|
||||
|
||||
Reference in New Issue
Block a user