"""add eval_runs / eval_run_cases / eval_router_predictions (Спринт 8a) Revision ID: k7e9d5c67h34 Revises: j6d8c4b56g23 Create Date: 2026-05-02 14:30:00.000000 Регрессия роутера в UI: - eval_runs — прогон, привязка к версии роутера, статус, статистика. - eval_run_cases — только расхождения (predicted != expected). - eval_router_predictions — кэш LLM-ответов по (text_hash, router_config_id). """ from typing import Sequence, Union from alembic import op import sqlalchemy as sa revision: str = 'k7e9d5c67h34' down_revision: Union[str, None] = 'j6d8c4b56g23' branch_labels: Union[str, Sequence[str], None] = None depends_on: Union[str, Sequence[str], None] = None def upgrade() -> None: op.create_table( 'eval_runs', sa.Column('id', sa.Integer(), nullable=False), sa.Column('suite', sa.String(length=50), nullable=False), sa.Column('router_config_id', sa.Integer(), nullable=True), sa.Column('min_count', sa.Integer(), nullable=False), sa.Column('status', sa.String(length=20), nullable=False), sa.Column('total', sa.Integer(), nullable=False), sa.Column('passed', sa.Integer(), nullable=False), sa.Column('failed', sa.Integer(), nullable=False), sa.Column('cache_hits', sa.Integer(), nullable=False), sa.Column('error_text', sa.Text(), nullable=True), sa.Column('started_at', sa.DateTime(timezone=True), nullable=False), sa.Column('finished_at', sa.DateTime(timezone=True), nullable=True), sa.ForeignKeyConstraint(['router_config_id'], ['agent_configs.id'], ondelete='SET NULL'), sa.PrimaryKeyConstraint('id'), ) op.create_index('ix_eval_runs_router_config_id', 'eval_runs', ['router_config_id']) op.create_table( 'eval_run_cases', sa.Column('id', sa.Integer(), nullable=False), sa.Column('run_id', sa.Integer(), nullable=False), sa.Column('text', sa.Text(), nullable=False), sa.Column('expected_intent', sa.String(length=50), nullable=False), sa.Column('predicted_intent', sa.String(length=50), nullable=False), sa.Column('count_weight', sa.Integer(), nullable=False), sa.ForeignKeyConstraint(['run_id'], ['eval_runs.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('id'), ) op.create_index('ix_eval_run_cases_run_id', 'eval_run_cases', ['run_id']) op.create_table( 'eval_router_predictions', sa.Column('id', sa.Integer(), nullable=False), sa.Column('text_hash', sa.String(length=64), nullable=False), sa.Column('router_config_id', sa.Integer(), nullable=True), sa.Column('predicted_intent', sa.String(length=50), nullable=False), sa.Column('created_at', sa.DateTime(timezone=True), nullable=False), sa.ForeignKeyConstraint(['router_config_id'], ['agent_configs.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('id'), sa.UniqueConstraint('text_hash', 'router_config_id', name='uq_eval_router_pred_hash_cfg'), ) op.create_index('ix_eval_router_predictions_text_hash', 'eval_router_predictions', ['text_hash']) op.create_index( 'ix_eval_router_predictions_router_config_id', 'eval_router_predictions', ['router_config_id'], ) def downgrade() -> None: op.drop_index('ix_eval_router_predictions_router_config_id', table_name='eval_router_predictions') op.drop_index('ix_eval_router_predictions_text_hash', table_name='eval_router_predictions') op.drop_table('eval_router_predictions') op.drop_index('ix_eval_run_cases_run_id', table_name='eval_run_cases') op.drop_table('eval_run_cases') op.drop_index('ix_eval_runs_router_config_id', table_name='eval_runs') op.drop_table('eval_runs')