El resumen de diff generado por Claude se enviaba con parse_mode=MARKDOWN;
texto libre con entidades desbalanceadas provocaba BadRequest y el mensaje
no llegaba. Además el send_message estaba fuera del try/except de _task y el
task se retenía en _active_tasks sin await, así que la excepción se tragaba
sin log alguno.
- _safe_send(): intenta Markdown y reintenta en texto plano si falla
- _task: except de nivel superior que loguea cualquier fallo
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
La purga de sesiones >30d fallaba con FOREIGN KEY constraint failed:
api_usage.session_id referencia research_sessions(id) pero nunca se
borraba antes de la sesión padre (con PRAGMA foreign_keys = ON).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Mismo patrón que cmd_generate: si hay sesión activa registrada
para el chat, consulta por id; si no, fallback a created_at DESC.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Si hay una sesión activa registrada para el chat, se consulta
directamente por id en lugar de por created_at DESC, evitando
que /generate use la sesión más reciente en vez de la actual.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
El session dict se obtiene antes de generator.generate() y puede
contener datos de la sesión anterior. Ahora se extrae el topic
directamente de la línea "Topic:" del header del output generado,
que siempre refleja la sesión actual usada en la generación.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Ghost añade el título del post automáticamente en el frontend,
por lo que el <h1> generado desde el markdown aparecía duplicado.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
El campo "html" en Ghost Admin API v5 (Lexical editor) es de solo
lectura. El contenido se debe enviar via mobiledoc con HTML card,
que Ghost acepta en todas las versiones de v5 y renderiza sin
conversión. Añadidos logs de diagnóstico y validación de HTML vacío.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The synchronous get_transcript() call was blocking the asyncio event
loop indefinitely, freezing the entire bot (including Telegram polling).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Use Claude Haiku (via ANTHROPIC_API_KEY) for all output generation.
Falls back to Ollama qwen2.5:3b if no API key is set.
Also translates all user-turn prompts to Spanish for consistency.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add "Escribe SIEMPRE en español" at the start of all system prompts
(podcast, blog, report, thread) so Ollama generates content in Spanish.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
processor.py: split _score_quality into _score_with_claude and
_score_with_ollama; if ANTHROPIC_API_KEY is set, use Claude Haiku
(claude-haiku-4-5) with max_tokens=10 for fast, accurate 0-10
relevance scoring; falls back to Ollama on any error
requirements.txt: add anthropic>=0.40.0
k8s: ANTHROPIC_API_KEY added to researchowl-secrets and mounted in
deployment; QUALITY_THRESHOLD restored to 0.4 (Claude scoring
is accurate enough to use the threshold)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>