Files
Profeto/src/api/routes/eval.py
T

65 lines
2.5 KiB
Python

"""评估路由。"""
from __future__ import annotations
import logging
from fastapi import APIRouter, Depends, HTTPException, Query
from src.api.deps import require_admin
from src.api.schemas import EvalSummaryOut, SettleRequest
from src.db.base import AsyncSession, get_db, get_db_read
from src.llm.eval import get_eval_summary, settle_prediction
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/api/v1", tags=["eval"])
@router.post("/eval/settle", dependencies=[Depends(require_admin)])
async def settle(req: SettleRequest, db: AsyncSession = Depends(get_db)):
"""回填实际结果。
status 为 degraded/failed 的预测无法结算(返回 400);
记录不存在返回 404。
"""
try:
pred = await settle_prediction(req.prediction_id, req.home_goals, req.away_goals)
return {"id": pred.id, "settled": pred.settled}
except ValueError as e:
msg = str(e)
# degraded/failed 拒绝:明确的 400,而非与"未找到"混为一谈
if "无法结算" in msg:
logger.warning("settle rejected: %s", msg)
raise HTTPException(400, msg)
logger.warning("settle failed: %s", msg)
raise HTTPException(404, "预测记录不存在")
except Exception as e:
logger.exception("settle error")
raise HTTPException(500, "回填失败,请查看服务器日志")
@router.get("/eval/summary", response_model=EvalSummaryOut, dependencies=[Depends(require_admin)])
async def eval_summary(
limit: int = Query(1000, ge=1, le=10000, description="最大评估条数"),
provider: str | None = Query(None, description="按提供商筛选"),
model: str | None = Query(None, description="按模型筛选"),
prompt_version: str | None = Query(None, description="按 prompt 版本筛选"),
mode: str | None = Query(None, description="按模式筛选(single/multi)"),
league_code: str | None = Query(None, description="按联赛代码筛选(如 E0/SP1)"),
db: AsyncSession = Depends(get_db_read),
):
"""提供商/模型准确率对比。
P3-4: 默认评估最近 1000 条,可通过 limit 调整。
支持按 provider / model / prompt_version / mode / league_code 筛选。
只统计 status=success 且预测比分齐全的已结算预测,degraded 不计入。
"""
return await get_eval_summary(
limit=limit,
provider=provider,
model=model,
prompt_version=prompt_version,
mode=mode,
league_code=league_code,
)