feat(ui): 优化熔断状态显示和健康度格式

- 健康度格式统一为 0-1 小数格式(前后端同步)
- 添加熔断倒计时显示,支持实时更新
- 新增 useCountdownTimer composable 用于倒计时逻辑
- 添加 Key 健康恢复功能(按 API 格式恢复)
- 优化 UI 布局:简化链路预览标题、调整 Key 信息为两行显示
- 修复错误处理顺序(先刷新数据再显示成功提示)
- 减少模板中函数重复调用
This commit is contained in:
fawney19
2026-01-13 18:42:15 +08:00
parent e536b7bc35
commit 1a0a3de2fc
6 changed files with 317 additions and 208 deletions

View File

@@ -47,7 +47,7 @@ class RoutingKeyInfo(BaseModel):
is_adaptive: bool = Field(False, description="是否为自适应 RPM 模式")
effective_rpm: Optional[int] = Field(None, description="有效 RPM 限制")
cache_ttl_minutes: int = Field(0, description="缓存 TTL分钟")
health_score: float = Field(100.0, description="健康度分数")
health_score: float = Field(1.0, description="健康度分数0-1 小数格式)")
is_active: bool
api_formats: List[str] = Field(default_factory=list, description="支持的 API 格式")
# 模型白名单
@@ -55,6 +55,7 @@ class RoutingKeyInfo(BaseModel):
# 熔断状态
circuit_breaker_open: bool = Field(False, description="熔断器是否打开")
circuit_breaker_formats: List[str] = Field(default_factory=list, description="熔断的 API 格式列表")
next_probe_at: Optional[str] = Field(None, description="下次探测时间ISO格式")
model_config = ConfigDict(from_attributes=True)
@@ -272,11 +273,11 @@ class AdminGetModelRoutingPreviewAdapter(AdminApiAdapter):
if is_adaptive and key.learned_rpm_limit:
effective_rpm = key.learned_rpm_limit
# 从 health_by_format 获取健康度
health_score = 100.0
# 从 health_by_format 获取健康度0-1 小数格式)
health_score = 1.0
if key.health_by_format and ep.api_format:
format_health = key.health_by_format.get(ep.api_format, {})
health_score = format_health.get("health_score", 100.0)
health_score = format_health.get("health_score", 1.0)
# 生成脱敏 SK先解密再脱敏
masked_key = ""
@@ -296,11 +297,17 @@ class AdminGetModelRoutingPreviewAdapter(AdminApiAdapter):
# 检查熔断状态
circuit_breaker_open = False
circuit_breaker_formats: List[str] = []
next_probe_at: Optional[str] = None
if key.circuit_breaker_by_format:
for fmt, cb_state in key.circuit_breaker_by_format.items():
if isinstance(cb_state, dict) and cb_state.get("open"):
circuit_breaker_open = True
circuit_breaker_formats.append(fmt)
# 取最早的探测时间
fmt_next_probe = cb_state.get("next_probe_at")
if fmt_next_probe:
if next_probe_at is None or fmt_next_probe < next_probe_at:
next_probe_at = fmt_next_probe
# 解析 allowed_models
# 语义说明:
@@ -334,6 +341,7 @@ class AdminGetModelRoutingPreviewAdapter(AdminApiAdapter):
allowed_models=allowed_models_list,
circuit_breaker_open=circuit_breaker_open,
circuit_breaker_formats=circuit_breaker_formats,
next_probe_at=next_probe_at,
)
)