feat: 核心模块增强 — 加密 + 运行时配置 + 日志缓冲
- crypto.py: API Key 加密/解密工具 - runtime_config.py: 运行时动态配置管理 - log_buffer.py: 内存日志缓冲区 - config.py: 新增加密配置项 - http_client.py: 增强重试和错误处理
This commit is contained in:
@@ -7,9 +7,10 @@ from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.orm import selectinload
|
||||
|
||||
from src.api.deps import require_admin
|
||||
from src.api.schemas import PredictOut, PredictRequest, PredictionOut
|
||||
from src.db.base import AsyncSession, get_db, get_db_read
|
||||
from src.db.models import Prediction
|
||||
from src.db.models import Match, Prediction
|
||||
from src.llm.predict import predict_match, PredictResult
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -20,6 +21,12 @@ router = APIRouter(prefix="/api/v1", tags=["predict"])
|
||||
@router.post("/predict", response_model=PredictOut)
|
||||
async def predict(req: PredictRequest, db: AsyncSession = Depends(get_db)):
|
||||
"""对一场比赛调 LLM 预测。mode=multi(默认,5专家+终裁)或 single。"""
|
||||
# 已完赛比赛不再支持预测(回测走服务层直调,不受此限)
|
||||
match = await db.get(Match, req.match_id)
|
||||
if match is None:
|
||||
raise HTTPException(404, "match not found")
|
||||
if match.match_status == "finished":
|
||||
raise HTTPException(400, "该比赛已完赛,不再支持预测")
|
||||
try:
|
||||
result = await predict_match(
|
||||
req.match_id,
|
||||
@@ -28,6 +35,9 @@ async def predict(req: PredictRequest, db: AsyncSession = Depends(get_db)):
|
||||
mode=req.mode,
|
||||
)
|
||||
except ValueError as e:
|
||||
msg = str(e)
|
||||
if "已结算" in msg:
|
||||
raise HTTPException(409, msg)
|
||||
logger.warning("predict validation error: %s", e)
|
||||
raise HTTPException(404, "比赛不存在")
|
||||
except RuntimeError as e:
|
||||
@@ -46,6 +56,8 @@ async def predict(req: PredictRequest, db: AsyncSession = Depends(get_db)):
|
||||
mode=getattr(result, "mode", "single"),
|
||||
pred_home_goals=result.pred_home_goals,
|
||||
pred_away_goals=result.pred_away_goals,
|
||||
alt_pred_home_goals=result.alt_pred_home_goals,
|
||||
alt_pred_away_goals=result.alt_pred_away_goals,
|
||||
pred_1x2=result.pred_1x2,
|
||||
subjective_confidence=result.subjective_confidence,
|
||||
reasoning=result.reasoning,
|
||||
@@ -54,9 +66,13 @@ async def predict(req: PredictRequest, db: AsyncSession = Depends(get_db)):
|
||||
context=result.context,
|
||||
latency_ms=result.latency_ms,
|
||||
)
|
||||
logger.info(
|
||||
"预测完成 match=%s mode=%s pred=%s:%s (%s)",
|
||||
req.match_id, req.mode, result.pred_home_goals, result.pred_away_goals, result.pred_1x2,
|
||||
)
|
||||
|
||||
|
||||
@router.get("/predictions", response_model=list[PredictionOut])
|
||||
@router.get("/predictions", response_model=list[PredictionOut], dependencies=[Depends(require_admin)])
|
||||
async def list_predictions(
|
||||
match_id: int | None = None,
|
||||
limit: int = Query(50, ge=1, le=200),
|
||||
@@ -90,7 +106,7 @@ async def list_predictions(
|
||||
]
|
||||
|
||||
|
||||
@router.get("/predictions/{prediction_id}", response_model=PredictionOut)
|
||||
@router.get("/predictions/{prediction_id}", response_model=PredictionOut, dependencies=[Depends(require_admin)])
|
||||
async def get_prediction(prediction_id: int, db: AsyncSession = Depends(get_db_read)):
|
||||
p = await db.get(Prediction, prediction_id)
|
||||
if p is None:
|
||||
|
||||
Reference in New Issue
Block a user