DeepRat/TrueEye_Reports
0
1# main.py2import os3import uuid4import logging5from fastapi import FastAPI, HTTPException6from fastapi.middleware.cors import CORSMiddleware7from fastapi.responses import FileResponse, Response8from fastapi.staticfiles import StaticFiles9from pydantic import BaseModel10import requests11from typing import Optional, Any, Dict12 13# -------------------------------14# CONFIGURACIÓN DE LOGGING15# -------------------------------16logging.basicConfig(17 level=logging.INFO,18 format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'19)20logger = logging.getLogger(__name__)21 22# -------------------------------23# CARGA DE SECRETS24# -------------------------------25FLOW_API_URL = os.getenv("FLOW_API_URL")26API_KEY = os.getenv("LANGFLOW_API_KEY")27if not FLOW_API_URL:28 raise RuntimeError("❌ FLOW_API_URL no está definido. Agrégalo en los Secrets de Hugging Face.")29if not API_KEY:30 raise RuntimeError("❌ LANGFLOW_API_KEY no está definido. Agrégalo en los Secrets de Hugging Face.")31 32logger.info(f"✅ FLOW_API_URL configurado: {FLOW_API_URL[:30]}...")33logger.info(f"✅ LANGFLOW_API_KEY cargada (longitud {len(API_KEY)})")34 35# -------------------------------36# INICIALIZACIÓN DE LA APP37# -------------------------------38app = FastAPI()39app.add_middleware(40 CORSMiddleware,41 allow_origins=["*"],42 allow_methods=["*"],43 allow_headers=["*"],44)45 46app.mount("/static", StaticFiles(directory="static"), name="static")47 48@app.get("/")49async def serve_index():50 return FileResponse("static/index.html")51 52@app.get("/static/te.png")53async def serve_logo():54 logo_path = "static/te.png"55 if os.path.exists(logo_path):56 return FileResponse(logo_path)57 svg = '''<svg width="40" height="40" ...>...</svg>''' # placeholder SVG58 return Response(svg, media_type="image/svg+xml", headers={"Cache-Control":"public, max-age=3600"})59 60# -------------------------------61# MODELOS DE Pydantic62# -------------------------------63class AnalyzeRequest(BaseModel):64 url: str65 66class AnalyzeResponse(BaseModel):67 result: str68 success: bool = True69 error: Optional[str] = None70 71# -------------------------------72# HELPER DE EXTRACCIÓN73# -------------------------------74def _extract_text_from_response(data: Any) -> Optional[str]:75 if isinstance(data, str):76 return data77 if isinstance(data, dict):78 for key in ("outputs","result","message","text","content"):79 val = data.get(key)80 if isinstance(val, str):81 return val82 elif val is not None:83 txt = _extract_text_from_response(val)84 if txt:85 return txt86 for val in data.values():87 txt = _extract_text_from_response(val)88 if txt:89 return txt90 if isinstance(data, list):91 for item in data:92 txt = _extract_text_from_response(item)93 if txt:94 return txt95 return None96 97# -------------------------------98# ENDPOINT /analyze99# -------------------------------100@app.post("/analyze", response_model=AnalyzeResponse)101async def analyze(request: AnalyzeRequest):102 logger.info(f"📥 Recibida solicitud de análisis para URL: {request.url}")103 session_id = str(uuid.uuid4())104 payload = {105 "input_value": request.url,106 "input_type": "chat",107 "output_type": "chat",108 "session_id": session_id,109 "output_component": "",110 "tweaks": None111 }112 headers = {113 "Content-Type": "application/json",114 "User-Agent": "TrueEye-HuggingFace-Space/1.0",115 "x-api-key": API_KEY116 }117 118 try:119 logger.info("📤 Enviando petición a Langflow...")120 logger.debug(f"Payload: {payload}")121 resp = requests.post(FLOW_API_URL, json=payload, headers=headers, timeout=300)122 logger.info(f"📨 Respuesta recibida. Status: {resp.status_code}")123 resp.raise_for_status()124 data = resp.json()125 logger.debug(f"Respuesta JSON completa: {data}")126 127 # Extraer texto final128 result_text = _extract_text_from_response(data)129 if not result_text:130 logger.warning("⚠️ No se pudo extraer texto de la respuesta")131 result_text = "⚠️ Se procesó la solicitud pero no se pudo extraer el resultado."132 133 logger.info("✅ Análisis completado exitosamente")134 return AnalyzeResponse(result=result_text)135 136 except requests.exceptions.Timeout:137 logger.error("⏱️ Timeout en la petición a Langflow")138 return AnalyzeResponse(result="❌ Error: Timeout (el análisis tardó demasiado)", success=False, error="timeout")139 140 except requests.exceptions.HTTPError as e:141 body = e.response.text if e.response is not None else "<no body>"142 logger.error(f"🚫 Error HTTP {e.response.status_code if e.response else ''}: {body}")143 return AnalyzeResponse(144 result=f"❌ Error HTTP al llamar al Flow: {body}",145 success=False,146 error=f"http_{e.response.status_code if e.response else 'unknown'}"147 )148 149 except Exception as e:150 logger.exception("💥 Error inesperado en /analyze")151 return AnalyzeResponse(result=f"❌ Error inesperado: {e}", success=False, error="unknown")152 153# -------------------------------154# HEALTHCHECK155# -------------------------------156@app.get("/health")157async def health_check():158 return {159 "status": "healthy",160 "flow_url": bool(FLOW_API_URL),161 "service": "TrueEye Reports"162 }