|
|
from __future__ import annotations |
|
|
|
|
|
from pathlib import Path |
|
|
from typing import Optional |
|
|
|
|
|
import os |
|
|
import yaml |
|
|
from fastapi import FastAPI, HTTPException, APIRouter, UploadFile, File, Query |
|
|
from fastapi.middleware.cors import CORSMiddleware |
|
|
from pydantic import BaseModel, Field |
|
|
|
|
|
from refinement.multiagent_refinement import ( |
|
|
execute_refinement, |
|
|
execute_refinement_for_video, |
|
|
_load_refinement_flags, |
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ROOT = Path(__file__).resolve().parent |
|
|
CONFIG_PATH = "config.yaml" |
|
|
router = APIRouter(prefix="/refinement", tags=["Refinement Process"]) |
|
|
|
|
|
|
|
|
def _load_engine_token() -> Optional[str]: |
|
|
"""Carga el token compartido del engine desde config.yaml o variables de entorno. |
|
|
|
|
|
Sigue la misma convención que engine/api.py: usa API_SHARED_TOKEN si está |
|
|
definido por entorno; en caso contrario, intenta leer demo/config.yaml. |
|
|
""" |
|
|
|
|
|
env_token = os.getenv("API_SHARED_TOKEN") |
|
|
if env_token: |
|
|
return env_token |
|
|
|
|
|
|
|
|
try: |
|
|
repo_root = ROOT |
|
|
|
|
|
demo_cfg = repo_root / "demo" / "config.yaml" |
|
|
if demo_cfg.exists(): |
|
|
with demo_cfg.open("r", encoding="utf-8") as f: |
|
|
cfg = yaml.safe_load(f) or {} |
|
|
api_cfg = cfg.get("api", {}) or {} |
|
|
token = api_cfg.get("token") |
|
|
if token and isinstance(token, str): |
|
|
|
|
|
|
|
|
if "${" in token and "}" in token: |
|
|
return None |
|
|
return token |
|
|
except Exception: |
|
|
pass |
|
|
|
|
|
return None |
|
|
|
|
|
|
|
|
ENGINE_TOKEN = _load_engine_token() |
|
|
|
|
|
|
|
|
def _assert_valid_token(token: str | None) -> None: |
|
|
expected = ENGINE_TOKEN |
|
|
if not expected: |
|
|
|
|
|
return |
|
|
if not token or token != expected: |
|
|
raise HTTPException(status_code=401, detail="Invalid or missing engine token") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ApplyRefinementRequest(BaseModel): |
|
|
token: Optional[str] = Field(None, description="Engine shared token") |
|
|
srt_content: Optional[str] = Field( |
|
|
None, |
|
|
description=( |
|
|
"Contenido del SRT a refinar. Opcional si se proporciona sha1sum+version, " |
|
|
"en cuyo caso se leerá el SRT desde audiodescriptions.db." |
|
|
), |
|
|
) |
|
|
sha1sum: Optional[str] = Field( |
|
|
None, |
|
|
description=( |
|
|
"Identificador sha1sum del vídeo. Si se proporciona junto con version, " |
|
|
"se utilizará el pipeline basat en BDs (audiodescriptions.db, casting.db, scenarios.db)." |
|
|
), |
|
|
) |
|
|
version: Optional[str] = Field( |
|
|
None, |
|
|
description=( |
|
|
"Versió de l'audiodescripció (p.ex. 'MoE', 'Salamandra', 'HITL'). " |
|
|
"Necessària si s'especifica sha1sum per utilitzar el pipeline de vídeo." |
|
|
), |
|
|
) |
|
|
reflection_enabled: bool = Field(True, description="Activar paso de reflection") |
|
|
reflexion_enabled: bool = Field(False, description="Activar paso de reflexion") |
|
|
introspection_enabled: bool = Field(False, description="Activar paso de introspection") |
|
|
|
|
|
|
|
|
class ApplyRefinementResponse(BaseModel): |
|
|
refined_srt: str |
|
|
|
|
|
|
|
|
class TrainMultiagentRefinementRequest(BaseModel): |
|
|
audiodescriptions_db_path: str = Field(..., description="Ruta a la base de datos tipo audiodescriptions.db") |
|
|
videos_db_path: str = Field(..., description="Ruta a la base de datos tipo videos.db") |
|
|
casting_db_path: str = Field(..., description="Ruta a la base de datos tipo casting.db") |
|
|
scenarios_db_path: str = Field(..., description="Ruta a la base de datos tipo scenarios.db") |
|
|
system_to_train: str = Field(..., pattern="^(reflexion|introspection)$", description="Sistema a entrenar: 'reflexion' o 'introspection'") |
|
|
|
|
|
|
|
|
class TrainMultiagentRefinementResponse(BaseModel): |
|
|
ok: bool |
|
|
detail: str |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.post("/apply_refinement", tags=["Refinement Process"], response_model=ApplyRefinementResponse) |
|
|
def apply_refinement(payload: ApplyRefinementRequest) -> ApplyRefinementResponse: |
|
|
"""Aplica el pipeline multi‑agente de refinamiento sobre un SRT. |
|
|
|
|
|
- Valida el token del engine. |
|
|
- Aplica los pasos reflection/reflexion/introspection según los flags |
|
|
recibidos en la petición. |
|
|
- Devuelve el SRT refinado. |
|
|
""" |
|
|
|
|
|
_assert_valid_token(payload.token) |
|
|
|
|
|
|
|
|
|
|
|
flags = _load_refinement_flags() |
|
|
flags["reflection_enabled"] = bool(payload.reflection_enabled) |
|
|
flags["reflexion_enabled"] = bool(payload.reflexion_enabled) |
|
|
flags["introspection_enabled"] = bool(payload.introspection_enabled) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
repo_root = ROOT |
|
|
demo_cfg = repo_root / "demo" / "config.yaml" |
|
|
if not demo_cfg.exists(): |
|
|
raise HTTPException(status_code=500, detail="demo/config.yaml not found") |
|
|
|
|
|
original_yaml = demo_cfg.read_text(encoding="utf-8") |
|
|
try: |
|
|
cfg = yaml.safe_load(original_yaml) or {} |
|
|
ref_cfg = cfg.get("refinement", {}) or {} |
|
|
ref_cfg["reflection_enabled"] = flags["reflection_enabled"] |
|
|
ref_cfg["reflexion_enabled"] = flags["reflexion_enabled"] |
|
|
ref_cfg["introspection_enabled"] = flags["introspection_enabled"] |
|
|
cfg["refinement"] = ref_cfg |
|
|
demo_cfg.write_text(yaml.safe_dump(cfg, allow_unicode=True), encoding="utf-8") |
|
|
|
|
|
|
|
|
if payload.sha1sum and payload.version: |
|
|
refined = execute_refinement_for_video( |
|
|
payload.sha1sum, |
|
|
payload.version, |
|
|
config_path=demo_cfg, |
|
|
) |
|
|
else: |
|
|
if not payload.srt_content: |
|
|
raise HTTPException( |
|
|
status_code=400, |
|
|
detail=( |
|
|
"Cal proporcionar o bé sha1sum+version, o bé srt_content " |
|
|
"per poder aplicar el refinament." |
|
|
), |
|
|
) |
|
|
refined = execute_refinement(payload.srt_content, config_path=demo_cfg) |
|
|
finally: |
|
|
|
|
|
demo_cfg.write_text(original_yaml, encoding="utf-8") |
|
|
|
|
|
return ApplyRefinementResponse(refined_srt=refined) |
|
|
|
|
|
|
|
|
@router.post("/train_multiagent_refinement", tags=["Refinement Process"], response_model=TrainMultiagentRefinementResponse) |
|
|
def train_multiagent_refinement(payload: TrainMultiagentRefinementRequest) -> TrainMultiagentRefinementResponse: |
|
|
"""Endpoint placeholder para entrenar els sistemes de reflexion / introspection. |
|
|
|
|
|
De moment no implementa cap lògica; simplement valida la càrrega i retorna |
|
|
un missatge indicant que és un stub. |
|
|
""" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return TrainMultiagentRefinementResponse( |
|
|
ok=True, |
|
|
detail=( |
|
|
"train_multiagent_refinement està definit com a stub; encara no s'ha " |
|
|
"implementat la lògica d'entrenament per als sistemes 'reflexion' o 'introspection'." |
|
|
), |
|
|
) |
|
|
|