feat(pool,admin): 拆分调度维度、重构调度 UI 与增强配置导入导出

调度维度:
- 新增 cache_affinity/free_first/team_first/plus_first/load_balance 五个独立维度
- 将 free_team_first 标记为 hidden,保留后向兼容但不再显示
- Registry 新增 hidden 属性,_helpers 新增 plus_only 优先级评分

前端调度对话框:
- 分配模式(互斥组)独立为按钮组选择,策略调度保留拖拽排序
- 默认调度从 LRU 轮转改为缓存亲和
- 提取 buildPresetListItem/insertMissingByPreferredOrder 消除重复代码

配置导入导出:
- 导出时 api_formats 支持规范化、去重与 None 回退到 Provider 端点
- 导入时兼容 supported_endpoints 别名与历史 None 语义
- 新增 test_admin_system_key_formats 单元测试
This commit is contained in:
fawney19
2026-03-05 23:22:25 +08:00
parent da915208a8
commit 228cbc8f87
12 changed files with 667 additions and 229 deletions

View File

@@ -5,14 +5,19 @@ Importing this package registers all built-in preset dimensions.
from __future__ import annotations
from . import cache_affinity # noqa: F401
from . import cost_first # noqa: F401
from . import free_first # noqa: F401
from . import free_team_first # noqa: F401
from . import health_first # noqa: F401
from . import latency_first # noqa: F401
from . import load_balance # noqa: F401
from . import plus_first # noqa: F401
from . import priority_first # noqa: F401
from . import quota_balanced # noqa: F401
from . import recent_refresh # noqa: F401
from . import single_account # noqa: F401
from . import team_first # noqa: F401
from .registry import (
PresetDimensionBase,
PresetDimensionMeta,

View File

@@ -219,7 +219,10 @@ def extract_health_score(key_obj: Any) -> float | None:
def plan_priority_score(plan_type: str | None, mode: str | None = None) -> float:
"""Score a key based on plan type and free_team_first mode."""
"""Score a key based on plan type and scheduling mode.
Lower score = higher priority.
"""
effective_mode = (mode or "both").strip().lower()
if effective_mode == "free_only":
@@ -232,6 +235,11 @@ def plan_priority_score(plan_type: str | None, mode: str | None = None) -> float
return 0.0
if plan_type == "free":
return 0.5
elif effective_mode == "plus_only":
if plan_type in {"plus", "pro"}:
return 0.0
if plan_type in {"enterprise", "business"}:
return 0.3
else:
# "both" or unrecognized -> original behavior
if plan_type in {"free", "team"}:

View File

@@ -0,0 +1,45 @@
"""cache_affinity preset dimension."""
from __future__ import annotations
from typing import Any
from ._helpers import rank_descending
from .registry import PresetDimensionBase, register_preset_dimension
class CacheAffinityDimension(PresetDimensionBase):
@property
def name(self) -> str:
return "cache_affinity"
@property
def label(self) -> str:
return "缓存亲和"
@property
def description(self) -> str:
return "优先复用最近使用过的 Key利用 Prompt Caching"
@property
def mutex_group(self) -> str | None:
return "distribution_mode"
@property
def evidence_hint(self) -> str | None:
return "依据 LRU 时间戳(最近使用优先,与 LRU 轮转相反)"
def compute_metric(
self,
*,
key_id: str,
all_key_ids: list[str],
keys_by_id: dict[str, Any],
lru_scores: dict[str, Any],
context: dict[str, Any],
mode: str | None,
) -> float:
return rank_descending(key_id, lru_scores, all_key_ids)
register_preset_dimension(CacheAffinityDimension())

View File

@@ -0,0 +1,51 @@
"""free_first preset dimension."""
from __future__ import annotations
from typing import Any
from ._helpers import extract_plan_type, plan_priority_score, rank_ascending
from .registry import PresetDimensionBase, register_preset_dimension
class FreeFirstDimension(PresetDimensionBase):
@property
def name(self) -> str:
return "free_first"
@property
def label(self) -> str:
return "Free 优先"
@property
def description(self) -> str:
return "优先消耗 Free 账号(依赖 plan_type"
@property
def evidence_hint(self) -> str | None:
return "依据 plan_typeFree 账号优先调度)"
@property
def providers(self) -> tuple[str, ...]:
return ("codex", "kiro")
def compute_metric(
self,
*,
key_id: str,
all_key_ids: list[str],
keys_by_id: dict[str, Any],
lru_scores: dict[str, Any],
context: dict[str, Any],
mode: str | None,
) -> float:
plan_scores = {
kid: plan_priority_score(extract_plan_type(keys_by_id.get(kid)), "free_only")
for kid in all_key_ids
}
if len(set(plan_scores.values())) <= 1:
return rank_ascending(key_id, lru_scores, all_key_ids)
return rank_ascending(key_id, plan_scores, all_key_ids)
register_preset_dimension(FreeFirstDimension())

View File

@@ -37,6 +37,10 @@ class FreeTeamFirstDimension(PresetDimensionBase):
def default_mode(self) -> str | None:
return "both"
@property
def hidden(self) -> bool:
return True
def compute_metric(
self,
*,

View File

@@ -0,0 +1,45 @@
"""load_balance preset dimension."""
from __future__ import annotations
import random
from typing import Any
from .registry import PresetDimensionBase, register_preset_dimension
class LoadBalanceDimension(PresetDimensionBase):
@property
def name(self) -> str:
return "load_balance"
@property
def label(self) -> str:
return "负载均衡"
@property
def description(self) -> str:
return "随机分散 Key 使用,均匀分摊负载"
@property
def mutex_group(self) -> str | None:
return "distribution_mode"
@property
def evidence_hint(self) -> str | None:
return "每次随机分值,实现完全均匀分散"
def compute_metric(
self,
*,
key_id: str,
all_key_ids: list[str],
keys_by_id: dict[str, Any],
lru_scores: dict[str, Any],
context: dict[str, Any],
mode: str | None,
) -> float:
return random.random()
register_preset_dimension(LoadBalanceDimension())

View File

@@ -0,0 +1,51 @@
"""plus_first preset dimension."""
from __future__ import annotations
from typing import Any
from ._helpers import extract_plan_type, plan_priority_score, rank_ascending
from .registry import PresetDimensionBase, register_preset_dimension
class PlusFirstDimension(PresetDimensionBase):
@property
def name(self) -> str:
return "plus_first"
@property
def label(self) -> str:
return "Plus 优先"
@property
def description(self) -> str:
return "优先消耗 Plus/Pro 账号(依赖 plan_type"
@property
def evidence_hint(self) -> str | None:
return "依据 plan_typePlus/Pro 账号优先调度)"
@property
def providers(self) -> tuple[str, ...]:
return ("codex", "kiro")
def compute_metric(
self,
*,
key_id: str,
all_key_ids: list[str],
keys_by_id: dict[str, Any],
lru_scores: dict[str, Any],
context: dict[str, Any],
mode: str | None,
) -> float:
plan_scores = {
kid: plan_priority_score(extract_plan_type(keys_by_id.get(kid)), "plus_only")
for kid in all_key_ids
}
if len(set(plan_scores.values())) <= 1:
return rank_ascending(key_id, lru_scores, all_key_ids)
return rank_ascending(key_id, plan_scores, all_key_ids)
register_preset_dimension(PlusFirstDimension())

View File

@@ -76,6 +76,16 @@ class PresetDimensionBase(ABC):
return None
@property
def hidden(self) -> bool:
"""If True, this dimension is excluded from API metadata listings.
The dimension remains functional for backward compatibility but
will not appear in the scheduling dialog.
"""
return False
@abstractmethod
def compute_metric(
self,
@@ -170,6 +180,10 @@ def register_preset_dimension(dim: PresetDimensionBase) -> None:
raw = str(self._wrapped.evidence_hint or "").strip()
return raw or None
@property
def hidden(self) -> bool:
return self._wrapped.hidden
def compute_metric(
self,
*,
@@ -223,6 +237,8 @@ def get_preset_dimension_metas() -> list[PresetDimensionMeta]:
metas: list[PresetDimensionMeta] = []
for dim in get_all_preset_dimensions():
if dim.hidden:
continue
metas.append(
PresetDimensionMeta(
name=dim.name,

View File

@@ -0,0 +1,51 @@
"""team_first preset dimension."""
from __future__ import annotations
from typing import Any
from ._helpers import extract_plan_type, plan_priority_score, rank_ascending
from .registry import PresetDimensionBase, register_preset_dimension
class TeamFirstDimension(PresetDimensionBase):
@property
def name(self) -> str:
return "team_first"
@property
def label(self) -> str:
return "Team 优先"
@property
def description(self) -> str:
return "优先消耗 Team 账号(依赖 plan_type"
@property
def evidence_hint(self) -> str | None:
return "依据 plan_typeTeam 账号优先调度)"
@property
def providers(self) -> tuple[str, ...]:
return ("codex", "kiro")
def compute_metric(
self,
*,
key_id: str,
all_key_ids: list[str],
keys_by_id: dict[str, Any],
lru_scores: dict[str, Any],
context: dict[str, Any],
mode: str | None,
) -> float:
plan_scores = {
kid: plan_priority_score(extract_plan_type(keys_by_id.get(kid)), "team_only")
for kid in all_key_ids
}
if len(set(plan_scores.values())) <= 1:
return rank_ascending(key_id, lru_scores, all_key_ids)
return rank_ascending(key_id, plan_scores, all_key_ids)
register_preset_dimension(TeamFirstDimension())