Auto-Plan LLM Resilience — log provider, retry JSON, regex glitch s49
Auto-Plan LLM Resilience — log provider, retry JSON, regex glitch s49
Resumen
PR#18 introduce tres mejoras defensivas al pipeline Auto-Plan tras detectar en la sesión s49 que el proveedor Vertex 2304px producía errores 500 silenciosos por glitches JSON de Gemma 4.
Antes: parse falla → HTTP 500 inmediato al usuario, sin contexto de diagnóstico.
Después: cleanup mejorado + 2 retries con seed alterado → solo si los 3 intentos fallan, se devuelve 500.
Mejora 1 — Log diagnóstico por request OpenRouter
Archivo: blueprints/services/openrouter_driver.py
Cada llamada a OpenRouter registra ahora:
[AutoPlan] OpenRouter served by Google | tokens: 5234p+3421c (cached=4523) | cost=$0.000123 | response=10567 chars
[AutoPlan] OpenRouter served by DekaLLM (retry 1) | tokens: …
Campos:
- Provider físico (
response.provider) — visible cuál proveedor bajo OpenRouter sirvió realmente la request. - Tokens prompt + completion + cached — permite detectar cache hits y anomalías de consumo.
- Coste real en USD (
usage.cost) — trazabilidad de gasto por request. - Label de reintento —
(retry N)si es un segundo/tercer intento.
Motivación: antes de este cambio, todos los requests aparecían como genéricos en los logs; era imposible correlacionar fallos con providers concretos.
Mejora 2 — Retry automático en JSONDecodeError
Archivo: blueprints/services/openrouter_driver.py
max_json_retries = 2
for json_attempt in range(max_json_retries + 1):
if json_attempt > 0 and seed is not None:
create_kwargs["seed"] = seed + json_attempt
response = client.chat.completions.create(**create_kwargs)
...
try:
return json.loads(cleaned)
except json.JSONDecodeError:
if json_attempt < max_json_retries:
logger.warning(...)
continue
logger.error(...)
raise
Lógica del seed alterado
Gemma 4 con seed fijo reproduce el mismo glitch determinista. Incrementar seed+1, seed+2 cambia la trayectoria de sampling y en la mayoría de casos produce una salida JSON válida en el segundo o tercer intento.
Alcance acotado
Solo json.JSONDecodeError. Errores de red, autenticación u otros se propagan inmediatamente sin reintentar.
Mejora 3 — Regex específico para glitch s49 en _clean_llm_json
Archivo: blueprints/services/autoplan.py
Patrón detectado (sesión s49, Vertex 2304px)
"text": "PLANTA M0", "x": 140, ": 700, "color": "blue"
^^^ falta "y": entero
El modelo emite ": 700 con la clave entera omitida. El campo : no es JSON válido.
Fix
text = re.sub(
r'("(x\d*)":\s*-?\d+(?:\.\d+)?,)\s*":',
lambda m: f'{m.group(1)} "y{m.group(2)[1:]}":',
text,
)
Reconstrucción:
"x": N, ":→"x": N, "y":"x1": N, ":→"x1": N, "y1":"x2": N, ":→"x2": N, "y2":
Justificación de dominio: en el esquema Auto-Plan, después de coordenada x/x1/x2 siempre sigue y/y1/y2. La reconstrucción es segura.
Impacto operacional
| Escenario | Antes | Después |
|---|---|---|
| Glitch JSON aislado (Gemma 4) | HTTP 500 inmediato | _clean_llm_json repara; si falla → retry |
| Glitch determinista con mismo seed | HTTP 500 en cada intento | Retry con seed+N rompe determinismo |
| Provider desconocido sirviendo | Sin visibilidad en logs | Log con provider, cost, tokens |
| Diagnóstico post-mortem | Solo char offset del error | Provider + tokens + cost + raw context |
Archivos cambiados
| Archivo | Tipo de cambio |
|---|---|
blueprints/services/autoplan.py | Nuevo regex en _clean_llm_json (+11 LOC) |
blueprints/services/openrouter_driver.py | Log diagnóstico + retry loop (+36 LOC) |
Véase también
- [[entity—blueprints—service—openrouter-driver]]
- [[entity—blueprints—service—autoplan]]
- [[entity—blueprints—endpoint—autoplan-import]]
- [[entity—blueprints—model—aiprompt]]
- [[concept—autoplan—json-cleanup]]