feat(failover): 支持 Provider 级别故障转移规则,默认全部转移策略

- 新增 failover_rules 配置:支持 success_failover_patterns(成功响应匹配时转移)
  和 error_stop_patterns(错误响应匹配时终止),支持按 status_code 过滤
- 修改默认转移策略:ErrorClassifier 不再返回 RAISE,所有错误默认继续转移
- TaskService 中客户端错误不再直接抛出,改为 break 继续尝试下一个候选
- 修复 proxy tunnel 连接/断连竞态:引入 per-node 锁和事件时间戳排序
- 优化 ProxyNode 状态判定:OFFLINE 统一由心跳超时判定,兼容多 worker 场景
- has_tunnel 改为纯检查方法,避免在 finally 块中误清理新注册连接
- Redis stream NOGROUP 异常自愈处理
- OAuthAccountDialog 输入框焦点样式补全
This commit is contained in:
fawney19
2026-03-01 00:21:16 +08:00
parent fbcb54a8a5
commit 005cc3e388
18 changed files with 941 additions and 116 deletions

View File

@@ -80,6 +80,53 @@ class ProxyConfig(BaseModel):
return self
class FailoverRuleItem(BaseModel):
"""故障转移规则条目"""
pattern: str = Field(..., min_length=1, max_length=500, description="正则表达式")
description: str = Field("", max_length=200, description="规则描述")
status_codes: list[int] | None = Field(
default=None,
description="HTTP 状态码列表(可选,为空时匹配所有状态码)",
)
@field_validator("pattern")
@classmethod
def validate_pattern(cls, v: str) -> str:
"""验证正则表达式语法"""
import re as _re
try:
_re.compile(v)
except _re.error as e:
raise ValueError(f"无效的正则表达式: {e}")
return v
@field_validator("status_codes")
@classmethod
def validate_status_codes(cls, v: list[int] | None) -> list[int] | None:
"""验证 HTTP 状态码"""
if v is None:
return v
for code in v:
if not (100 <= code <= 599):
raise ValueError(f"无效的 HTTP 状态码: {code}")
return v
class FailoverRulesConfig(BaseModel):
"""故障转移规则配置"""
success_failover_patterns: list[FailoverRuleItem] = Field(
default_factory=list,
description="成功响应转移规则: HTTP 200 但响应体匹配正则时触发转移",
)
error_stop_patterns: list[FailoverRuleItem] = Field(
default_factory=list,
description="错误终止规则: HTTP 非 200 且响应体匹配正则时停止转移",
)
class PoolAdvancedConfig(BaseModel):
"""通用号池配置(适用于所有 Provider 类型)。"""
@@ -247,6 +294,7 @@ class CreateProviderRequest(BaseModel):
claude_code_advanced: ClaudeCodeAdvancedConfig | None = Field(
None, description="Claude Code 特有配置"
)
failover_rules: FailoverRulesConfig | None = Field(None, description="故障转移规则配置")
config: dict[str, Any] | None = Field(None, description="其他配置")
@field_validator("provider_type")
@@ -356,6 +404,7 @@ class UpdateProviderRequest(BaseModel):
claude_code_advanced: ClaudeCodeAdvancedConfig | None = Field(
None, description="Claude Code 特有配置"
)
failover_rules: FailoverRulesConfig | None = Field(None, description="故障转移规则配置")
config: dict[str, Any] | None = None
# 复用相同的验证器

View File

@@ -10,7 +10,12 @@ from typing import Any, Literal
from pydantic import BaseModel, ConfigDict, Field, field_validator
from src.models.admin_requests import ClaudeCodeAdvancedConfig, PoolAdvancedConfig, ProxyConfig
from src.models.admin_requests import (
ClaudeCodeAdvancedConfig,
FailoverRulesConfig,
PoolAdvancedConfig,
ProxyConfig,
)
# ========== Header Rule 类型定义 ==========
# 请求头规则支持三种操作:
@@ -938,6 +943,7 @@ class ProviderUpdateRequest(BaseModel):
None, description="Claude Code 高级配置"
)
pool_advanced: PoolAdvancedConfig | None = Field(None, description="通用号池配置")
failover_rules: FailoverRulesConfig | None = Field(None, description="故障转移规则配置")
class ProviderWithEndpointsSummary(BaseModel):
@@ -982,6 +988,7 @@ class ProviderWithEndpointsSummary(BaseModel):
default=None, description="Claude Code 高级配置"
)
pool_advanced: PoolAdvancedConfig | None = Field(default=None, description="通用号池配置")
failover_rules: FailoverRulesConfig | None = Field(default=None, description="故障转移规则配置")
# Endpoint 统计
total_endpoints: int = Field(default=0, description="总 Endpoint 数量")