use slug base

This commit is contained in:
9qeklajc
2026-07-01 17:01:32 +02:00
parent 17bc949597
commit f588147b41
3 changed files with 87 additions and 7 deletions
@@ -10,12 +10,49 @@ from __future__ import annotations
import sqlalchemy as sa
from alembic import op
from routstr.core.provider_slugs import provider_slug_base, provider_slug_candidate
revision = "c6d7e8f9a0b1"
down_revision = "b5e7c9d1f3a2"
branch_labels = None
depends_on = None
def _allocate_backfill_slug(provider_type: str, reserved_slugs: set[str]) -> str:
base = provider_slug_base(provider_type)
suffix_number = 1
while True:
candidate = provider_slug_candidate(base, suffix_number)
if candidate not in reserved_slugs:
reserved_slugs.add(candidate)
return candidate
suffix_number += 1
def _backfill_provider_slugs(conn: sa.Connection) -> None:
existing_rows = conn.execute(
sa.text(
"SELECT slug FROM upstream_providers "
"WHERE slug IS NOT NULL AND slug != ''"
)
)
reserved_slugs = {str(row.slug).lower() for row in existing_rows}
rows_to_backfill = conn.execute(
sa.text(
"SELECT id, provider_type FROM upstream_providers "
"WHERE slug IS NULL OR slug = '' "
"ORDER BY id"
)
)
for row in rows_to_backfill:
slug = _allocate_backfill_slug(str(row.provider_type), reserved_slugs)
conn.execute(
sa.text("UPDATE upstream_providers SET slug = :slug WHERE id = :id"),
{"slug": slug, "id": row.id},
)
def upgrade() -> None:
conn = op.get_bind()
inspector = sa.inspect(conn)
@@ -27,11 +64,7 @@ def upgrade() -> None:
sa.Column("slug", sa.String(), nullable=True),
)
op.execute(
"UPDATE upstream_providers "
"SET slug = LOWER(provider_type) || '-' || CAST(id AS TEXT) "
"WHERE slug IS NULL OR slug = ''"
)
_backfill_provider_slugs(conn)
existing_indexes = {idx["name"] for idx in inspector.get_indexes("upstream_providers")}
if "ix_upstream_providers_slug" not in existing_indexes:
+2 -2
View File
@@ -28,7 +28,7 @@ def provider_slug_base(provider_type: str) -> str:
return base
def _slug_candidate(base: str, suffix_number: int) -> str:
def provider_slug_candidate(base: str, suffix_number: int) -> str:
if suffix_number == 1:
return base
@@ -52,7 +52,7 @@ async def allocate_unique_provider_slug(
reserved = {slug.lower() for slug in reserved_slugs}
for suffix_number in count(1):
candidate = _slug_candidate(base, suffix_number)
candidate = provider_slug_candidate(base, suffix_number)
if candidate in reserved:
continue
@@ -0,0 +1,47 @@
from __future__ import annotations
import importlib
import sqlalchemy as sa
migration = importlib.import_module(
"migrations.versions.c6d7e8f9a0b1_add_slug_to_upstream_providers"
)
def test_slug_migration_backfill_uses_api_safe_deterministic_slugs() -> None:
engine = sa.create_engine("sqlite:///:memory:")
with engine.begin() as conn:
conn.execute(
sa.text(
"CREATE TABLE upstream_providers ("
"id INTEGER PRIMARY KEY, "
"provider_type VARCHAR NOT NULL, "
"slug VARCHAR NULL"
")"
)
)
conn.execute(
sa.text(
"INSERT INTO upstream_providers (id, provider_type, slug) VALUES "
"(1, 'OpenAI Compatible', NULL), "
"(2, 'OpenAI Compatible', ''), "
"(3, '123', NULL), "
"(4, 'x', NULL), "
"(5, 'anthropic', 'anthropic')"
)
)
migration._backfill_provider_slugs(conn)
rows = conn.execute(
sa.text("SELECT id, slug FROM upstream_providers ORDER BY id")
).all()
assert rows == [
(1, "openai-compatible"),
(2, "openai-compatible-2"),
(3, "provider-123"),
(4, "x-provider"),
(5, "anthropic"),
]