Files
ss-tools/backend/alembic/versions/f7a8b9c0d1e2_add_translate_performance_knobs.py
2026-07-23 18:53:45 +03:00

185 lines
6.1 KiB
Python

# #region Alembic.AddTranslatePerformanceKnobs [C:3] [TYPE Module] [SEMANTICS alembic,translate,performance]
# @defgroup Alembic Persist translation performance knobs (job policy + provider capabilities).
"""Add translation performance knobs (job + provider capabilities).
Revision ID: f7a8b9c0d1e2
Revises: e6f7a8b9c0d1
Create Date: 2026-07-20 10:30:00.000000
NULL defaults preserve legacy algorithm behaviour (serial LLM, auto hard caps).
"""
from collections.abc import Sequence
import sqlalchemy as sa
from alembic import op
revision: str = "f7a8b9c0d1e2"
down_revision: str | Sequence[str] | None = "e6f7a8b9c0d1"
branch_labels: str | Sequence[str] | None = None
depends_on: str | Sequence[str] | None = None
# #region Alembic.AddTranslatePerformanceKnobs.AddColIfMissing [C:2] [TYPE Function] [SEMANTICS alembic,translate,idempotent]
# @ingroup Alembic
# @BRIEF Add a column only when absent — keeps the migration re-runnable.
def _add_col_if_missing(table: str, column: sa.Column) -> None:
bind = op.get_bind()
inspector = sa.inspect(bind)
if not inspector.has_table(table):
return
existing = {c["name"] for c in inspector.get_columns(table)}
if column.name in existing:
return
op.add_column(table, column)
# #endregion Alembic.AddTranslatePerformanceKnobs.AddColIfMissing
# #region Alembic.AddTranslatePerformanceKnobs.Upgrade [C:3] [TYPE Function] [SEMANTICS alembic,translate,performance]
# @ingroup Alembic
# @BRIEF Add nullable performance knobs to translation_jobs and llm_providers.
# @RATIONALE NULL defaults preserve legacy algorithm behaviour (serial LLM, auto hard caps);
# capabilities are stored in DB, not inferred from brand/host at runtime.
# @RATIONALE llm_providers is an ORM-owned table created by Base.metadata.create_all()
# after Alembic on a fresh install; absent tables must therefore be skipped here.
# @REJECTED Non-nullable columns with server defaults — would silently flip legacy jobs to new behaviour.
def upgrade() -> None:
# ── translation_jobs (job policy / performance) ────────────────────────
_add_col_if_missing(
"translation_jobs",
sa.Column(
"llm_batch_max_rows",
sa.Integer(),
nullable=True,
comment="Max source rows per LLM batch (NULL = algorithm default)",
),
)
_add_col_if_missing(
"translation_jobs",
sa.Column(
"llm_concurrency",
sa.Integer(),
nullable=True,
comment="Parallel LLM batch workers (NULL = 1, legacy serial)",
),
)
_add_col_if_missing(
"translation_jobs",
sa.Column(
"insert_concurrency",
sa.Integer(),
nullable=True,
comment="Parallel insert workers (NULL = 1)",
),
)
_add_col_if_missing(
"translation_jobs",
sa.Column(
"multi_lang_mode",
sa.String(),
nullable=True,
comment="single_call | per_language (NULL = single_call)",
),
)
_add_col_if_missing(
"translation_jobs",
sa.Column(
"batch_aggressiveness",
sa.String(),
nullable=True,
comment="safe | balanced | fast (NULL = balanced legacy constants)",
),
)
_add_col_if_missing(
"translation_jobs",
sa.Column(
"max_in_flight_batches",
sa.Integer(),
nullable=True,
comment="Backpressure queue depth for parallel results (NULL = 32)",
),
)
# ── llm_providers (capabilities, not brand heuristics) ─────────────────
_add_col_if_missing(
"llm_providers",
sa.Column(
"throughput_class",
sa.String(),
nullable=True,
comment="standard | local (NULL = derive later via capability, not host sniff at runtime)",
),
)
_add_col_if_missing(
"llm_providers",
sa.Column(
"reasoning_control",
sa.String(),
nullable=True,
comment="off|generic_none|openai_effort|deepseek_thinking|llamacpp_think|auto",
),
)
_add_col_if_missing(
"llm_providers",
sa.Column(
"supports_json_object",
sa.Boolean(),
nullable=True,
comment="If true, send response_format=json_object (NULL = true for openai-compatible)",
),
)
_add_col_if_missing(
"llm_providers",
sa.Column(
"default_llm_concurrency",
sa.Integer(),
nullable=True,
comment="Default job llm_concurrency when job field is NULL",
),
)
_add_col_if_missing(
"llm_providers",
sa.Column(
"max_llm_concurrency",
sa.Integer(),
nullable=True,
comment="Hard ceiling for job llm_concurrency",
),
)
# #endregion Alembic.AddTranslatePerformanceKnobs.Upgrade
# #region Alembic.AddTranslatePerformanceKnobs.Downgrade [C:2] [TYPE Function] [SEMANTICS alembic,translate,performance]
# @ingroup Alembic
# @BRIEF Drop performance knobs conditionally (idempotent).
def downgrade() -> None:
bind = op.get_bind()
inspector = sa.inspect(bind)
job_cols = {c["name"] for c in inspector.get_columns("translation_jobs")}
for name in (
"max_in_flight_batches",
"batch_aggressiveness",
"multi_lang_mode",
"insert_concurrency",
"llm_concurrency",
"llm_batch_max_rows",
):
if name in job_cols:
op.drop_column("translation_jobs", name)
if not inspector.has_table("llm_providers"):
return
prov_cols = {c["name"] for c in inspector.get_columns("llm_providers")}
for name in (
"max_llm_concurrency",
"default_llm_concurrency",
"supports_json_object",
"reasoning_control",
"throughput_class",
):
if name in prov_cols:
op.drop_column("llm_providers", name)
# #endregion Alembic.AddTranslatePerformanceKnobs.Downgrade
# #endregion Alembic.AddTranslatePerformanceKnobs