Harden DeepSeek agent: LiteLLM adapter, DSML/reasoning/embeddings/error fixes
- LiteLLMAdapter (subclasses OpenAIAdapter via _acreate hook): routes DeepSeek through LiteLLM. Opt-in AGENTIC_DEFAULT_MODEL_PROVIDER=litellm. A/B beat the hand-rolled adapter (0 DSML, 0 parse-fails). Defensive chunk.usage getattr, token-estimate usage fallback for billing, quiet litellm logs. - DSML parser: tolerate single/multi fullwidth pipes, honor string="true/false" typed args (openai_adapter fallback when DeepSeek leaks tool calls as text). - Thinking mode: capture and round-trip reasoning_content across turns. - Embeddings: dedicated AGENTIC_EMBEDDINGS_API_KEY (DeepSeek has no embeddings); disable cleanly when unset to avoid per-turn 401. - claude_format: friendly generic error messages to the chat, raw only in logs. - acai agent max_tokens 4096->16384 (whole-file writes no longer truncate); system.md size-based edit policy; strict tools opt-in (off). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -19,6 +19,71 @@ from .sse import EventType, SSEEmitter
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
_GENERIC_ERROR = (
|
||||
"Ha ocurrido un error procesando tu mensaje. Vuelve a intentarlo en unos momentos."
|
||||
)
|
||||
|
||||
# Patrones que el frontend interpreta por sí mismo (login / sesión expirada).
|
||||
# No los genericamos para no romper esas detecciones.
|
||||
_PASSTHROUGH_PATTERNS = (
|
||||
"not logged in",
|
||||
"login required",
|
||||
"authentication required",
|
||||
"no conversation found",
|
||||
)
|
||||
|
||||
|
||||
def friendly_error_message(raw: str, code: str = "") -> str:
|
||||
"""Traduce un error crudo (proveedor/excepción) a un mensaje genérico y
|
||||
localizado para el usuario final, sin filtrar detalles internos.
|
||||
|
||||
Devuelve el texto original sin tocar para los casos de auth/sesión que el
|
||||
frontend ya gestiona por contenido.
|
||||
"""
|
||||
raw = raw or ""
|
||||
text = "{} {}".format(code or "", raw).lower()
|
||||
|
||||
# Auth / sesión: dejar pasar el texto original (lo maneja el frontend)
|
||||
if any(p in text for p in _PASSTHROUGH_PATTERNS):
|
||||
return raw
|
||||
|
||||
# Timeout de ejecución
|
||||
if "timeout" in text or "timed out" in text:
|
||||
return (
|
||||
"La tarea tardó demasiado en completarse. Prueba a dividirla en "
|
||||
"pasos más pequeños o vuelve a intentarlo."
|
||||
)
|
||||
# Saldo insuficiente / facturación del proveedor (402)
|
||||
if (
|
||||
"402" in text
|
||||
or "insufficient balance" in text
|
||||
or "insufficient_quota" in text
|
||||
or "billing" in text
|
||||
):
|
||||
return (
|
||||
"El asistente no está disponible en este momento. Inténtalo de "
|
||||
"nuevo en unos minutos."
|
||||
)
|
||||
# Credenciales del proveedor inválidas (401)
|
||||
if (
|
||||
"401" in text
|
||||
or "invalid_api_key" in text
|
||||
or "incorrect api key" in text
|
||||
or "invalid api key" in text
|
||||
):
|
||||
return (
|
||||
"El asistente no está disponible temporalmente por un problema de "
|
||||
"configuración. Estamos trabajando en ello."
|
||||
)
|
||||
# Límite de peticiones (429)
|
||||
if "429" in text or "rate limit" in text or "rate_limit" in text:
|
||||
return (
|
||||
"Hay mucha demanda en este momento. Espera unos segundos y vuelve "
|
||||
"a intentarlo."
|
||||
)
|
||||
return _GENERIC_ERROR
|
||||
|
||||
|
||||
class ClaudeFormatEmitter:
|
||||
"""Emits events in Claude Code CLI SSE format.
|
||||
|
||||
@@ -304,7 +369,10 @@ class ClaudeFormatEmitter:
|
||||
self._push(session_id, {"type": "done"})
|
||||
|
||||
elif event_type == EventType.ERROR:
|
||||
error_msg = data.get("message", str(data.get("error", "Unknown error")))
|
||||
raw_msg = data.get("message", str(data.get("error", "Unknown error")))
|
||||
user_msg = friendly_error_message(raw_msg, str(data.get("error", "")))
|
||||
# El error real (detalles del proveedor) solo va al log, nunca al cliente.
|
||||
logger.warning("Session %s error (raw): %s", session_id, raw_msg)
|
||||
|
||||
# Close any open block
|
||||
self._close_text_block(session_id)
|
||||
@@ -312,7 +380,7 @@ class ClaudeFormatEmitter:
|
||||
self._push(session_id, {
|
||||
"type": "result",
|
||||
"is_error": True,
|
||||
"result": error_msg,
|
||||
"result": user_msg,
|
||||
"usage": {"input_tokens": 0, "output_tokens": 0, "cache_read_input_tokens": 0, "cache_creation_input_tokens": 0},
|
||||
"total_cost_usd": 0,
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user