Evolucionar de SLO pasivo (solo observar metricas en Grafana) a SLO activo (reaccionar automaticamente a degradaciones) sin romper el streaming real, gateway hardening, routing tightening ni el three-model runtime establecido en FASE 29.3.

El runtime ahora puede:

  • Detectar cuando esta degradado (TTFB alto, GPU sobrecargada, timeouts)
  • Reaccionar automaticamente (forzar llama, proteger qwen, reducir concurrencia)
  • Recuperarse cuando la presion baja (cooldown de 30s)
  • Exponer todo via metricas Prometheus + dashboard dedicado

Todo es reversible via feature flags. Default: dry-run.

SLOState (deques sliding window por modelo)
├─ TTFB readings (maxlen=300 ≈ 5min a 1req/s)
├─ Timeout readings (maxlen=600 ≈ 10min)
├─ GPU / VRAM pressure (ultimo valor)
└─ Gateway crashes / orphan streams (acumuladores)
RuntimeSLOManager (evaluacion cada 10s)
├─ GREEN (0): sin violaciones
├─ YELLOW (1): 1+ SLO en warning
└─ RED (2): 1+ SLO en critical
DegradationManager (con anti-flapping 30s + cooldown 30s)
├─ LEVEL 0 — NORMAL: routing completo
├─ LEVEL 1 — LIGHT: forced llama + qwen protection
├─ LEVEL 2 — HEAVY: pause qwen (observable, no auto-activo)
└─ LEVEL 3 — EMERGENCY: llama-only (observable, no auto-activo)
AdaptiveConcurrency
└─ qwen parallel: 2 (GPU<70%) → 1 (GPU 70-90%) → 1 (GPU>90%)
└─ llama parallel: 3 → 2 (GPU>90%)
PrioritySlotManager
└─ Lane 1: 2 slots dedicados (minimal, observe)
└─ Lane 2+3: pool compartido de 2 slots
CircuitBreakerRegistry
└─ Por modelo: CLOSED → OPEN (3 fallos/60s) → HALF_OPEN (30s)
└─ Observable: no bloquea requests en esta fase
FlagDefaultProposito
AI_LAB_ENABLE_SLO_ENFORCEMENTfalseActiva enforcement real. Sin esto, todo es dry-run.
AI_LAB_SLO_DRY_RUNtrueModo observacion: evalua SLO, emite metricas, NO actua.

Establecer via env al iniciar el gateway, misma convencion que AI_LAB_REAL_STREAMING.

DRY RUN obligatorio primero. Solo activar enforcement tras validar dashboards durante al menos 15 minutos de burn-in.

runtime/slo/
├── __init__.py # Export publico
├── runtime_slo.py # SLOState + RuntimeSLOManager
├── degradation.py # DegradationManager (4 niveles)
├── concurrency.py # AdaptiveConcurrency
├── priority_lanes.py # PrioritySlotManager
├── circuit_breakers.py # ModelCircuitBreaker + Registry
└── metrics.py # 14 metricas Prometheus nuevas

SLOState (thread-safe, daemon thread):

  • ttfb_readings: deque[float] — sliding window 300 entries (~5 min)
  • timeout_readings: deque[bool] — sliding window 600 entries (~10 min)
  • gpu_util, vram_pressure — ultimo valor conocido
  • gateway_crashes, orphan_streams — acumuladores
  • record_ttfb(ms), record_timeout(is_timeout), record_gpu_state(util, vram)
  • get_snapshot(){ttfb_p50, ttfb_p95, timeout_rate, gpu_util, ...}
  • _compute_p50(): O(n log n) sobre deque de 300 — sorted + mid index
  • _compute_p95(): sorted + 95th percentile index
  • _compute_timeout_rate(): sum(timeouts) / len(readings)

RuntimeSLOManager:

  • evaluate_slo_state() → 0/1/2 — evalua cada 10s contra SLO_TARGETS
  • get_runtime_health(){slo_state, degradation_level, dry_run, snapshot, violations}
  • trigger_degradation_mode(level) / clear_degradation_mode()
  • update_metrics() — actualiza metricas Prometheus desde snapshot si no dry-run

SLO_TARGETS (constantes del modulo):

SLOWarningCritical
ttfb_p50800ms1200ms
ttfb_p955000ms10000ms
timeout_rate0.02 (2%)0.05 (5%)
gpu_util0.85 (85%)0.95 (95%)
vram_pressure0.90 (90%)0.97 (97%)
gateway_crashes01
orphan_streams01

DegradationManager (thread-safe):

Niveles:

