feat(short): generación y render de Shorts vía shortsmith
Build & Deploy ResearchOwl / build-and-push (push) Successful in 9s
Build & Deploy ResearchOwl / build-and-push (push) Successful in 9s
Añade /generate short_en y /short_spec. El pipeline genera un shot spec con Haiku, verifica cada cifra, fecha y cita contra los chunks de la sesión, lo renderiza en shortsmith y entrega el MP4 por Telegram junto a un informe de claims. - ShortsmithClient con sondeo y fallback al spec JSON si el render falla - Contrato de plantillas obtenido de GET /templates, no codificado - Comprobación de fundamento determinista, sin LLM - outputs.published_url para enlazar el artículo de Ghost - Normalización de comillas rectas a tipográficas (ver KNOWN-ISSUES.md) Lo que no aparece en los chunks se contrasta contra el ejemplo del prompt: si casa ahí es fuga, no invención, y se informa como tal. El purgado de sesiones se lleva también su MP4. La subida a YouTube queda fuera a propósito: fase 3. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+21
-11
@@ -442,6 +442,24 @@ class ContentProcessor:
|
||||
"""
|
||||
Retrieve most relevant chunks for a query using embeddings + keyword fallback
|
||||
"""
|
||||
top_chunks = await self.rag_chunks(session_id, query, top_k)
|
||||
|
||||
# Build context
|
||||
context_parts = []
|
||||
for chunk in top_chunks:
|
||||
source_label = f"[{chunk.get('source_type', 'web').upper()}] {chunk.get('title', 'Unknown')}"
|
||||
context_parts.append(f"{source_label}:\n{chunk['content']}")
|
||||
|
||||
return "\n\n---\n\n".join(context_parts)
|
||||
|
||||
async def rag_chunks(self, session_id: int, query: str,
|
||||
top_k: int = 20) -> list[dict]:
|
||||
"""Los chunks en sí, no el contexto ya montado.
|
||||
|
||||
Mismo ranking que `rag_query` (que ahora llama aquí). Hace falta para el
|
||||
comprobador de fundamento: comprueba contra EXACTAMENTE el material que
|
||||
se le pasó al modelo, y para eso necesita las filas, con su `url`.
|
||||
"""
|
||||
# Get query embedding
|
||||
query_embedding = await self.ollama.embed(query)
|
||||
|
||||
@@ -462,15 +480,7 @@ class ContentProcessor:
|
||||
scored.append((sim * 0.7 + chunk["quality_score"] * 0.3, chunk))
|
||||
|
||||
scored.sort(key=lambda x: x[0], reverse=True)
|
||||
top_chunks = [c for _, c in scored[:top_k]]
|
||||
else:
|
||||
# Fallback: just use quality score
|
||||
top_chunks = chunks[:top_k]
|
||||
return [c for _, c in scored[:top_k]]
|
||||
|
||||
# Build context
|
||||
context_parts = []
|
||||
for chunk in top_chunks:
|
||||
source_label = f"[{chunk.get('source_type', 'web').upper()}] {chunk.get('title', 'Unknown')}"
|
||||
context_parts.append(f"{source_label}:\n{chunk['content']}")
|
||||
|
||||
return "\n\n---\n\n".join(context_parts)
|
||||
# Fallback: just use quality score
|
||||
return chunks[:top_k]
|
||||
|
||||
Reference in New Issue
Block a user