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:
@@ -14,7 +14,13 @@ from typing import Any, Protocol
|
||||
|
||||
from ..errors import TransientProviderError
|
||||
from ..types import LlmRequest
|
||||
from .base import Capabilities, ProviderResult, ProviderUsage, StreamChunk
|
||||
from .base import (
|
||||
Capabilities,
|
||||
ProviderResult,
|
||||
ProviderUsage,
|
||||
StreamChunk,
|
||||
is_transient_by_name,
|
||||
)
|
||||
|
||||
_TRANSIENT_NAMES = frozenset(
|
||||
{
|
||||
@@ -31,7 +37,7 @@ _TRANSIENT_NAMES = frozenset(
|
||||
class GeminiModels(Protocol):
|
||||
async def generate_content(self, **kwargs: Any) -> Any: ...
|
||||
|
||||
def generate_content_stream(self, **kwargs: Any) -> Any: ...
|
||||
async def generate_content_stream(self, **kwargs: Any) -> Any: ...
|
||||
|
||||
|
||||
class GeminiAio(Protocol):
|
||||
@@ -45,11 +51,8 @@ class GeminiClient(Protocol):
|
||||
|
||||
|
||||
def _is_transient(exc: Exception) -> bool:
|
||||
name = type(exc).__name__
|
||||
if name in _TRANSIENT_NAMES:
|
||||
return True
|
||||
status = getattr(exc, "code", None) or getattr(exc, "status_code", None)
|
||||
return isinstance(status, int) and (status == 429 or status >= 500)
|
||||
# Gemini SDK 部分错误用 `exc.code` 携带状态码 → extra_codes=True 一并检查。
|
||||
return is_transient_by_name(exc, _TRANSIENT_NAMES, extra_codes=True)
|
||||
|
||||
|
||||
def _contents(req: LlmRequest) -> str:
|
||||
|
||||
Reference in New Issue
Block a user