refactor: 改进上游错误消息的提取和传递

- 新增 extract_error_message 工具函数,统一错误消息提取逻辑
- 在 HTTPStatusError 异常上附加 upstream_response 属性,保留原始错误
- 优先使用上游响应内容作为错误消息,而非异常字符串表示
- 移除错误消息的长度限制(500/1000 字符)
- 修复边界条件检查,使用 startswith 匹配 "Unable to read" 前缀
- 简化 result.py 中的条件判断逻辑
This commit is contained in:
fawney19
2026-01-05 03:18:55 +08:00
parent dec681fea0
commit 3064497636
7 changed files with 85 additions and 44 deletions

View File

@@ -47,7 +47,6 @@ if TYPE_CHECKING:
from src.api.handlers.base.stream_context import StreamContext
class MessageTelemetry:
"""
负责记录 Usage/Audit避免处理器里重复代码。

View File

@@ -36,6 +36,7 @@ from src.api.handlers.base.stream_processor import StreamProcessor
from src.api.handlers.base.stream_telemetry import StreamTelemetryRecorder
from src.api.handlers.base.utils import build_sse_headers
from src.config.settings import config
from src.core.error_utils import extract_error_message
from src.core.exceptions import (
EmbeddedErrorException,
ProviderAuthException,
@@ -500,6 +501,8 @@ class ChatHandlerBase(BaseMessageHandler, ABC):
error_text = await self._extract_error_text(e)
logger.error(f"Provider 返回错误: {e.response.status_code}\n Response: {error_text}")
await http_client.aclose()
# 将上游错误信息附加到异常,以便故障转移时能够返回给客户端
e.upstream_response = error_text # type: ignore[attr-defined]
raise
except EmbeddedErrorException:
@@ -549,7 +552,7 @@ class ChatHandlerBase(BaseMessageHandler, ABC):
model=ctx.model,
response_time_ms=response_time_ms,
status_code=status_code,
error_message=str(error),
error_message=extract_error_message(error),
request_headers=original_headers,
request_body=actual_request_body,
is_stream=True,
@@ -785,7 +788,7 @@ class ChatHandlerBase(BaseMessageHandler, ABC):
model=model,
response_time_ms=response_time_ms,
status_code=status_code,
error_message=str(e),
error_message=extract_error_message(e),
request_headers=original_headers,
request_body=actual_request_body,
is_stream=False,
@@ -802,10 +805,10 @@ class ChatHandlerBase(BaseMessageHandler, ABC):
try:
if hasattr(e.response, "is_stream_consumed") and not e.response.is_stream_consumed:
error_bytes = await e.response.aread()
return error_bytes.decode("utf-8", errors="replace")[:500]
return error_bytes.decode("utf-8", errors="replace")
else:
return (
e.response.text[:500] if hasattr(e.response, "_content") else "Unable to read"
e.response.text if hasattr(e.response, "_content") else "Unable to read"
)
except Exception as decode_error:
return f"Unable to read error: {decode_error}"

View File

@@ -35,6 +35,7 @@ from src.api.handlers.base.parsers import get_parser_for_format
from src.api.handlers.base.request_builder import PassthroughRequestBuilder
from src.api.handlers.base.stream_context import StreamContext
from src.api.handlers.base.utils import build_sse_headers
from src.core.error_utils import extract_error_message
# 直接从具体模块导入,避免循环依赖
from src.api.handlers.base.response_parser import (
@@ -488,6 +489,8 @@ class CliMessageHandlerBase(BaseMessageHandler):
error_text = await self._extract_error_text(e)
logger.error(f"Provider 返回错误状态: {e.response.status_code}\n Response: {error_text}")
await http_client.aclose()
# 将上游错误信息附加到异常,以便故障转移时能够返回给客户端
e.upstream_response = error_text # type: ignore[attr-defined]
raise
except EmbeddedErrorException:
@@ -1359,7 +1362,7 @@ class CliMessageHandlerBase(BaseMessageHandler):
model=ctx.model,
response_time_ms=response_time_ms,
status_code=status_code,
error_message=str(error),
error_message=extract_error_message(error),
request_headers=original_headers,
request_body=actual_request_body,
is_stream=True,
@@ -1627,7 +1630,7 @@ class CliMessageHandlerBase(BaseMessageHandler):
model=model,
response_time_ms=response_time_ms,
status_code=status_code,
error_message=str(e),
error_message=extract_error_message(e),
request_headers=original_headers,
request_body=actual_request_body,
is_stream=False,
@@ -1647,14 +1650,14 @@ class CliMessageHandlerBase(BaseMessageHandler):
for encoding in ["utf-8", "gbk", "latin1"]:
try:
return error_bytes.decode(encoding)[:500]
return error_bytes.decode(encoding)
except (UnicodeDecodeError, LookupError):
continue
return error_bytes.decode("utf-8", errors="replace")[:500]
return error_bytes.decode("utf-8", errors="replace")
else:
return (
e.response.text[:500]
e.response.text
if hasattr(e.response, "_content")
else "Unable to read response"
)