File size: 5,565 Bytes
29384ce
 
d290510
e5c3ef3
 
29384ce
6d68582
29384ce
 
 
d290510
e5c3ef3
 
 
 
 
 
 
 
 
29384ce
 
d290510
29384ce
 
d290510
 
 
 
 
e5c3ef3
3066400
d290510
f8a4504
29384ce
 
 
 
 
 
e5c3ef3
29384ce
 
 
 
 
 
 
 
 
 
6d68582
 
 
 
 
d290510
 
6d68582
29384ce
d290510
29384ce
 
 
 
e5c3ef3
 
 
 
 
29384ce
d290510
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29384ce
e5c3ef3
29384ce
e5c3ef3
d290510
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29384ce
3066400
 
d290510
 
 
 
 
 
 
 
e5c3ef3
d290510
 
f8a4504
e5c3ef3
 
f8a4504
e5c3ef3
 
d290510
3066400
e5c3ef3
d290510
 
e5c3ef3
d290510
3066400
d290510
e5c3ef3
3066400
29384ce
d290510
 
e5c3ef3
 
d290510
e5c3ef3
 
 
 
 
d290510
 
e5c3ef3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
# 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 = '''<svg width="40" height="40" ...>...</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 "<no body>"
        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"
    }