refactor: 将 global_priority 和 rate_multiplier 改为按 API 格式配置

- 新增 global_priority_by_format 字段,支持按 API 格式设置全局优先级
- 移除已废弃的 rate_multiplier 字段,统一使用 rate_multipliers
- 移除已废弃的 timeout 字段(providers 和 provider_endpoints 表)
- 更新调度器以支持按格式的优先级排序
- 同步更新前后端类型定义和 API 接口
- 添加数据库迁移脚本,自动迁移现有数据
This commit is contained in:
fawney19
2026-01-16 17:53:27 +08:00
parent 6c98816f9f
commit d5d74339dd
18 changed files with 247 additions and 136 deletions

View File

@@ -50,7 +50,7 @@ async def update_endpoint_key(
- `api_key`: 新的 API Key 原文
- `name`: Key 名称
- `note`: 备注
- `rate_multiplier`: 速率倍数
- `rate_multipliers`: 按 API 格式的成本倍率
- `internal_priority`: 内部优先级
- `rpm_limit`: RPM 限制(设置为 null 可切换到自适应模式)
- `allowed_models`: 允许的模型列表
@@ -82,8 +82,9 @@ async def get_keys_grouped_by_format(
- `name`: Key 名称
- `api_key_masked`: 脱敏后的 API Key
- `internal_priority`: 内部优先级
- `global_priority`: 全局优先级
- `rate_multiplier`: 速率倍数
- `global_priority_by_format`: 按 API 格式的全局优先级
- `format_priority`: 当前格式的优先级
- `rate_multipliers`: 按 API 格式的成本倍率
- `is_active`: 是否活跃
- `circuit_breaker_open`: 熔断器状态
- `provider_name`: Provider 名称
@@ -367,7 +368,6 @@ class AdminGetKeysGroupedByFormatAdapter(AdminApiAdapter):
Provider.is_active.is_(True),
)
.order_by(
ProviderAPIKey.global_priority.asc().nullslast(),
ProviderAPIKey.internal_priority.asc(),
)
.all()
@@ -427,8 +427,8 @@ class AdminGetKeysGroupedByFormatAdapter(AdminApiAdapter):
"name": key.name,
"api_key_masked": masked_key,
"internal_priority": key.internal_priority,
"global_priority": key.global_priority,
"rate_multiplier": key.rate_multiplier,
"global_priority_by_format": key.global_priority_by_format,
"rate_multipliers": key.rate_multipliers,
"is_active": key.is_active,
"provider_name": provider.name,
"api_formats": api_formats,
@@ -438,9 +438,10 @@ class AdminGetKeysGroupedByFormatAdapter(AdminApiAdapter):
"request_count": key.request_count,
}
# 将 Key 添加到每个支持的格式分组中,并附加格式特定的健康度数据
# 将 Key 添加到每个支持的格式分组中,并附加格式特定的数据
health_by_format = key.health_by_format or {}
circuit_by_format = key.circuit_breaker_by_format or {}
priority_by_format = key.global_priority_by_format or {}
provider_id = str(provider.id)
for api_format in api_formats:
if api_format not in grouped:
@@ -451,6 +452,8 @@ class AdminGetKeysGroupedByFormatAdapter(AdminApiAdapter):
format_key_info["endpoint_base_url"] = endpoint_base_url_map.get(
(provider_id, api_format)
)
# 添加格式特定的优先级
format_key_info["format_priority"] = priority_by_format.get(api_format)
# 添加格式特定的健康度数据
format_health = health_by_format.get(api_format, {})
format_circuit = circuit_by_format.get(api_format, {})
@@ -597,8 +600,7 @@ class AdminCreateProviderKeyAdapter(AdminApiAdapter):
api_key=encrypted_key,
name=self.key_data.name,
note=self.key_data.note,
rate_multiplier=self.key_data.rate_multiplier,
rate_multipliers=self.key_data.rate_multipliers, # 按 API 格式的成本倍率
rate_multipliers=self.key_data.rate_multipliers,
internal_priority=self.key_data.internal_priority,
rpm_limit=self.key_data.rpm_limit,
allowed_models=self.key_data.allowed_models if self.key_data.allowed_models else None,

View File

