feat(orchestration): 增加 AWS 账号被暂停 (suspended) 的错误识别和自动停用处理

- ErrorClassifier 新增 403 suspended 状态检测,归类为 ProviderAuthException
- ErrorHandlerService 新增 _is_account_suspended 静态方法,匹配多种 suspended 错误文本
- 403 suspended 的 OAuth key 自动标记为账号异常并停用
- 重构 _mark_oauth_key_blocked 支持自定义 reason 参数
- 将 _is_account_validation_required 调用改为静态方法调用
This commit is contained in:
fawney19
2026-02-26 11:32:31 +08:00
parent f41bfecf0b
commit 560345a889
2 changed files with 48 additions and 4 deletions

View File

@@ -460,10 +460,18 @@ class ErrorClassifier:
# 403: 检查是否为 Google VALIDATION_REQUIRED账号需要手动验证
# 这类错误是永久性的,重试同一个 key 无意义,应视为认证错误
if status == 403 and self._is_account_validation_required(error_response_text):
if status == 403 and ErrorHandlerService._is_account_validation_required(
error_response_text
):
logger.warning("检测到 Google 账号验证要求 (VALIDATION_REQUIRED): {}", provider_name)
return ProviderAuthException(provider_name=provider_name)
# 403: 检查是否为 AWS 账号被暂停suspended
# 账号被封禁后所有请求都会返回 403重试同一个 key 无意义
if status == 403 and ErrorHandlerService._is_account_suspended(error_response_text):
logger.warning("检测到 AWS 账号被暂停 (suspended): {}", provider_name)
return ProviderAuthException(provider_name=provider_name)
if status == 429:
return ProviderRateLimitException(
message="请求过于频繁,请稍后重试",

View File

@@ -8,6 +8,7 @@
from __future__ import annotations
import json
import re
from typing import Any
import httpx
@@ -168,6 +169,14 @@ class ErrorHandlerService:
and self._is_account_validation_required(error_response_text)
):
self._mark_oauth_key_blocked(key, request_id)
# 403 suspended -> 标记 OAuth key 为账号被暂停
elif (
status_code == 403
and key
and str(getattr(key, "auth_type", "") or "").lower() == "oauth"
and self._is_account_suspended(error_response_text)
):
self._mark_oauth_key_blocked(key, request_id, reason="AWS 账号被暂停")
return
# 限流错误
@@ -310,7 +319,33 @@ class ErrorHandlerService:
return True
return False
def _mark_oauth_key_blocked(self, key: ProviderAPIKey, request_id: str | None) -> None:
@staticmethod
def _is_account_suspended(error_text: str | None) -> bool:
"""
检测 403 错误是否为 AWS 账号被暂停 (suspended)
匹配条件(满足任一即可):
- 错误文本包含 "temporarily is suspended""temporarily suspended"
- 错误文本包含 "AccountSuspendedException"
- 错误文本匹配 User ID ... suspended 模式
"""
if not error_text:
return False
search_text = error_text.lower()
if "temporarily is suspended" in search_text or "temporarily suspended" in search_text:
return True
if "accountsuspendedexception" in search_text:
return True
if re.search(r"user\s*id.*suspend", search_text):
return True
return False
def _mark_oauth_key_blocked(
self,
key: ProviderAPIKey,
request_id: str | None,
reason: str = "Google 要求验证账号",
) -> None:
"""标记 OAuth key 为账号级别封禁"""
try:
from datetime import datetime, timezone
@@ -318,13 +353,14 @@ class ErrorHandlerService:
from src.services.provider.oauth_token import OAUTH_ACCOUNT_BLOCK_PREFIX
key.oauth_invalid_at = datetime.now(timezone.utc)
key.oauth_invalid_reason = f"{OAUTH_ACCOUNT_BLOCK_PREFIX}Google 要求验证账号"
key.oauth_invalid_reason = f"{OAUTH_ACCOUNT_BLOCK_PREFIX}{reason}"
key.is_active = False
self.db.commit()
logger.warning(
" [{}] {}403 VALIDATION_REQUIRED 已标记为账号异常并自动停用",
" [{}] {}{} 已标记为账号异常并自动停用",
request_id,
self._format_key_display(key),
reason,
)
except Exception as mark_exc:
logger.debug(" [{}] 标记 oauth_invalid 失败: {}", request_id, mark_exc)