From 6948a61b31667e92a331c2058b0cec6e9e2128f7 Mon Sep 17 00:00:00 2001 From: ThreeFish Date: Wed, 8 Apr 2026 14:37:54 +0800 Subject: [PATCH] =?UTF-8?q?fix(failover):=20=E5=B0=86=20HTTP=20529=20(over?= =?UTF-8?q?loaded=5Ferror)=20=E7=BA=B3=E5=85=A5=E9=99=8D=E7=BA=A7=E8=A7=A6?= =?UTF-8?q?=E5=8F=91=E6=9D=A1=E4=BB=B6;?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Anthropic API 过载时返回 529,但该状态码未被纳入 FailoverConfig.status_codes, 导致请求被原样透传给客户端而未触发向下级 vendor 的降级。 - FailoverConfig 默认 status_codes 添加 529 - config.default.yaml 配置模板同步添加 529 - BaseVendor.should_trigger_failover 备用安全网逻辑添加 529 - 新增 3 个 529 降级相关单元测试 🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist) Co-Authored-By: Aurelius Huang --- src/coding/proxy/config/config.default.yaml | 2 +- src/coding/proxy/config/resiliency.py | 2 +- src/coding/proxy/vendors/base.py | 4 ++-- tests/test_vendors.py | 25 +++++++++++++++++++++ 4 files changed, 29 insertions(+), 4 deletions(-) diff --git a/src/coding/proxy/config/config.default.yaml b/src/coding/proxy/config/config.default.yaml index 9e43ba7..05cbf4a 100644 --- a/src/coding/proxy/config/config.default.yaml +++ b/src/coding/proxy/config/config.default.yaml @@ -118,7 +118,7 @@ vendors: # === 故障转移触发条件 === failover: - status_codes: [429, 403, 503, 500] + status_codes: [429, 403, 503, 500, 529] error_types: - "rate_limit_error" - "overloaded_error" diff --git a/src/coding/proxy/config/resiliency.py b/src/coding/proxy/config/resiliency.py index 734167c..71ba258 100644 --- a/src/coding/proxy/config/resiliency.py +++ b/src/coding/proxy/config/resiliency.py @@ -24,7 +24,7 @@ class RetryConfig(BaseModel): class FailoverConfig(BaseModel): status_codes: list[int] = Field( - default=[429, 403, 503, 500], + default=[429, 403, 503, 500, 529], ) error_types: list[str] = Field( default=["rate_limit_error", "overloaded_error", "api_error"], diff --git a/src/coding/proxy/vendors/base.py b/src/coding/proxy/vendors/base.py index ef9fa6a..975d3af 100644 --- a/src/coding/proxy/vendors/base.py +++ b/src/coding/proxy/vendors/base.py @@ -268,8 +268,8 @@ def should_trigger_failover( for pattern in self._failover_config.error_message_patterns: if pattern.lower() in error_message: return True - # 429/503 即使无法解析 body 也触发故障转移 - return status_code in (429, 503) + # 429/503/529 即使无法解析 body 也触发故障转移 + return status_code in (429, 503, 529) async def check_health(self) -> bool: """检查供应商健康状态(轻量级探测). diff --git a/tests/test_vendors.py b/tests/test_vendors.py index 97eaefd..3f2fe8c 100644 --- a/tests/test_vendors.py +++ b/tests/test_vendors.py @@ -650,6 +650,31 @@ def test_base_failover_without_config_returns_false(): assert not zhipu_vendor.should_trigger_failover(503, None) +# --- 529 overloaded_error 降级测试 --- + + +def test_529_overloaded_triggers_failover(): + """529 + overloaded_error 应触发降级(FailoverConfig 默认包含 529).""" + anthropic_vendor = AnthropicVendor(AnthropicConfig(), FailoverConfig()) + + assert anthropic_vendor.should_trigger_failover( + 529, {"error": {"type": "overloaded_error", "message": "Overloaded"}} + ) + + +def test_529_without_body_triggers_failover(): + """529 无 body 也应触发降级(备用安全网逻辑).""" + anthropic_vendor = AnthropicVendor(AnthropicConfig(), FailoverConfig()) + + assert anthropic_vendor.should_trigger_failover(529, None) + + +def test_529_in_failover_config_default(): + """验证 FailoverConfig 默认 status_codes 包含 529.""" + config = FailoverConfig() + assert 529 in config.status_codes + + # --- _sanitize_headers_for_synthetic_response ---