feat(short): la paleta de audio viva — GET /audio en el contrato, el prompt y el bucle de edición
Build & Deploy ResearchOwl / build-and-push (push) Successful in 9s
Build & Deploy ResearchOwl / build-and-push (push) Successful in 9s
shortsmith ya compone la banda sonora (sonar); ahora publica una paleta (pulse, static) y este repo la consume en vivo: el prompt la ofrece con sus notas de mood, validate_spec la usa como fuente de verdad para audio.preset, y editar el preset en /short_spec es la manera gratis de escucharlas. Sin /audio (404 o caída) todo cae a la paleta base y nada deja de renderizar. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
1fd0c1b3d6
commit
93a506b636
+21
-4
@@ -28,7 +28,8 @@ from src.config import settings
|
||||
from src.db.database import OutputType, ResearchDB
|
||||
from src.generator.grounding import GroundingReport, check_grounding
|
||||
from src.generator.shortsmith import (
|
||||
ShortsmithClient, ShortsmithError, ShortsmithRejected, ShortsmithUnavailable,
|
||||
BASELINE_PRESETS, ShortsmithClient, ShortsmithError, ShortsmithRejected,
|
||||
ShortsmithUnavailable,
|
||||
)
|
||||
from src.generator.shortspec import ShortSpecWriter, SpecWriteFailed
|
||||
from src.generator.spec_contract import SpecInvalid, editorial_notes, validate_spec
|
||||
@@ -138,6 +139,15 @@ class ShortProducer:
|
||||
directory.mkdir(parents=True, exist_ok=True)
|
||||
return directory / f"{session_id}.mp4"
|
||||
|
||||
async def _presets(self) -> dict[str, str]:
|
||||
"""La paleta de audio, en vivo. Nunca tumba nada: sin ella se ofrece la
|
||||
base y el Short sale con sonar, que es lo que salía siempre."""
|
||||
try:
|
||||
return await self.client.audio_presets()
|
||||
except Exception as e:
|
||||
logger.warning("GET /audio falló — paleta base", error=str(e))
|
||||
return dict(BASELINE_PRESETS)
|
||||
|
||||
# --- pipeline -----------------------------------------------------------
|
||||
|
||||
async def produce(self, session_id: int,
|
||||
@@ -171,15 +181,19 @@ class ShortProducer:
|
||||
logger.warning("Short sin URL de artículo — se sigue con el dominio pelado",
|
||||
session_id=session_id)
|
||||
|
||||
# 2. El contrato, en vivo. Sin él no hay prompt que escribir.
|
||||
# 2. El contrato, en vivo. Sin él no hay prompt que escribir. La paleta
|
||||
# de audio mejora el prompt pero no lo define: si /audio falla, se
|
||||
# ofrece la paleta base y el render sale igual.
|
||||
templates = await self.client.templates()
|
||||
presets = await self._presets()
|
||||
|
||||
# 3. El spec.
|
||||
started = time.monotonic()
|
||||
llm_call = self.llm_override or self._llm_call(session_id)
|
||||
writer = ShortSpecWriter(
|
||||
llm_call, templates,
|
||||
refresh_templates=lambda: self.client.templates(refresh=True))
|
||||
refresh_templates=lambda: self.client.templates(refresh=True),
|
||||
presets=presets)
|
||||
try:
|
||||
written = await writer.write(
|
||||
topic, context, article_url=result.article_url,
|
||||
@@ -251,9 +265,12 @@ class ShortProducer:
|
||||
result = ShortResult(topic=topic, spec=spec)
|
||||
|
||||
# 1. El contrato, en vivo — las mismas rutas verbatim que ve el modelo.
|
||||
# La paleta también: editar audio.preset a "pulse" es justo el tipo
|
||||
# de retoque para el que existe este camino.
|
||||
templates = await self.client.templates()
|
||||
presets = await self._presets()
|
||||
try:
|
||||
validate_spec(spec, templates)
|
||||
validate_spec(spec, templates, presets=presets)
|
||||
except SpecInvalid as e:
|
||||
result.failure = ("El spec editado no pasa el contrato: "
|
||||
+ "; ".join(e.errors[:6]))
|
||||
|
||||
Reference in New Issue
Block a user