"""评测运行:错误也分因,而不是只有一个总数。 **要修的是什么。** 一次运行里的 `error` 是「这次没量到」,刻意不算失败 —— 厂商 连不上不是「助手答得差」,把两者混在一起会让一次故障在趋势线上长得和一次质量回退 一模一样。这个分工是对的,而且已经贯彻到了 `pass_rate` 的分母里。 问题在另一头:**「没量到」也有原因,而那个原因决定该叫谁。** 评委模型开始返回散文 而不是 JSON,和厂商一次都没连上,产出的都是「这一轮 error」—— 前者是评测台自己的 缺陷,后者是运维事件。此前两者都只表现为 `attempts_errored` 涨了一截,报表上没有 任何东西说得出是哪一种。 这一列存的就是那个分因(`{"judge_error": 60, ...}`),**和 `failure_breakdown` 分开 存**:error 刻意不是 failure,合进去等于把一次故障重新塞回质量数字里,而那正是 error 被单独分出来的原因。 Revision ID: 595 Revises: 594 ⚠️ 迁移号要对着 `origin/main` 抢,而且每次 push 之前重新抢一次 —— 本仓踩过的号是 462 / 472 / 582 / 587,两个分支同时拿同一个号,合进 main 之后链上出现两个 head, 后端每个测试分片和镜像构建一起红。 """ from __future__ import annotations import sqlalchemy as sa from alembic import op revision = "595" down_revision = "594" branch_labels = None depends_on = None RUNS = "assistant_eval_runs" COLUMN = "error_breakdown" def _has_table(bind, name: str) -> bool: return bind.execute( sa.text( "SELECT 1 FROM information_schema.tables " "WHERE table_schema = DATABASE() AND table_name = :n" ), {"n": name}, ).scalar() is not None def _has_column(bind, table: str, column: str) -> bool: return bind.execute( sa.text( "SELECT 1 FROM information_schema.columns " "WHERE table_schema = DATABASE() " "AND table_name = :t AND column_name = :c" ), {"t": table, "c": column}, ).scalar() is not None def upgrade() -> None: bind = op.get_bind() if not _has_table(bind, RUNS): return if not _has_column(bind, RUNS, COLUMN): # NULL, not '{}': a run from before this column existed did not measure # zero errors of every kind, it measured nothing about them — and the # console renders the two differently on purpose. op.add_column(RUNS, sa.Column(COLUMN, sa.JSON(), nullable=True)) def downgrade() -> None: bind = op.get_bind() if _has_table(bind, RUNS) and _has_column(bind, RUNS, COLUMN): op.drop_column(RUNS, COLUMN)