LevelNombreTriggersAcciones
0NORMALRouting completo
1LIGHTTTFB p95>10s o GPU util>92%greetings → llama obligatorio; bloquear escalation a qwen; qwen parallel 2→1
2HEAVYtimeout_rate>5% o VRAM>97% o stream backlogbloquear observe/report/cognitive; pausa temporal qwen routing (observable, NO auto-activo)
3EMERGENCYgateway instability o orphan streams o timeout storms repetidosforce llama-only; rechazar prompts > 500 chars; deshabilitar embeddings (observable, NO auto-activo)

Anti-flapping: minimo 30s entre transiciones de nivel. Cooldown: 30s de estado saludable continuo antes de bajar de nivel.

Metodos de consulta (usados por gateway):

  • should_force_llama(level) → True si level>=3
  • should_block_qwen_escalation(level) → True si level>=1
  • should_pause_qwen_routing(level) → True si level>=2
  • should_block_observe_report(level) → True si level>=2
  • should_reject_long_prompts(level) → True si level==3
  • record_qwen_protection(reason, dry_run) — log + metrica
  • record_llama_forced(reason, dry_run) — log + metrica

AdaptiveConcurrency (thread-safe):

Constantes:

  • QWEN_BASE_PARALLEL = 2, QWEN_MIN_PARALLEL = 1
  • LLAMA_BASE_PARALLEL = 3, LLAMA_MIN_PARALLEL = 2

Logica:

  • _compute_qwen_parallel(): degradation>=2→0,>=1→1, GPU>90% o VRAM>97%→1, timeout>5%→1, GPU>70%→2, else→2
  • _compute_llama_parallel(): degradation>=3→3 (llama full), GPU>90%→2, else→3
  • get_streams_max(): qwen_parallel + llama_parallel (total concurrent streams)

PrioritySlotManager (thread-safe):

LaneRutasSlots dedicados
1 — Critical/Fastminimal, observe, tool_fastpath2 slots
2 — Cognitivecognitive, learning, report1 slot (compartido con Lane 3)
3 — Backgroundembeddings, memory indexing1 slot (compartido con Lane 2)

Sin worker pools, sin preemption. Lane 1 siempre tiene prioridad en el proximo slot disponible via acquire_slot("critical").

Funcion auxiliar get_lane_for_route(route_family)"critical" | "cognitive".

ModelCircuitBreaker (por modelo, thread-safe):

  • CLOSED (0): operacion normal
  • OPEN (2): 3 fallos consecutivos en ventana de 60s
  • HALF_OPEN (1): prueba cada 30s. 1 exito → CLOSED. 1 fallo → OPEN.

CircuitBreakerRegistry: singleton que gestiona breakers para todos los modelos.

  • record_success(model), record_failure(model), should_allow(model), get_all_states()

En esta fase: NO bloquean requests. Solo exponen estado via ailab_circuit_breaker_state. El gateway puede decidir si usarlos.

openai_gateway.py (5 puntos de integracion)

Section titled “openai_gateway.py (5 puntos de integracion)”
  1. Startup (linea 63): import try/except del modulo SLO. Si falla, _HAVE_SLO = False y todo el SLO se desactiva silenciosamente.

  2. TTFB recording (linea 1140): tras medir TTFB, registrar en _slo_state.record_ttfb(ttfb_ms) + GPU state.

  3. Degradation model override (linea 1096): tras seleccionar modelo, comprobar nivel de degradacion:

    • EMERGENCY → forzar llama-3.1-8b siempre
    • HEAVY + qwen seleccionado → redirigir a llama
    • LIGHT + ruta minimal/observe con qwen → bloquear escalation
  4. Periodic evaluation (linea 1263): tras completar request, llamar evaluate_slo_state(), pasar a DegradationManager.evaluate_and_apply(), actualizar AdaptiveConcurrency, emitir metricas, y llamar stream_sanitizer.set_max_streams().

  5. Circuit breaker recording: en exito (non-stream + stream paths) y error (RequestException, Exception).

  • set_max_streams(limit) — override externo para MAX_CONCURRENT_STREAMS
  • _get_max_streams() — lee override o devuelve default (3)
  • _acquire_slot() — usa _get_max_streams() en vez de la constante
Terminal window
curl -s http://192.168.1.30:8008/slo/health | jq .

Respuesta:

