diff --git a/backend/alembic/versions/20260603_add_token_limits_to_llm_providers.py b/backend/alembic/versions/20260603_add_token_limits_to_llm_providers.py index 37b06e51..b19b982f 100644 --- a/backend/alembic/versions/20260603_add_token_limits_to_llm_providers.py +++ b/backend/alembic/versions/20260603_add_token_limits_to_llm_providers.py @@ -1,5 +1,11 @@ """Add context_window and max_output_tokens to llm_providers +@RATIONALE llm_providers is created at runtime by init_db() → + Base.metadata.create_all(), not by any Alembic migration. On a fresh + database, the table doesn't exist when migrations run. Guard with + _table_exists() — create_all() will create the table with the model's + columns (which already include context_window and max_output_tokens). + Revision ID: a1b2c3d4e5f6 Revises: f1a2b3c4d5e6 Create Date: 2026-06-03 @@ -12,6 +18,7 @@ Both NULL = use PROVIDER_DEFAULTS fallback from model name. from typing import Sequence, Union from alembic import op +from sqlalchemy import inspect import sqlalchemy as sa @@ -22,7 +29,15 @@ branch_labels: Union[str, Sequence[str], None] = None depends_on: Union[str, Sequence[str], None] = None +def _table_exists(table_name: str) -> bool: + conn = op.get_bind() + inspector = inspect(conn) + return inspector.has_table(table_name) + + def upgrade() -> None: + if not _table_exists("llm_providers"): + return op.add_column( "llm_providers", sa.Column( @@ -44,5 +59,7 @@ def upgrade() -> None: def downgrade() -> None: + if not _table_exists("llm_providers"): + return op.drop_column("llm_providers", "max_output_tokens") op.drop_column("llm_providers", "context_window") diff --git a/backend/alembic/versions/2df63b7ce038_add_missing_columns_policy_id_provider_.py b/backend/alembic/versions/2df63b7ce038_add_missing_columns_policy_id_provider_.py index 62e7b789..c9f8e7d1 100644 --- a/backend/alembic/versions/2df63b7ce038_add_missing_columns_policy_id_provider_.py +++ b/backend/alembic/versions/2df63b7ce038_add_missing_columns_policy_id_provider_.py @@ -18,6 +18,7 @@ from collections.abc import Sequence import sqlalchemy as sa from alembic import op +from sqlalchemy import inspect # revision identifiers, used by Alembic. revision: str = '2df63b7ce038' @@ -26,27 +27,35 @@ branch_labels: str | Sequence[str] | None = None depends_on: str | Sequence[str] | None = None +def _table_exists(table: str) -> bool: + """Check if a table exists in the current database connection.""" + conn = op.get_bind() + inspector = inspect(conn) + return inspector.has_table(table) + + def upgrade() -> None: """Upgrade schema.""" - # Use IF NOT EXISTS — this is a catch-up migration for columns that - # may already exist (inserted by out-of-band schema changes). conn = op.get_bind() # Add is_multimodal to llm_providers (from 9f8e7d6c5b4a) - if not _column_exists(conn, "llm_providers", "is_multimodal"): + # llm_providers is created by create_all() at runtime — skip if not exist + if _table_exists("llm_providers") and not _column_exists(conn, "llm_providers", "is_multimodal"): op.add_column("llm_providers", sa.Column("is_multimodal", sa.Boolean(), nullable=False, server_default="false") ) op.alter_column("llm_providers", "is_multimodal", server_default=None) # Add provider_id to validation_policies (from a7b1c2d3e4f5) - if not _column_exists(conn, "validation_policies", "provider_id"): + # validation_policies is created by create_all() at runtime — skip if not exist + if _table_exists("validation_policies") and not _column_exists(conn, "validation_policies", "provider_id"): op.add_column("validation_policies", sa.Column("provider_id", sa.String(), nullable=True) ) # Add policy_id to llm_validation_results (from b1c2d3e4f5a6) - if not _column_exists(conn, "llm_validation_results", "policy_id"): + # llm_validation_results is created by create_all() at runtime — skip if not exist + if _table_exists("llm_validation_results") and not _column_exists(conn, "llm_validation_results", "policy_id"): op.add_column("llm_validation_results", sa.Column("policy_id", sa.String(), nullable=True, index=True) )