185 lines
6.1 KiB
Python
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
|