fix: 后端 API 修复

- deps.py: 依赖注入优化
- matches.py: 比赛列表查询修复
- predict.py: 预测接口参数校验修复
- orchestrator.py: 多 Agent 编排逻辑修复
This commit is contained in:
shangfangjian
2026-09-20 08:45:19 +08:00
parent 8e6ad5394e
commit 7b1457f0db
4 changed files with 19 additions and 8 deletions
+6 -1
View File
@@ -187,8 +187,13 @@ async def rate_limit_predict(request: Request) -> None:
client_ip = get_client_ip(request) client_ip = get_client_ip(request)
if not _predict_limiter.is_allowed(client_ip): if not _predict_limiter.is_allowed(client_ip):
logger.warning("rate limit exceeded for %s", client_ip) logger.warning("rate limit exceeded for %s", ip)
raise HTTPException( raise HTTPException(
status_code=429, status_code=429,
detail="请求过于频繁,请稍后再试(每分钟最多 10 次)", detail="请求过于频繁,请稍后再试(每分钟最多 10 次)",
) )
def get_predict_rate_limit_remaining(request: Request) -> int:
"""查询当前 IP 剩余的预测配额(用于响应中提示前端)。"""
return _predict_limiter.remaining(get_client_ip(request))
+7 -1
View File
@@ -72,7 +72,13 @@ async def list_matches(
d = datetime.strptime(date, "%Y-%m-%d") d = datetime.strptime(date, "%Y-%m-%d")
except ValueError: except ValueError:
raise HTTPException(400, "date 格式应为 YYYY-MM-DD") raise HTTPException(400, "date 格式应为 YYYY-MM-DD")
q = q.where(Match.match_date >= d, Match.match_date < d + timedelta(days=1)) # date 是用户本地日期(默认北京 UTC+8);match_date 存 UTC,需转换:
# 本地 00:00 (UTC+8) = UTC 前一天 16:00;本地 24:00 = UTC 当天 16:00
from datetime import timezone as tz_mod
tz_cn = tz_mod(timedelta(hours=8))
local_start = d.replace(tzinfo=tz_cn)
local_end = local_start + timedelta(days=1)
q = q.where(Match.match_date >= local_start, Match.match_date < local_end)
# 未开赛按日期正序(最近的排最前,便于预测);其余按日期倒序(最新赛果在前) # 未开赛按日期正序(最近的排最前,便于预测);其余按日期倒序(最新赛果在前)
if status == "scheduled": if status == "scheduled":
+5 -5
View File
@@ -8,11 +8,11 @@ from __future__ import annotations
import logging import logging
from fastapi import APIRouter, Depends, HTTPException, Query from fastapi import APIRouter, Depends, HTTPException, Query, Request
from sqlalchemy import select from sqlalchemy import select
from sqlalchemy.orm import selectinload from sqlalchemy.orm import selectinload
from src.api.deps import rate_limit_predict, require_admin from src.api.deps import get_predict_rate_limit_remaining, rate_limit_predict, require_admin
from src.api.schemas import PredictOut, PredictRequest, PredictionOut from src.api.schemas import PredictOut, PredictRequest, PredictionOut
from src.db.base import AsyncSession, get_db_read, short_read from src.db.base import AsyncSession, get_db_read, short_read
from src.db.models import Match, Prediction from src.db.models import Match, Prediction
@@ -24,8 +24,8 @@ router = APIRouter(prefix="/api/v1", tags=["predict"])
@router.post("/predict", response_model=PredictOut, dependencies=[Depends(rate_limit_predict)]) @router.post("/predict", response_model=PredictOut, dependencies=[Depends(rate_limit_predict)])
async def predict(req: PredictRequest): async def predict(req: PredictRequest, request: Request):
"""对一场比赛调 LLM 预测。mode=multi(默认,5专家+终裁)single。 """对一场比赛调 LLM 预测。mode=multi(默认,5专家+终裁)single 或 baseline
公开接口,仅做限流保护(不要求登录)。 公开接口,仅做限流保护(不要求登录)。
@@ -100,7 +100,7 @@ async def predict(req: PredictRequest):
latency_ms=result.get("latency_ms", 0) if result_dict else result.latency_ms, latency_ms=result.get("latency_ms", 0) if result_dict else result.latency_ms,
prompt_tokens=result.get("prompt_tokens") if result_dict else getattr(result, "prompt_tokens", None), prompt_tokens=result.get("prompt_tokens") if result_dict else getattr(result, "prompt_tokens", None),
completion_tokens=result.get("completion_tokens") if result_dict else getattr(result, "completion_tokens", None), completion_tokens=result.get("completion_tokens") if result_dict else getattr(result, "completion_tokens", None),
rate_limit_remaining=_predict_limiter.remaining(get_client_ip(request)), rate_limit_remaining=get_predict_rate_limit_remaining(request),
) )
+1 -1
View File
@@ -95,10 +95,10 @@ class MultiPredictResult:
pred_1x2: str | None pred_1x2: str | None
subjective_confidence: float | None subjective_confidence: float | None
reasoning: str | None reasoning: str | None
context: str
agent_outputs: list[dict] agent_outputs: list[dict]
agent_weights: dict | None agent_weights: dict | None
status: str = "success" status: str = "success"
context: str
latency_ms: int | None = None latency_ms: int | None = None
prompt_tokens: int | None = None prompt_tokens: int | None = None
completion_tokens: int | None = None completion_tokens: int | None = None