Files

147 lines
5.3 KiB
Diff

diff --git a/python-api/app/core/exceptions.py b/python-api/app/core/exceptions.py
index d9970d5..837f8d3 100644
--- a/python-api/app/core/exceptions.py
+++ b/python-api/app/core/exceptions.py
@@ -24,9 +24,16 @@ class AppException(HTTPException):
status_code: int,
message: str = "操作失败",
detail: dict | None = None,
+ *,
+ error_code: str | None = None,
):
- super().__init__(status_code=status_code, detail=detail or {})
+ body = detail or {}
+ body["message"] = message
+ if error_code:
+ body["error_code"] = error_code
+ super().__init__(status_code=status_code, detail=body)
self.message = message
+ self.error_code = error_code
class NotFoundException(AppException):
@@ -44,7 +51,7 @@ class ValidationException(AppException):
def __init__(self, message: str = "参数验证失败"):
super().__init__(
- status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
+ status_code=status.HTTP_422_UNPROCESSABLE_CONTENT,
message=message,
)
@@ -79,6 +86,17 @@ class BusinessException(AppException):
)
+class InsufficientPointsException(AppException):
+ """积分不足"""
+
+ def __init__(self, message: str = "积分不足"):
+ super().__init__(
+ status_code=status.HTTP_402_PAYMENT_REQUIRED,
+ message=message,
+ error_code="insufficient_points",
+ )
+
+
class ModelUnavailableException(AppException):
"""AI 模型不可用"""
@@ -99,6 +117,50 @@ class TaskFailedException(AppException):
)
+class PromptNotFoundException(AppException):
+ """提示词文件不存在"""
+
+ def __init__(self, message: str = "未找到提示词"):
+ super().__init__(
+ status_code=status.HTTP_404_NOT_FOUND,
+ message=message,
+ error_code="prompt_not_found",
+ )
+
+
+class AIEmptyResponseException(AppException):
+ """AI 返回内容为空"""
+
+ def __init__(self, message: str = "AI 返回内容为空"):
+ super().__init__(
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
+ message=message,
+ error_code="empty_result",
+ )
+
+
+class AIParseErrorException(AppException):
+ """AI 返回内容解析失败"""
+
+ def __init__(self, message: str = "AI 返回格式解析失败"):
+ super().__init__(
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
+ message=message,
+ error_code="parse_error",
+ )
+
+
+class AITimeoutException(AppException):
+ """AI 调用超时"""
+
+ def __init__(self, message: str = "AI 请求超时,请稍后重试"):
+ super().__init__(
+ status_code=status.HTTP_504_GATEWAY_TIMEOUT,
+ message=message,
+ error_code="timeout",
+ )
+
+
# ═══════════════════════════════════════════════════════════════
# 第三方平台异常(PlatformError 体系)
# ═══════════════════════════════════════════════════════════════
@@ -111,14 +173,15 @@ class PlatformErrorType:
确保前端和网关能够统一处理。
"""
- RATE_LIMIT = "rate_limit" # 429,可重试
- AUTH_FAILED = "auth_failed" # 401/403,不可重试
- TIMEOUT = "timeout" # 连接/读取超时,可重试
- SERVER_ERROR = "server_error" # 第三方 5xx,可重试
- BAD_REQUEST = "bad_request" # 参数错误,不可重试
+ RATE_LIMIT = "rate_limit" # 429,可重试
+ AUTH_FAILED = "auth_failed" # 401/403,不可重试
+ TIMEOUT = "timeout" # 连接/读取超时,可重试
+ SERVER_ERROR = "server_error" # 第三方 5xx,可重试
+ BAD_REQUEST = "bad_request" # 参数错误,不可重试
QUOTA_EXHAUSTED = "quota_exhausted" # 额度用完,不可重试(或延迟重试)
- NOT_FOUND = "not_found" # 资源不存在,不可重试
- UNKNOWN = "unknown" # 兜底
+ NOT_FOUND = "not_found" # 资源不存在,不可重试
+ CONTENT_VIOLATION = "content_violation" # 内容安全/审核不通过,不可重试
+ UNKNOWN = "unknown" # 兜底
class PlatformError(Exception):
@@ -145,12 +208,14 @@ class PlatformError(Exception):
retryable: bool = False,
error_type: str = PlatformErrorType.UNKNOWN,
status_code: int | None = None,
+ raw_code: str | None = None,
):
super().__init__(message)
self.platform = platform
self.retryable = retryable
self.error_type = error_type
self.status_code = status_code
+ self.raw_code = raw_code
def to_http_status(self) -> int:
"""根据 error_type 和 retryable 返回标准 HTTP 状态码"""
@@ -161,6 +226,7 @@ class PlatformError(Exception):
PlatformErrorType.AUTH_FAILED: 401,
PlatformErrorType.BAD_REQUEST: 400,
PlatformErrorType.NOT_FOUND: 404,
+ PlatformErrorType.CONTENT_VIOLATION: 400,
}
if self.error_type in mapping:
return mapping[self.error_type]