# src/expon/presentation/interfaces/rest/controllers/multimodal_controller.py from fastapi import APIRouter, UploadFile, File, Form, HTTPException, Depends from pydantic import BaseModel from typing import Optional, Dict import os from tempfile import NamedTemporaryFile from src.expon.presentation.domain.services.multimodal_service import MultimodalService # 🔐 Usa la MISMA dependencia de auth del backend from src.expon.iam.infrastructure.authorization.sfs.auth_bearer import get_current_user router = APIRouter() _service = MultimodalService() class PredictResponse(BaseModel): dominant_emotion: str emotion_probabilities: Dict[str, float] transcript: Optional[str] # ← puede no haber texto alpha: float temp_text: float provider: str @router.post("/predict", response_model=PredictResponse) async def predict_multimodal( audio: UploadFile = File(...), text: Optional[str] = Form( None, description="Texto opcional para fusión. Déjalo vacío para usar solo audio.", example=None, ), user=Depends(get_current_user), # ← exige login con tu flujo real ): # 🔹 Sanitiza el placeholder de Swagger y vacíos if text is not None: t = text.strip() if not t or t.lower() == "string": text = None ext = (os.path.splitext(audio.filename or "")[-1] or "").lower() if ext not in [".wav", ".mp3", ".m4a"]: raise HTTPException(status_code=400, detail="Formato no soportado (.wav, .mp3, .m4a)") with NamedTemporaryFile(delete=False, suffix=ext) as tmp: tmp.write(await audio.read()) tmp_path = tmp.name try: result = _service.predict(tmp_path, text) return PredictResponse(**result) finally: try: os.remove(tmp_path) except Exception: pass