P0 contexto: ventana por modelo + recuperación ante overflow + self-heal del catálogo
Que las conversaciones largas no se rompan ni gasten de más: Ventana de contexto por modelo (antes: budget estático 120k/200k para todos): - cost.resolve_context_window: lee context_length del catálogo OpenRouter/DeepSeek en Redis, con fallback a litellm. config.budget_for_window deriva el budget de la ventana real (window - max_output - reserve). build_context lo aplica por turno (param model_id) en vez del fijo de settings. - Self-heal del catálogo OpenRouter: el admin panel lo cachea con TTL 1h y solo lo repuebla al abrir su ventana de IA → en runtime caducaba y se perdían ventana y precio. Ahora cost._get_catalog lo refresca solo (fetch público, mismo shape, cooldown 5min, TTL 24h). Arregla también el coste (caía al fijo). Recuperación ante overflow: - adapters.base.ContextOverflowError; openai_adapter traduce el error de context-length del proveedor (init e iteración del stream). - base.py: retry proactivo que recompacta hasta caber en la ventana ANTES de llamar al LLM; si ni así cabe → error accionable (no rompe la sesión). - engine.py: mensaje user-facing claro (modelo + ventana). Tests: ventana/budget, self-heal (mockeado), overflow, y sesión REAL de Redis. 106 verdes. evals/: harness para evaluar al agente acai-code (driver + README + resultados). Comparativa kimi vs deepseek vs glm (deepseek-v4-pro high = mejor calidad/precio). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -155,5 +155,24 @@ class Settings(BaseSettings):
|
||||
return min(self.compaction_threshold_tokens, self.effective_context_budget)
|
||||
return max(1, int(self.effective_context_budget * self.compaction_threshold_ratio))
|
||||
|
||||
def budget_for_window(self, window: int, max_output: int | None = None) -> int:
|
||||
"""Budget de contexto para la ventana REAL del modelo activo.
|
||||
|
||||
Misma fórmula que `effective_context_budget` (`window - max_output -
|
||||
reserve`) pero parametrizada por la ventana del modelo del turno. Si la
|
||||
ventana no es válida, cae al budget estático. Un override explícito
|
||||
(`context_max_tokens`) siempre manda (lo aplica el caller)."""
|
||||
if window <= 0:
|
||||
return self.effective_context_budget
|
||||
out = self.model_max_output_tokens if max_output is None else max_output
|
||||
reserve = int(window * self.context_reserve_ratio)
|
||||
return max(1, window - max(0, out) - max(0, reserve))
|
||||
|
||||
def compaction_threshold_for(self, budget: int) -> int:
|
||||
"""Umbral de compactación para un budget dado (ratio configurable)."""
|
||||
if self.compaction_threshold_tokens > 0:
|
||||
return min(self.compaction_threshold_tokens, budget)
|
||||
return max(1, int(budget * self.compaction_threshold_ratio))
|
||||
|
||||
|
||||
settings = Settings()
|
||||
|
||||
Reference in New Issue
Block a user