CreaRack-SL

ADR: Centralizar model strings AI en settings (s61 · 14-05-2026)

ADR: Centralizar model strings AI en config/settings/base.py

Estado: Aceptada · Aplicada en v1.0.73 (s61 · 14-05-2026) Autores: Edu + Claude Code Opus · PR#28 Decisión en una línea: Todo model string de IA runtime vive exclusivamente en config/settings/base.py. Cambiar de modelo es 1 env var en Dokploy + redeploy, no N ediciones de código.


Contexto

Tras el audit de drift acordado en la Sesión 61, se detectaron 9 callsites en 8 archivos con model strings hardcoded ("gemma-4-26b-a4b-it", "claude-haiku-4-5-20251001") o con os.environ.get(...) directo en callsites Django (lo que duplica defaults y bypasea el setting centralizado).

La Regla 8 de CLAUDE.md ya exigía “modelo único”, pero su aplicación dependía de disciplina humana. Con 9 callsites independientes el drift era inevitable: basta un PR que añada un callsite sin actualizar los demás para que la política se rompa silenciosamente.


Decisión

Única fuente de verdad por model string: config/settings/base.py, consumido vía django.conf.settings en todo callsite runtime Django.

Settings añadidas (PR#28)

SettingEnv varDefaultUso
ANTHROPIC_HAIKU_MODELANTHROPIC_HAIKU_MODELclaude-haiku-4-5-20251001CNS / Tutor / Explain / fallback router
ANTHROPIC_SONNET_MODELANTHROPIC_SONNET_MODELclaude-sonnet-4-6Reservado runtime (actualmente sin callsite activo)

Settings preexistentes (ahora correctamente consumidas)

SettingEnv varDefault
GEMMA4_GENAI_MODELGEMMA4_GENAI_MODELgemma-4-26b-a4b-it
EDGE_AI_GOOGLE_GENAI_MODELEDGE_AI_GOOGLE_GENAI_MODELgemma-4-26b-a4b-it
GEMMA4_GENAI_SEEDGEMMA4_GENAI_SEEDNone
GEMMA4_GENAI_MAX_IMAGE_DIMENSIONGEMMA4_GENAI_MAX_IMAGE_DIMENSION2048
GEMMA4_GENAI_JPEG_QUALITYGEMMA4_GENAI_JPEG_QUALITY95

Callsites migrados (9)

ArchivoCambio concreto
monitoring/services/ai_providers/google_genai.pyElimina DEFAULT_MODEL = "gemma-..." · _get_model() lee settings.EDGE_AI_GOOGLE_GENAI_MODEL directo
core/services/ai_providers/router.pyElimina DEFAULT_GOOGLE_GENAI_MODEL y DEFAULT_CLAUDE_MODEL · _try_google_genai y _try_claude leen settings
core/services/ai_operations.pyElimina AIOperations.MODEL = "gemma-..." class attr · _call_ai lee settings.GEMMA4_GENAI_MODEL
blueprints/services/google_genai_driver.py4 env GEMMA4_GENAI_* leídas vía settings · elimina import os
monitoring/services/ai_providers/claude.pyElimina CLAUDE_MODEL = "claude-haiku-..." literal
monitoring/services/tutor_service.py_ask_claude lee django_settings.ANTHROPIC_HAIKU_MODEL
monitoring/services/explain_service.pyexplain_insight + _revise_claude leen settings (2 callsites)
.github/scripts/bib_ingest.pyLee os.getenv("BIB_INGEST_TRIAGE_MODEL", "claude-haiku-4-5") y BIB_INGEST_DEEP_MODEL — NO carga Django, excepción legítima

Excluidos del refactor (correctos, sin cambio)

  • scripts/test_vertex_eu_minitest.py, scripts/test_tutor_vlan*.py: literales son parte del input bajo prueba (regression tests). El modelo es datos, no config runtime.

Consecuencias

Positivas

  • Cambio de modelo = 1 operación: editar 1 env var en Dokploy panel + redeploy (~2 min). Tiempo de rollback estimado: 90-120 s.
  • Drift imposible por construcción: si no hay model strings en el código, no pueden divergir.
  • Comportamiento PROD invariante: los defaults son idénticos a los literales anteriores. Cero acción manual post-merge.
  • Regla 8 ahora es estructural, no documental.

Negativas / Trade-offs

  • Un settings.NONEXISTENT_ATTR en callsite falla en runtime (antes: os.environ.get con default local fallaba silenciosamente). Mitigado: smoke check en CI + healthcheck Dokploy.
  • El script bib_ingest.py queda como excepción explícita y documentada — requiere vigilancia para no crear más excepciones.

Procedimiento de cambio de modelo (resumen ejecutivo)

El procedimiento completo vive en context/AI_CONFIG.md §3. Resumen:

  1. Validar Regla 8: el modelo nuevo cumple whitelist (Google AI Studio Paid Tier o Anthropic, GDPR EU, sin free tier). Si no → parar.
  2. Probar en local con .env local → validar CNS + Tutor + MIB.
  3. Dokploy STAGE → editar env var → Redeploy → smoke 24h.
  4. Dokploy PROD → editar env var → Redeploy → monitorizar 24h (latency ai_call_duration_ms + logs monitoring.ai.*).
  5. Rollback: revertir env var en Dokploy → Redeploy. Sin cambios de código.
  6. Cuando el nuevo modelo lleva ≥30 días en PROD estable: bump del default en settings/base.py + actualizar AI_CONFIG.md + Regla 8 + crear nuevo ADR wiki.

Whitelist providers (Regla 8)

ProviderEstadoNota
Google AI Studio — Gemma 4 (google-genai SDK)✅ Default canónicoPaid Tier, billing EU
Anthropic Claude (Haiku 4.5 / Sonnet 4.6)✅ AlternativoPaid, EU residency
Vertex AI directo⏸️ Postpuesto s50Investigación residencia datos EU pendiente
OpenRouter❌ Retirado s55No reintroducir
Gemini Flash/Pro/Lite❌ ExcluidoRegla 8 monoproveedor + memoria feedback
OpenAI GPT-*❌ ExcluidoMismo motivo
Free tiers (cualquier proveedor)❌ ExcluidoSaaS comercial
Ollama / self-hosted⏸️ Solo dev localAUTOPLAN_PROVIDER=ollama experimentación · PROD self-host descartado s53

Historial de sesiones relevante

SesiónFechaHito
s4903-05-2026Migración a google-genai SDK directo, sin OpenRouter intermediario
s5308-05-2026Self-host inferencia (pve-epyc-02) dado de baja. 100% AI Studio Paid Tier
s5511-05-2026Cleanup OpenRouter del repo (-1266 LOC). Stack consolidada
s5812-05-2026translate-wiki.mjs + CF Workers functions/api/wiki/translate.ts migrados a AI Studio directo
s6114-05-2026Este ADR — 9 callsites runtime centralizados en settings

Lección sistémica

La política “modelo único” (Regla 8) no se sostiene con disciplina humana cuando el código tiene N callsites con literales. La aplicación tiene que ser estructural: si no hay literales que cambiar, no hay drift posible.

Aplicable a futuros tokens centralizables: URLs de servicio, claves de caché, timeouts AI — todos candidatos al mismo patrón.


Véase también

  • [[concept—ai—regla-8-monoproveedor]]
  • [[runbook—infra—cambio-modelo-ai]]
  • [[feature—monitoring—cns-insight]]
  • [[feature—blueprints—auto-plan]]
  • [[feature—biblioteca—bib-ingest]]