AgentEvalTool/backend/agenteval/web/routers/model_configs.py
sinohqb 2e3c00bc69
All checks were successful
CI / test (pull_request) Successful in 3m55s
feat(v1.3.1): Phase 4 场景扩充、网关延迟优化与报告 go/no-go 展示
4.1 新增三个评估场景(急诊分诊、慢病管理、健康咨询),各 3 个用例,
    全部使用无模型绑定依赖的规则;急诊场景编码 <20s 延迟验收标准
4.2 ModelGateway 由每次请求新建 httpx.AsyncClient 改为单实例共享客户端
    (复用 TCP/TLS 连接),引擎与模型连通性测试端点负责关闭;
    tutu 通道已具备同等优化,无需改动
4.3 Reports.tsx 单次报告顶部新增上线评估横幅:go/no-go/conditional
    三态 banner + 各验收标准达标情况标签

版本号升至 1.3.1(v1.3.1-final)。

门禁:pytest tests/unit 709 passed;ruff 全绿;
前端 tsc --noEmit + vitest 232 passed。

附带修复 RunList 测试时区缺陷:started_at 用 UTC 日期构造,
本地 00:00-08:00 之间会被默认"今天"过滤器排除导致误报失败。
2026-08-26 02:15:18 +08:00

129 lines
4.5 KiB
Python

"""API routes for centralized model configurations."""
from datetime import datetime, timezone
from fastapi import APIRouter, Depends, HTTPException, Query
from sqlmodel import Session
from agenteval.model_gateway import ModelGateway, ModelGatewayError
from agenteval.models import ModelCapability
from agenteval.services.model_configs import (
ModelConfigError,
ModelConfigInUseError,
ModelConfigNotFoundError,
ModelConfigService,
)
from agenteval.storage.db import ScenarioDB
from agenteval.web.deps import get_db
from agenteval.web.model_config_schemas import (
ModelConfigCreate,
ModelConfigReference,
ModelConfigResponse,
ModelConfigUpdate,
ModelConnectionTestResponse,
)
router = APIRouter()
def _http_error(exc: ModelConfigError) -> HTTPException:
if isinstance(exc, ModelConfigNotFoundError):
return HTTPException(status_code=404, detail=str(exc))
if isinstance(exc, ModelConfigInUseError):
return HTTPException(status_code=409, detail=str(exc))
return HTTPException(status_code=400, detail=str(exc))
@router.get("", response_model=list[ModelConfigResponse])
def list_model_configs(
capability: ModelCapability | None = Query(default=None),
enabled: bool | None = Query(default=None),
session: Session = Depends(get_db),
) -> list[ModelConfigResponse]:
configs = ModelConfigService(session).repo.list_all(
capability=capability.value if capability else None,
enabled=enabled,
)
return [ModelConfigResponse.from_db(config) for config in configs]
@router.post("", response_model=ModelConfigResponse)
def create_model_config(payload: ModelConfigCreate, session: Session = Depends(get_db)) -> ModelConfigResponse:
try:
config = ModelConfigService(session).create(**payload.model_dump(mode="json"))
except ModelConfigError as exc:
raise _http_error(exc) from exc
return ModelConfigResponse.from_db(config)
@router.get("/{config_id}", response_model=ModelConfigResponse)
def get_model_config(config_id: str, session: Session = Depends(get_db)) -> ModelConfigResponse:
try:
config = ModelConfigService(session).require(config_id)
except ModelConfigError as exc:
raise _http_error(exc) from exc
return ModelConfigResponse.from_db(config)
@router.put("/{config_id}", response_model=ModelConfigResponse)
def update_model_config(
config_id: str,
payload: ModelConfigUpdate,
session: Session = Depends(get_db),
) -> ModelConfigResponse:
try:
config = ModelConfigService(session).update(config_id, **payload.model_dump(mode="json"))
except ModelConfigError as exc:
raise _http_error(exc) from exc
return ModelConfigResponse.from_db(config)
@router.delete("/{config_id}")
def delete_model_config(config_id: str, session: Session = Depends(get_db)) -> dict[str, bool]:
try:
ModelConfigService(session).delete(config_id)
except ModelConfigError as exc:
raise _http_error(exc) from exc
return {"ok": True}
@router.get("/{config_id}/references", response_model=list[ModelConfigReference])
def list_model_config_references(
config_id: str,
session: Session = Depends(get_db),
) -> list[ModelConfigReference]:
service = ModelConfigService(session)
try:
service.require(config_id)
except ModelConfigError as exc:
raise _http_error(exc) from exc
references = []
for binding in service.repo.list_references(config_id):
scenario = session.get(ScenarioDB, binding.scenario_id)
references.append(
ModelConfigReference(
scenario_id=binding.scenario_id,
scenario_name=scenario.name if scenario else binding.scenario_id,
purpose=binding.purpose,
)
)
return references
@router.post("/{config_id}/test", response_model=ModelConnectionTestResponse)
async def test_model_config(
config_id: str,
session: Session = Depends(get_db),
) -> ModelConnectionTestResponse:
gateway = ModelGateway()
try:
runtime = ModelConfigService(session).resolve(config_id)
message = await gateway.test_connection(runtime)
except ModelConfigError as exc:
raise _http_error(exc) from exc
except ModelGatewayError as exc:
return ModelConnectionTestResponse(ok=False, message=str(exc), tested_at=datetime.now(timezone.utc))
finally:
await gateway.close()
return ModelConnectionTestResponse(ok=True, message=message, tested_at=datetime.now(timezone.utc))