fix: 修复 Code Review 发现的 4 个 High + 1 个 Medium 问题
High-1/2: baseline mode 违反 DB CHECK 约束
- ck_mode_enum 扩展为 ('single','multi','baseline')
- baseline 的 run_type 从 'baseline' 改为 'live'(符合现有约束)
- 新增迁移 0017_mode_baseline
High-3: 限流日志 NameError: ip 未定义
- deps.py:190 的 logger.warning 中 ip → client_ip
High-4: 生产 Cookie 缺少 Secure 标志
- auth.py 登录时根据 APP_ENV 设置 secure=True(生产)
Medium-5: 降级日志参数类型错误
- orchestrator.py:266 ok_reports(list) → len(ok_reports)(int)
附加: PredictRequest mode 字段加 pattern 校验,与 DB 约束同源
Co-Authored-By: new-provider/LongCat-2.0 <<EMAIL>>
This commit is contained in:
co-authored by
new-provider/LongCat-2.0 <
parent
0a6af0657a
commit
675e176603
@@ -0,0 +1,39 @@
|
|||||||
|
"""扩展 ck_mode_enum 约束支持 baseline 模式
|
||||||
|
|
||||||
|
Revision ID: 0017_mode_baseline
|
||||||
|
Revises: 0016_schedules
|
||||||
|
Create Date: 2026-09-21
|
||||||
|
|
||||||
|
Code Review High-1/2 修复:
|
||||||
|
业务支持 mode='baseline'(非 LLM 统计基线),但 DB CHECK 约束只允许
|
||||||
|
('single', 'multi'),导致 baseline 预测写入时 CheckViolation。
|
||||||
|
run_type='baseline' 改为 'live'(代码侧),约束无需扩展。
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import Sequence, Union
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision: str = '0017_mode_baseline'
|
||||||
|
down_revision: Union[str, None] = '0016_schedules'
|
||||||
|
branch_labels: Union[str, Sequence[str], None] = None
|
||||||
|
depends_on: Union[str, Sequence[str], None] = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade() -> None:
|
||||||
|
op.drop_constraint('ck_mode_enum', 'predictions', type_='check')
|
||||||
|
op.create_check_constraint(
|
||||||
|
'ck_mode_enum', 'predictions',
|
||||||
|
"mode IN ('single', 'multi', 'baseline')",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade() -> None:
|
||||||
|
# 先清理 baseline 数据再恢复旧约束
|
||||||
|
op.execute("DELETE FROM predictions WHERE mode = 'baseline'")
|
||||||
|
op.drop_constraint('ck_mode_enum', 'predictions', type_='check')
|
||||||
|
op.create_check_constraint(
|
||||||
|
'ck_mode_enum', 'predictions',
|
||||||
|
"mode IN ('single', 'multi')",
|
||||||
|
)
|
||||||
+1
-1
@@ -187,7 +187,7 @@ 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", ip)
|
logger.warning("rate limit exceeded for %s", client_ip)
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=429,
|
status_code=429,
|
||||||
detail="请求过于频繁,请稍后再试(每分钟最多 10 次)",
|
detail="请求过于频繁,请稍后再试(每分钟最多 10 次)",
|
||||||
|
|||||||
@@ -83,6 +83,8 @@ async def login(body: LoginIn, request: Request, response: Response):
|
|||||||
raise HTTPException(status_code=401, detail="密码错误")
|
raise HTTPException(status_code=401, detail="密码错误")
|
||||||
|
|
||||||
_fail_times.pop(ip, None)
|
_fail_times.pop(ip, None)
|
||||||
|
# Code Review High-4: 生产环境(HHTTPS)下 Cookie 必须带 Secure,防中间人窃取
|
||||||
|
secure = settings.APP_ENV == "production"
|
||||||
response.set_cookie(
|
response.set_cookie(
|
||||||
key=SESSION_COOKIE,
|
key=SESSION_COOKIE,
|
||||||
value=create_session_token(await get_session_secret()),
|
value=create_session_token(await get_session_secret()),
|
||||||
@@ -90,6 +92,7 @@ async def login(body: LoginIn, request: Request, response: Response):
|
|||||||
httponly=True,
|
httponly=True,
|
||||||
samesite="lax",
|
samesite="lax",
|
||||||
path="/",
|
path="/",
|
||||||
|
secure=secure,
|
||||||
)
|
)
|
||||||
logger.info("管理员登录成功 (ip=%s)", ip)
|
logger.info("管理员登录成功 (ip=%s)", ip)
|
||||||
return {"ok": True, "expires_in_hours": settings.ADMIN_SESSION_TTL_HOURS}
|
return {"ok": True, "expires_in_hours": settings.ADMIN_SESSION_TTL_HOURS}
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ async def _persist_baseline(match_id: int, baseline: dict) -> int:
|
|||||||
provider_name="baseline",
|
provider_name="baseline",
|
||||||
model="baseline",
|
model="baseline",
|
||||||
mode="baseline",
|
mode="baseline",
|
||||||
run_type="baseline",
|
run_type="live", # baseline 是 live 预测的变体,符合 ck_run_type_enum
|
||||||
values={
|
values={
|
||||||
"prompt_version": "baseline_v1",
|
"prompt_version": "baseline_v1",
|
||||||
"prompt_tokens": 0,
|
"prompt_tokens": 0,
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ class PredictRequest(BaseModel):
|
|||||||
prompt_version: str | None = None
|
prompt_version: str | None = None
|
||||||
mode: str = Field(
|
mode: str = Field(
|
||||||
"multi",
|
"multi",
|
||||||
|
pattern="^(multi|single|baseline)$",
|
||||||
description="multi(默认,5专家+终裁) | single(单次) | baseline(极简统计基线,不调用 LLM)",
|
description="multi(默认,5专家+终裁) | single(单次) | baseline(极简统计基线,不调用 LLM)",
|
||||||
)
|
)
|
||||||
use_cache: bool = True
|
use_cache: bool = True
|
||||||
|
|||||||
+1
-1
@@ -249,7 +249,7 @@ class Prediction(Base):
|
|||||||
CheckConstraint("pred_away_goals >= 0", name="ck_pred_away_goals_nonneg"),
|
CheckConstraint("pred_away_goals >= 0", name="ck_pred_away_goals_nonneg"),
|
||||||
CheckConstraint("subjective_confidence >= 0 AND subjective_confidence <= 1", name="ck_confidence_range"),
|
CheckConstraint("subjective_confidence >= 0 AND subjective_confidence <= 1", name="ck_confidence_range"),
|
||||||
CheckConstraint("pred_1x2 IN ('1', 'X', '2')", name="ck_pred_1x2_enum"),
|
CheckConstraint("pred_1x2 IN ('1', 'X', '2')", name="ck_pred_1x2_enum"),
|
||||||
CheckConstraint("mode IN ('single', 'multi')", name="ck_mode_enum"),
|
CheckConstraint("mode IN ('single', 'multi', 'baseline')", name="ck_mode_enum"),
|
||||||
CheckConstraint("status IN ('success', 'failed', 'degraded')", name="ck_status_enum"),
|
CheckConstraint("status IN ('success', 'failed', 'degraded')", name="ck_status_enum"),
|
||||||
CheckConstraint("run_type IN ('live', 'backtest')", name="ck_run_type_enum"),
|
CheckConstraint("run_type IN ('live', 'backtest')", name="ck_run_type_enum"),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -263,7 +263,7 @@ async def predict_match_multi(
|
|||||||
# 所有专家无数据/均失败:跳过终裁,标记 degraded
|
# 所有专家无数据/均失败:跳过终裁,标记 degraded
|
||||||
logger.warning(
|
logger.warning(
|
||||||
"预测降级 match=%s mode=%s status=degraded experts=%d/%d 均无有效数据",
|
"预测降级 match=%s mode=%s status=degraded experts=%d/%d 均无有效数据",
|
||||||
match_id, "multi", ok_reports, len(reports),
|
match_id, "multi", len(ok_reports), len(reports),
|
||||||
)
|
)
|
||||||
# 无有效专家时不调用 aggregator provider,避免多余开销
|
# 无有效专家时不调用 aggregator provider,避免多余开销
|
||||||
# model 使用 settings 默认值占位(无实际 LLM 调用)
|
# model 使用 settings 默认值占位(无实际 LLM 调用)
|
||||||
|
|||||||
Reference in New Issue
Block a user