@@ -47,7 +47,7 @@ class RoutingKeyInfo(BaseModel):
name: str
masked_key: str = Field("", description="脱敏的 API Key")
internal_priority: int = Field(..., description="Key 内部优先级")
global_priority: Optional[int] = Field(None, description="全局 Key 优先级")
global_priority_by_format: Optional[Dict[str, int]] = Field(None, description="按 API 格式的全局优先级")
rpm_limit: Optional[int] = Field(None, description="RPM 限制null 表示自适应")
is_adaptive: bool = Field(False, description="是否为自适应 RPM 模式")
effective_rpm: Optional[int] = Field(None, description="有效 RPM 限制")
@@ -293,8 +293,14 @@ class AdminGetModelRoutingPreviewAdapter(AdminApiAdapter):
for ep in provider_endpoints:
# 获取该 Endpoint 格式对应的 Keys
ep_keys = keys_by_endpoint.get(ep.api_format or "", [])
# 按优先级排序
ep_keys.sort(key=lambda k: (k.global_priority or 999, k.internal_priority or 0))
# 按优先级排序(使用当前格式的全局优先级)
api_format = ep.api_format or ""
def get_key_priority(k: ProviderAPIKey) -> tuple[int, int]:
format_priority = 999
if k.global_priority_by_format and api_format in k.global_priority_by_format:
format_priority = k.global_priority_by_format[api_format]
return (format_priority, k.internal_priority or 0)
ep_keys.sort(key=get_key_priority)
key_infos = []
for key in ep_keys:
@@ -353,7 +359,7 @@ class AdminGetModelRoutingPreviewAdapter(AdminApiAdapter):
name=key.name or "",
masked_key=masked_key,
internal_priority=key.internal_priority or 0,
global_priority=key.global_priority,
global_priority_by_format=key.global_priority_by_format,
rpm_limit=key.rpm_limit,
is_adaptive=is_adaptive,
effective_rpm=effective_rpm,

View File

@@ -213,12 +213,11 @@ async def list_affinities(
- `provider_id`: Provider ID
- `provider_name`: Provider 显示名称
- `endpoint_id`: Endpoint ID
- `endpoint_api_format`: Endpoint API 格式
- `endpoint_url`: Endpoint 基础 URL
- `key_id`: Key ID
- `key_name`: Key 名称
- `key_prefix`: 脱敏后的 Provider Key
- `rate_multiplier`: 速率倍数
- `rate_multipliers`: 按 API 格式的成本倍率
- `global_model_id`: GlobalModel ID
- `model_name`: 模型名称
- `model_display_name`: 模型显示名称
@@ -821,14 +820,11 @@ class AdminListAffinitiesAdapter(AdminApiAdapter):
"provider_id": provider_id,
"provider_name": provider.name if provider else None,
"endpoint_id": endpoint_id,
"endpoint_api_format": (
endpoint.api_format if endpoint and endpoint.api_format else None
),
"endpoint_url": endpoint.base_url if endpoint else None,
"key_id": key_id,
"key_name": key.name if key else None,
"key_prefix": provider_key_masked,
"rate_multiplier": key.rate_multiplier if key else 1.0,
"rate_multipliers": key.rate_multipliers if key else None,
"global_model_id": affinity.get("model_name"), # 原始的 global_model_id
"model_name": (
global_model_map.get(affinity.get("model_name")).name

View File

@@ -803,10 +803,9 @@ class AdminExportConfigAdapter(AdminApiAdapter):
"name": key.name,
"note": key.note,
"api_formats": key.api_formats or [],
"rate_multiplier": key.rate_multiplier,
"rate_multipliers": key.rate_multipliers,
"internal_priority": key.internal_priority,
"global_priority": key.global_priority,
"global_priority_by_format": key.global_priority_by_format,
"rpm_limit": key.rpm_limit,
"allowed_models": key.allowed_models,
"capabilities": key.capabilities,
@@ -1159,10 +1158,9 @@ class AdminImportConfigAdapter(AdminApiAdapter):
api_key=encrypted_key,
name=key_data.get("name") or "Imported Key",
note=key_data.get("note"),
rate_multiplier=key_data.get("rate_multiplier", 1.0),
rate_multipliers=key_data.get("rate_multipliers"),
internal_priority=key_data.get("internal_priority", 50),
global_priority=key_data.get("global_priority"),
global_priority_by_format=key_data.get("global_priority_by_format"),
rpm_limit=key_data.get("rpm_limit"),
allowed_models=key_data.get("allowed_models"),
capabilities=key_data.get("capabilities"),