fix(gateway): 适配器瞬时错误消息脱敏——只留类名+状态码不泄漏厂商响应体(CR-M2-2)
This commit is contained in:
@@ -22,6 +22,7 @@ from .base import (
|
||||
ProviderUsage,
|
||||
StreamChunk,
|
||||
is_transient_by_name,
|
||||
provider_error_summary,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@@ -131,7 +132,9 @@ class AnthropicAdapter:
|
||||
return await self._complete_text(req, model)
|
||||
except Exception as exc:
|
||||
if _is_transient(exc):
|
||||
raise TransientProviderError(str(exc), provider=self.provider) from exc
|
||||
raise TransientProviderError(
|
||||
provider_error_summary(exc), provider=self.provider
|
||||
) from exc
|
||||
raise
|
||||
|
||||
async def _complete_text(self, req: LlmRequest, model: str) -> ProviderResult:
|
||||
@@ -187,5 +190,7 @@ class AnthropicAdapter:
|
||||
yield StreamChunk(usage=_usage_from(usage))
|
||||
except Exception as exc:
|
||||
if _is_transient(exc):
|
||||
raise TransientProviderError(str(exc), provider=self.provider) from exc
|
||||
raise TransientProviderError(
|
||||
provider_error_summary(exc), provider=self.provider
|
||||
) from exc
|
||||
raise
|
||||
|
||||
@@ -23,6 +23,22 @@ def _is_transient_status(status: object) -> bool:
|
||||
)
|
||||
|
||||
|
||||
def provider_error_summary(exc: Exception) -> str:
|
||||
"""脱敏摘要:只含异常类名 + 可选状态码,绝不含原始厂商 HTTP 响应体(CR-M2-2)。
|
||||
|
||||
厂商异常的 `str(exc)` 常内嵌完整响应体(可能含请求内容/内部细节),不应进我们
|
||||
构造并上抛的错误消息(防泄漏,§9.3)。完整细节仍可留在服务端日志(若已记)。
|
||||
状态码优先取 `status_code`,回退取 `code`(Gemini 部分错误用 `exc.code`)。
|
||||
"""
|
||||
name = type(exc).__name__
|
||||
status = getattr(exc, "status_code", None)
|
||||
if not isinstance(status, int):
|
||||
status = getattr(exc, "code", None)
|
||||
if isinstance(status, int):
|
||||
return f"{name} (status={status})"
|
||||
return name
|
||||
|
||||
|
||||
def is_transient_by_name(
|
||||
exc: Exception, names: frozenset[str], *, extra_codes: bool = False
|
||||
) -> bool:
|
||||
|
||||
@@ -20,6 +20,7 @@ from .base import (
|
||||
ProviderUsage,
|
||||
StreamChunk,
|
||||
is_transient_by_name,
|
||||
provider_error_summary,
|
||||
)
|
||||
|
||||
_TRANSIENT_NAMES = frozenset(
|
||||
@@ -103,7 +104,9 @@ class GeminiAdapter:
|
||||
)
|
||||
except Exception as exc:
|
||||
if _is_transient(exc):
|
||||
raise TransientProviderError(str(exc), provider=self.provider) from exc
|
||||
raise TransientProviderError(
|
||||
provider_error_summary(exc), provider=self.provider
|
||||
) from exc
|
||||
raise
|
||||
text = getattr(resp, "text", "") or ""
|
||||
usage = _usage_from(getattr(resp, "usage_metadata", None))
|
||||
@@ -126,5 +129,7 @@ class GeminiAdapter:
|
||||
yield StreamChunk(usage=_usage_from(meta))
|
||||
except Exception as exc:
|
||||
if _is_transient(exc):
|
||||
raise TransientProviderError(str(exc), provider=self.provider) from exc
|
||||
raise TransientProviderError(
|
||||
provider_error_summary(exc), provider=self.provider
|
||||
) from exc
|
||||
raise
|
||||
|
||||
@@ -21,6 +21,7 @@ from .base import (
|
||||
ProviderUsage,
|
||||
StreamChunk,
|
||||
is_transient_by_name,
|
||||
provider_error_summary,
|
||||
)
|
||||
|
||||
# OpenAI 兼容 SDK 的瞬时错误类名(按名匹配,覆盖 DeepSeek/Kimi/Qwen/GLM 等共用 SDK)。
|
||||
@@ -115,7 +116,9 @@ class OpenAICompatAdapter:
|
||||
return await self._complete_text(req, model)
|
||||
except Exception as exc:
|
||||
if _is_transient(exc):
|
||||
raise TransientProviderError(str(exc), provider=self.provider) from exc
|
||||
raise TransientProviderError(
|
||||
provider_error_summary(exc), provider=self.provider
|
||||
) from exc
|
||||
raise
|
||||
|
||||
async def _complete_text(self, req: LlmRequest, model: str) -> ProviderResult:
|
||||
@@ -158,5 +161,7 @@ class OpenAICompatAdapter:
|
||||
yield StreamChunk(usage=_usage_from(chunk.usage))
|
||||
except Exception as exc:
|
||||
if _is_transient(exc):
|
||||
raise TransientProviderError(str(exc), provider=self.provider) from exc
|
||||
raise TransientProviderError(
|
||||
provider_error_summary(exc), provider=self.provider
|
||||
) from exc
|
||||
raise
|
||||
|
||||
Reference in New Issue
Block a user