# main.py import os import uuid import logging from fastapi import FastAPI, HTTPException from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import FileResponse, Response from fastapi.staticfiles import StaticFiles from pydantic import BaseModel import requests from typing import Optional, Any, Dict # ------------------------------- # CONFIGURACIÓN DE LOGGING # ------------------------------- logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' ) logger = logging.getLogger(__name__) # ------------------------------- # CARGA DE SECRETS # ------------------------------- FLOW_API_URL = os.getenv("FLOW_API_URL") API_KEY = os.getenv("LANGFLOW_API_KEY") if not FLOW_API_URL: raise RuntimeError("❌ FLOW_API_URL no está definido. Agrégalo en los Secrets de Hugging Face.") if not API_KEY: raise RuntimeError("❌ LANGFLOW_API_KEY no está definido. Agrégalo en los Secrets de Hugging Face.") logger.info(f"✅ FLOW_API_URL configurado: {FLOW_API_URL[:30]}...") logger.info(f"✅ LANGFLOW_API_KEY cargada (longitud {len(API_KEY)})") # ------------------------------- # INICIALIZACIÓN DE LA APP # ------------------------------- app = FastAPI() app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"], ) app.mount("/static", StaticFiles(directory="static"), name="static") @app.get("/") async def serve_index(): return FileResponse("static/index.html") @app.get("/static/te.png") async def serve_logo(): logo_path = "static/te.png" if os.path.exists(logo_path): return FileResponse(logo_path) svg = '''...''' # placeholder SVG return Response(svg, media_type="image/svg+xml", headers={"Cache-Control":"public, max-age=3600"}) # ------------------------------- # MODELOS DE Pydantic # ------------------------------- class AnalyzeRequest(BaseModel): url: str class AnalyzeResponse(BaseModel): result: str success: bool = True error: Optional[str] = None # ------------------------------- # HELPER DE EXTRACCIÓN # ------------------------------- def _extract_text_from_response(data: Any) -> Optional[str]: if isinstance(data, str): return data if isinstance(data, dict): for key in ("outputs","result","message","text","content"): val = data.get(key) if isinstance(val, str): return val elif val is not None: txt = _extract_text_from_response(val) if txt: return txt for val in data.values(): txt = _extract_text_from_response(val) if txt: return txt if isinstance(data, list): for item in data: txt = _extract_text_from_response(item) if txt: return txt return None # ------------------------------- # ENDPOINT /analyze # ------------------------------- @app.post("/analyze", response_model=AnalyzeResponse) async def analyze(request: AnalyzeRequest): logger.info(f"📥 Recibida solicitud de análisis para URL: {request.url}") session_id = str(uuid.uuid4()) payload = { "input_value": request.url, "input_type": "chat", "output_type": "chat", "session_id": session_id, "output_component": "", "tweaks": None } headers = { "Content-Type": "application/json", "User-Agent": "TrueEye-HuggingFace-Space/1.0", "x-api-key": API_KEY } try: logger.info("📤 Enviando petición a Langflow...") logger.debug(f"Payload: {payload}") resp = requests.post(FLOW_API_URL, json=payload, headers=headers, timeout=300) logger.info(f"📨 Respuesta recibida. Status: {resp.status_code}") resp.raise_for_status() data = resp.json() logger.debug(f"Respuesta JSON completa: {data}") # Extraer texto final result_text = _extract_text_from_response(data) if not result_text: logger.warning("⚠️ No se pudo extraer texto de la respuesta") result_text = "⚠️ Se procesó la solicitud pero no se pudo extraer el resultado." logger.info("✅ Análisis completado exitosamente") return AnalyzeResponse(result=result_text) except requests.exceptions.Timeout: logger.error("⏱️ Timeout en la petición a Langflow") return AnalyzeResponse(result="❌ Error: Timeout (el análisis tardó demasiado)", success=False, error="timeout") except requests.exceptions.HTTPError as e: body = e.response.text if e.response is not None else "" logger.error(f"🚫 Error HTTP {e.response.status_code if e.response else ''}: {body}") return AnalyzeResponse( result=f"❌ Error HTTP al llamar al Flow: {body}", success=False, error=f"http_{e.response.status_code if e.response else 'unknown'}" ) except Exception as e: logger.exception("💥 Error inesperado en /analyze") return AnalyzeResponse(result=f"❌ Error inesperado: {e}", success=False, error="unknown") # ------------------------------- # HEALTHCHECK # ------------------------------- @app.get("/health") async def health_check(): return { "status": "healthy", "flow_url": bool(FLOW_API_URL), "service": "TrueEye Reports" }