"""Repurpose JobUsageStatistics for new Steps table Revision ID: 416b9d2db10b Revises: 25fc99e97839 Create Date: 2025-01-17 11:27:42.115755 """ from typing import Sequence, Union import sqlalchemy as sa from sqlalchemy.dialects import postgresql from alembic import op from letta.settings import settings # revision identifiers, used by Alembic. revision: str = "416b9d2db10b" down_revision: Union[str, None] = "25fc99e97839" branch_labels: Union[str, Sequence[str], None] = None depends_on: Union[str, Sequence[str], None] = None def upgrade() -> None: # Skip this migration for SQLite if not settings.letta_pg_uri_no_default: return # ### commands auto generated by Alembic - please adjust! ### # Rename the table op.rename_table("job_usage_statistics", "steps") # Rename the foreign key constraint and drop non-null constraint op.alter_column("steps", "job_id", nullable=True) op.drop_constraint("fk_job_usage_statistics_job_id", "steps", type_="foreignkey") # Change id field from int to string op.execute("ALTER TABLE steps RENAME COLUMN id TO old_id") op.add_column("steps", sa.Column("id", sa.String(), nullable=True)) op.execute("""UPDATE steps SET id = 'step-' || gen_random_uuid()::text""") op.drop_column("steps", "old_id") op.alter_column("steps", "id", nullable=False) op.create_primary_key("pk_steps_id", "steps", ["id"]) # Add new columns op.add_column("steps", sa.Column("origin", sa.String(), nullable=True)) op.add_column("steps", sa.Column("organization_id", sa.String(), nullable=True)) op.add_column("steps", sa.Column("provider_id", sa.String(), nullable=True)) op.add_column("steps", sa.Column("provider_name", sa.String(), nullable=True)) op.add_column("steps", sa.Column("model", sa.String(), nullable=True)) op.add_column("steps", sa.Column("context_window_limit", sa.Integer(), nullable=True)) op.add_column( "steps", sa.Column("completion_tokens_details", postgresql.JSON(astext_type=sa.Text()), autoincrement=False, nullable=True), ) op.add_column( "steps", sa.Column("tags", postgresql.JSON(astext_type=sa.Text()), autoincrement=False, nullable=True), ) op.add_column("steps", sa.Column("tid", sa.String(), nullable=True)) # Add new foreign key constraint for provider_id op.create_foreign_key("fk_steps_organization_id", "steps", "providers", ["provider_id"], ["id"], ondelete="RESTRICT") # Add new foreign key constraint for provider_id op.create_foreign_key("fk_steps_provider_id", "steps", "organizations", ["organization_id"], ["id"], ondelete="RESTRICT") # Add new foreign key constraint for provider_id op.create_foreign_key("fk_steps_job_id", "steps", "jobs", ["job_id"], ["id"], ondelete="SET NULL") # Drop old step_id and step_count columns which aren't in the new model op.drop_column("steps", "step_id") op.drop_column("steps", "step_count") # Add step_id to messages table op.add_column("messages", sa.Column("step_id", sa.String(), nullable=True)) op.create_foreign_key("fk_messages_step_id", "messages", "steps", ["step_id"], ["id"], ondelete="SET NULL") # ### end Alembic commands ### def downgrade() -> None: # Skip this migration for SQLite if not settings.letta_pg_uri_no_default: return # ### commands auto generated by Alembic - please adjust! ### # Remove step_id from messages first to avoid foreign key conflicts op.drop_constraint("fk_messages_step_id", "messages", type_="foreignkey") op.drop_column("messages", "step_id") # Restore old step_count and step_id column op.add_column("steps", sa.Column("step_count", sa.Integer(), nullable=True)) op.add_column("steps", sa.Column("step_id", sa.String(), nullable=True)) # Drop new columns and constraints op.drop_constraint("fk_steps_provider_id", "steps", type_="foreignkey") op.drop_constraint("fk_steps_organization_id", "steps", type_="foreignkey") op.drop_constraint("fk_steps_job_id", "steps", type_="foreignkey") op.drop_column("steps", "tid") op.drop_column("steps", "tags") op.drop_column("steps", "completion_tokens_details") op.drop_column("steps", "context_window_limit") op.drop_column("steps", "model") op.drop_column("steps", "provider_name") op.drop_column("steps", "provider_id") op.drop_column("steps", "organization_id") op.drop_column("steps", "origin") # Add constraints back op.execute("DELETE FROM steps WHERE job_id IS NULL") op.alter_column("steps", "job_id", nullable=False) op.create_foreign_key("fk_job_usage_statistics_job_id", "steps", "jobs", ["job_id"], ["id"], ondelete="CASCADE") # Change id field from string back to int op.add_column("steps", sa.Column("old_id", sa.Integer(), nullable=True)) op.execute("""UPDATE steps SET old_id = CAST(ABS(hashtext(REPLACE(id, 'step-', '')::text)) AS integer)""") op.drop_column("steps", "id") op.execute("ALTER TABLE steps RENAME COLUMN old_id TO id") op.alter_column("steps", "id", nullable=False) op.create_primary_key("pk_steps_id", "steps", ["id"]) # Rename the table op.rename_table("steps", "job_usage_statistics") # ### end Alembic commands ###