{
"slo_state": "green",
"slo_state_code": 0,
"degradation_level": 0,
"dry_run": true,
"enabled": false,
"snapshot": {
"ttfb_p50": 804.0,
"ttfb_p95": 3120.0,
"timeout_rate": 0.0,
"gpu_util": 0.3,
"vram_pressure": 0.5,
"gateway_crashes": 0,
"orphan_streams": 0,
"stream_backlog": 0,
"active_streams": 0
},
"violations": []
}
MetricaTipoLabelsProposito
ailab_runtime_slo_stateGauge0=GREEN, 1=YELLOW, 2=RED
ailab_runtime_degradation_levelGauge0-3
ailab_runtime_timeout_rateGaugeTimeout rate en ventana actual
ailab_runtime_vram_pressureGaugePresion VRAM (0.0-1.0)
ailab_runtime_gpu_pressureGaugePresion GPU (0.0-1.0)
ailab_runtime_priority_lane_totalCounterlaneRequests procesados por lane
ailab_runtime_emergency_mode_totalCounterreasonActivaciones de emergency mode
ailab_runtime_qwen_protection_totalCounterreasonEventos de proteccion a qwen
ailab_runtime_llama_fastpath_forced_totalCounterreasonLlama forzado por degradacion
ailab_runtime_stream_backlogGaugeRequests esperando slot de stream
ailab_circuit_breaker_stateGaugemodel0=closed, 1=half, 2=open
ailab_slo_violations_totalCounterslo_name, levelViolaciones de SLO
ailab_runtime_qwen_parallelGaugeLimite paralelo actual de qwen
ailab_runtime_concurrent_streamsGaugeStreams activos actualmente

14 paneles en el dashboard:

PanelTipoMetricaThreshold
Runtime SLO StateStatailab_runtime_slo_state0=GREEN, 1=YELLOW, 2=RED
Degradation LevelStatailab_runtime_degradation_level0-3 con thresholds
GPU PressureGaugeailab_runtime_gpu_pressure85%, 95%
VRAM PressureGaugeailab_runtime_vram_pressure90%, 97%
Timeout RateGaugeailab_runtime_timeout_rate2%, 5%
Forced Llama RoutingTimeseriesrate(ailab_llama_fastpath_forced_total[5m])
Qwen Protection EventsTimeseriesrate(ailab_qwen_protection_total[5m])
Emergency Mode ActivationsTimeseriesrate(ailab_emergency_mode_total[5m])
Priority Lane DistributionBarchartrate(ailab_priority_lane_total[5m])
Stream BacklogTimeseriesailab_runtime_stream_backlog
Dynamic Qwen ParallelStatailab_runtime_qwen_parallel0-2
Concurrent StreamsTimeseriesailab_runtime_concurrent_streams
SLO ViolationsTimeseriesrate(ailab_slo_violations_total[5m])
Circuit Breaker StatesTableailab_circuit_breaker_stateclosed/half/open

Provisioning JSON: /opt/ai-lab/dashboards/ailab-runtime-protection.json Datasource UID: PBFA97CFB590B2093 (mismo que los otros dashboards AI-LAB)

Config:

Terminal window
AI_LAB_SLO_DRY_RUN=true
AI_LAB_ENABLE_SLO_ENFORCEMENT=false

Objetivo: verificar que metricas nuevas aparecen en /metrics y dashboard, transiciones GREEN/YELLOW son visibles, 0 side effects en el runtime.

PASS criteria:

  • ailab_runtime_slo_state, ailab_runtime_degradation_level visibles en :8008/metrics
  • Dashboard AI-LAB Runtime Protection muestra datos
  • SLO state transiciones GREEN↔YELLOW visibles bajo carga controlada
  • 0 cambios en comportamiento de routing, streaming o gateway

FASE B — LIGHT ENFORCEMENT (30 min, 3 workers)

Section titled “FASE B — LIGHT ENFORCEMENT (30 min, 3 workers)”

Config:

Terminal window
AI_LAB_SLO_DRY_RUN=false
AI_LAB_ENABLE_SLO_ENFORCEMENT=true

Solo LEVEL 1 activo: forced llama routing + qwen protection + qwen parallel 2→1.

PASS criteria:

  • Degradacion LEVEL 1 se activa bajo GPU>92%
  • Greetings se redirigen a llama bajo degradacion
  • Escalation a qwen se bloquea bajo degradacion
  • Degradacion se revierte al bajar la carga (cooldown 30s)
  • TTFB se mantiene estable (no empeora por el enforcement)
  • 0 crashes, 0 orphan streams, 0 qwen3.6 usage

LEVEL 2 y LEVEL 3 implementados, metricados y feature-flagged pero NO auto-activos. Se activaran en fases futuras tras validar comportamiento.

  • ❌ Auto-restart runtime
  • ❌ Self-healing loops
  • ❌ Autonomous scaling
  • ❌ Planner/executor (FASE 28.x)
  • ❌ Write sandbox
  • ❌ Async/multiprocessing refactor
  • ❌ Multi-GPU orchestration

runtime/slo/ es completamente desacoplado de runtime/agentic/. No toca planner, executor, governance pipeline ni sandbox write. Los circuit breakers, priority lanes y concurrency manager son wrappers sobre infra existente y no interfieren con el pipeline agentic.

CP-29.4-SLO-ENFORCEMENT-STABLE