fix(gateway): 熔断计入持续 4xx + 去 assert + Protocol/transient 去重
P1-2 非可重试错误(持续 401/403)也 record_failure,坏 key 可触发熔断。 P1-9 _complete_structured 的 assert 改显式 raise ValueError(-O 安全)。 P2 GatewayRun 抽到 orchestrator/_protocols.py 单点(去 4 处重复); _is_transient 抽到 adapters/base.py is_transient_by_name(去 3 处重复); Gemini Protocol 改 async def;gateway._retrying 去无用 async。
This commit is contained in:
@@ -12,6 +12,34 @@ from pydantic import BaseModel, ConfigDict
|
||||
|
||||
from ..types import LlmRequest
|
||||
|
||||
# 瞬时(可退避重试)的 HTTP 状态码判定:429 限流 或 5xx 服务端错误。
|
||||
_RATE_LIMITED_STATUS = 429
|
||||
_SERVER_ERROR_MIN_STATUS = 500
|
||||
|
||||
|
||||
def _is_transient_status(status: object) -> bool:
|
||||
return isinstance(status, int) and (
|
||||
status == _RATE_LIMITED_STATUS or status >= _SERVER_ERROR_MIN_STATUS
|
||||
)
|
||||
|
||||
|
||||
def is_transient_by_name(
|
||||
exc: Exception, names: frozenset[str], *, extra_codes: bool = False
|
||||
) -> bool:
|
||||
"""统一的瞬时错误判定(DRY:三个适配器共用)。
|
||||
|
||||
① 异常类名命中 `names`(按名匹配,避免硬依赖各厂商 SDK 异常类型);或
|
||||
② `status_code` 属性是 429/5xx;或
|
||||
③ `extra_codes=True` 时额外检查 `code` 属性是否为 429/5xx(Gemini 用 `exc.code`)。
|
||||
"""
|
||||
if type(exc).__name__ in names:
|
||||
return True
|
||||
if _is_transient_status(getattr(exc, "status_code", None)):
|
||||
return True
|
||||
if extra_codes and _is_transient_status(getattr(exc, "code", None)):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
class Capabilities(BaseModel):
|
||||
structured_output: bool = False
|
||||
|
||||
Reference in New Issue
Block a user