mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 17:30:23 +08:00
feat(test,quota,failover): 模型并发测试、统一配额读取器与故障转移取消支持
- 新增 QuotaReader 抽象层,统一 Codex/Kiro/Antigravity 配额解析逻辑,
替换 pool/routes.py 中分散的配额构建函数
- 模型测试支持并发执行多候选,前端新增 useModelTest composable 统一
ModelsTab 和 ModelMappingTab 的测试逻辑
- ModelTestDialog 增加结果概览摘要、超长结果折叠、端点列和新状态支持,
删除已合并的 TestResultDialog
- FailoverEngine 新增客户端断开检测,支持取消剩余候选并标记记录
- 刷新配额改为分批执行,直连测试候选按可用性排序
- 修复 error 判断从 "error" in dict 改为 dict.get("error") 避免误判
This commit is contained in:
@@ -1,11 +1,17 @@
|
||||
from types import SimpleNamespace
|
||||
|
||||
import pytest
|
||||
from pydantic import ValidationError
|
||||
|
||||
from src.api.admin.provider_query import TestModelFailoverRequest as FailoverRequestModel
|
||||
from src.api.admin.provider_query import (
|
||||
_build_direct_test_candidates,
|
||||
_build_test_attempts_from_candidate_keys,
|
||||
_filter_test_candidates_by_endpoint,
|
||||
_flatten_test_candidates_for_concurrency,
|
||||
_resolve_test_effective_model,
|
||||
)
|
||||
from src.services.scheduling.schemas import PoolCandidate
|
||||
|
||||
|
||||
def _build_provider() -> tuple[SimpleNamespace, SimpleNamespace, SimpleNamespace]:
|
||||
@@ -89,3 +95,56 @@ def test_build_test_attempts_from_candidate_keys_includes_retry_index() -> None:
|
||||
assert attempts[0].retry_index == 1
|
||||
assert attempts[0].effective_model == "mapped-model"
|
||||
assert attempts[0].endpoint_api_format == "openai:chat"
|
||||
|
||||
|
||||
def test_flatten_test_candidates_for_concurrency_expands_pool_keys() -> None:
|
||||
provider, endpoint_a, _endpoint_b = _build_provider()
|
||||
pool_key_a = SimpleNamespace(
|
||||
id="pool-a",
|
||||
name="Pool A",
|
||||
auth_type="oauth",
|
||||
_pool_mapping_matched_model="mapped-a",
|
||||
)
|
||||
pool_key_b = SimpleNamespace(
|
||||
id="pool-b",
|
||||
name="Pool B",
|
||||
auth_type="oauth",
|
||||
_pool_skipped=True,
|
||||
_pool_skip_reason="quota_exhausted",
|
||||
)
|
||||
candidate = PoolCandidate(
|
||||
provider=provider, # type: ignore[arg-type]
|
||||
endpoint=endpoint_a, # type: ignore[arg-type]
|
||||
key=pool_key_a, # type: ignore[arg-type]
|
||||
pool_keys=[pool_key_a, pool_key_b], # type: ignore[list-item]
|
||||
is_cached=True,
|
||||
provider_api_format="openai:chat",
|
||||
)
|
||||
|
||||
flattened = _flatten_test_candidates_for_concurrency([candidate])
|
||||
|
||||
assert len(flattened) == 2
|
||||
assert flattened[0].key.id == "pool-a"
|
||||
assert flattened[0].mapping_matched_model == "mapped-a"
|
||||
assert flattened[0].is_skipped is False
|
||||
assert flattened[1].key.id == "pool-b"
|
||||
assert flattened[1].is_skipped is True
|
||||
assert flattened[1].skip_reason == "quota_exhausted"
|
||||
|
||||
|
||||
def test_test_model_failover_request_validates_concurrency_range() -> None:
|
||||
ok = FailoverRequestModel(
|
||||
provider_id="p1",
|
||||
mode="global",
|
||||
model_name="gpt-4o-mini",
|
||||
concurrency=5,
|
||||
)
|
||||
assert ok.concurrency == 5
|
||||
|
||||
with pytest.raises(ValidationError):
|
||||
FailoverRequestModel(
|
||||
provider_id="p1",
|
||||
mode="global",
|
||||
model_name="gpt-4o-mini",
|
||||
concurrency=0,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user