DeepRat commited on
Commit
d290510
·
verified ·
1 Parent(s): 3066400

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +74 -113
main.py CHANGED
@@ -1,5 +1,6 @@
1
  # main.py
2
  import os
 
3
  import logging
4
  from fastapi import FastAPI, HTTPException
5
  from fastapi.middleware.cors import CORSMiddleware
@@ -7,7 +8,7 @@ from fastapi.responses import FileResponse, Response
7
  from fastapi.staticfiles import StaticFiles
8
  from pydantic import BaseModel
9
  import requests
10
- from typing import Optional
11
 
12
  # -------------------------------
13
  # CONFIGURACIÓN DE LOGGING
@@ -19,63 +20,45 @@ logging.basicConfig(
19
  logger = logging.getLogger(__name__)
20
 
21
  # -------------------------------
22
- # CARGA DEL FLOW_API_URL DESDE SECRETS
23
  # -------------------------------
24
  FLOW_API_URL = os.getenv("FLOW_API_URL")
25
- if FLOW_API_URL is None:
26
- raise RuntimeError("❌ FLOW_API_URL no está definido. Agregalo en los Secrets de Hugging Face.")
 
 
 
27
 
28
  logger.info(f"✅ FLOW_API_URL configurado: {FLOW_API_URL[:30]}...")
 
29
 
30
  # -------------------------------
31
  # INICIALIZACIÓN DE LA APP
32
  # -------------------------------
33
  app = FastAPI()
34
-
35
- # Middleware CORS
36
  app.add_middleware(
37
  CORSMiddleware,
38
  allow_origins=["*"],
39
- allow_credentials=True,
40
  allow_methods=["*"],
41
  allow_headers=["*"],
42
  )
43
 
44
- # Montar carpeta estática
45
  app.mount("/static", StaticFiles(directory="static"), name="static")
46
 
47
- # Ruta principal: servir index.html
48
  @app.get("/")
49
  async def serve_index():
50
  return FileResponse("static/index.html")
51
 
52
- # -------------------------------
53
- # LOGO FALLBACK HANDLER
54
- # -------------------------------
55
  @app.get("/static/te.png")
56
  async def serve_logo():
57
  logo_path = "static/te.png"
58
  if os.path.exists(logo_path):
59
  return FileResponse(logo_path)
60
- else:
61
- svg_content = '''<svg width="40" height="40" viewBox="0 0 40 40" xmlns="http://www.w3.org/2000/svg">
62
- <!-- Fondo circular -->
63
- <circle cx="20" cy="20" r="18" fill="#f6ae2d"/>
64
- <!-- Ojo estilizado -->
65
- <g transform="translate(20, 20)">
66
- <path d="M -12 0 Q -6 -6 0 -6 Q 6 -6 12 0 Q 6 6 0 6 Q -6 6 -12 0" fill="#420909" stroke="none"/>
67
- <circle cx="0" cy="0" r="5" fill="#f6ae2d"/>
68
- <circle cx="0" cy="0" r="3" fill="#420909"/>
69
- <circle cx="-1" cy="-1" r="1" fill="white" opacity="0.8"/>
70
- </g>
71
- <text x="20" y="35" font-family="Arial, sans-serif" font-size="8" font-weight="bold"
72
- text-anchor="middle" fill="#420909">TE</text>
73
- </svg>'''
74
- return Response(content=svg_content, media_type="image/svg+xml",
75
- headers={"Cache-Control": "public, max-age=3600"})
76
 
77
  # -------------------------------
78
- # MODELO DE ENTRADA / SALIDA
79
  # -------------------------------
80
  class AnalyzeRequest(BaseModel):
81
  url: str
@@ -86,116 +69,94 @@ class AnalyzeResponse(BaseModel):
86
  error: Optional[str] = None
87
 
88
  # -------------------------------
89
- # ENDPOINT DE ANÁLISIS
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
90
  # -------------------------------
91
  @app.post("/analyze", response_model=AnalyzeResponse)
92
  async def analyze(request: AnalyzeRequest):
93
  logger.info(f"📥 Recibida solicitud de análisis para URL: {request.url}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
94
  try:
95
- payload = {
96
- "input_value": request.url,
97
- "output_type": "chat",
98
- "input_type": "chat",
99
- "tweaks": {}
100
- }
101
- headers = {
102
- "Content-Type": "application/json",
103
- "User-Agent": "TrueEye-HuggingFace-Space/1.0"
104
- }
105
  logger.info("📤 Enviando petición a Langflow...")
106
  logger.debug(f"Payload: {payload}")
107
-
108
- response = requests.post(
109
- FLOW_API_URL,
110
- json=payload,
111
- headers=headers,
112
- timeout=300
113
- )
114
- logger.info(f"📨 Respuesta recibida. Status: {response.status_code}")
115
- response.raise_for_status()
116
-
117
- data = response.json()
118
- logger.debug(f"Respuesta JSON: {data}")
119
-
120
- # Extracción de texto
121
- result_text = None
122
- if isinstance(data, dict) and "result" in data:
123
- result_text = data["result"]
124
- elif isinstance(data, dict) and "outputs" in data:
125
- outputs = data["outputs"]
126
- if outputs and isinstance(outputs, list):
127
- for node in outputs[0].get("outputs", []):
128
- if "message" in node:
129
- msg = node["message"]
130
- result_text = msg.get("text") if isinstance(msg, dict) else str(msg)
131
- break
132
  if not result_text:
133
- result_text = _extract_text_from_response(data) or \
134
- "⚠️ Se procesó la solicitud pero no se pudo extraer el resultado."
135
 
136
  logger.info("✅ Análisis completado exitosamente")
137
  return AnalyzeResponse(result=result_text)
138
 
139
  except requests.exceptions.Timeout:
140
  logger.error("⏱️ Timeout en la petición a Langflow")
141
- return AnalyzeResponse(
142
- result="❌ Error: La solicitud tardó demasiado tiempo.",
143
- success=False,
144
- error="timeout"
145
- )
146
-
147
- except requests.exceptions.ConnectionError as e:
148
- logger.error(f"🔌 Error de conexión: {e}")
149
- return AnalyzeResponse(
150
- result="❌ Error: No se pudo conectar con el servicio de análisis.",
151
- success=False,
152
- error="connection"
153
- )
154
 
155
  except requests.exceptions.HTTPError as e:
156
- # <-- Aquí mejora el logging para capturar el body del 500 interno
157
- status = e.response.status_code if e.response else "unknown"
158
- body = e.response.text if e.response else ""
159
- logger.error(f"🚫 Error HTTP {status} al llamar al Flow. Body de error:\n{body}")
160
  return AnalyzeResponse(
161
- result=f"❌ Error del servidor (HTTP {status}). Revisa los logs internos.",
162
  success=False,
163
- error=f"http_{status}"
164
  )
165
 
166
  except Exception as e:
167
- logger.exception(f"💥 Error inesperado: {e}")
168
- return AnalyzeResponse(
169
- result=f"❌ Error inesperado: {str(e)}",
170
- success=False,
171
- error="unknown"
172
- )
173
-
174
- def _extract_text_from_response(data):
175
- if isinstance(data, str):
176
- return data
177
- if isinstance(data, dict):
178
- for key in ['text', 'message', 'result', 'output', 'content']:
179
- if key in data:
180
- return _extract_text_from_response(data[key]) if isinstance(data[key], (dict, list)) else data[key]
181
- for value in data.values():
182
- txt = _extract_text_from_response(value)
183
- if txt:
184
- return txt
185
- if isinstance(data, list):
186
- for item in data:
187
- txt = _extract_text_from_response(item)
188
- if txt:
189
- return txt
190
- return None
191
 
192
  # -------------------------------
193
- # ENDPOINT DE SALUD
194
  # -------------------------------
195
  @app.get("/health")
196
  async def health_check():
197
  return {
198
  "status": "healthy",
199
- "flow_configured": bool(FLOW_API_URL),
200
- "service": "TrueEye Reports"
201
  }
 
1
  # main.py
2
  import os
3
+ import uuid
4
  import logging
5
  from fastapi import FastAPI, HTTPException
6
  from fastapi.middleware.cors import CORSMiddleware
 
8
  from fastapi.staticfiles import StaticFiles
9
  from pydantic import BaseModel
10
  import requests
11
+ from typing import Optional, Any, Dict
12
 
13
  # -------------------------------
14
  # CONFIGURACIÓN DE LOGGING
 
20
  logger = logging.getLogger(__name__)
21
 
22
  # -------------------------------
23
+ # CARGA DE SECRETS
24
  # -------------------------------
25
  FLOW_API_URL = os.getenv("FLOW_API_URL")
26
+ API_KEY = os.getenv("LANGFLOW_API_KEY")
27
+ if not FLOW_API_URL:
28
+ raise RuntimeError("❌ FLOW_API_URL no está definido. Agrégalo en los Secrets de Hugging Face.")
29
+ if not API_KEY:
30
+ raise RuntimeError("❌ LANGFLOW_API_KEY no está definido. Agrégalo en los Secrets de Hugging Face.")
31
 
32
  logger.info(f"✅ FLOW_API_URL configurado: {FLOW_API_URL[:30]}...")
33
+ logger.info(f"✅ LANGFLOW_API_KEY cargada (longitud {len(API_KEY)})")
34
 
35
  # -------------------------------
36
  # INICIALIZACIÓN DE LA APP
37
  # -------------------------------
38
  app = FastAPI()
 
 
39
  app.add_middleware(
40
  CORSMiddleware,
41
  allow_origins=["*"],
 
42
  allow_methods=["*"],
43
  allow_headers=["*"],
44
  )
45
 
 
46
  app.mount("/static", StaticFiles(directory="static"), name="static")
47
 
 
48
  @app.get("/")
49
  async def serve_index():
50
  return FileResponse("static/index.html")
51
 
 
 
 
52
  @app.get("/static/te.png")
53
  async 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 SVG
58
+ return Response(svg, media_type="image/svg+xml", headers={"Cache-Control":"public, max-age=3600"})
 
 
 
 
 
 
 
 
 
 
 
 
 
 
59
 
60
  # -------------------------------
61
+ # MODELOS DE Pydantic
62
  # -------------------------------
63
  class AnalyzeRequest(BaseModel):
64
  url: str
 
69
  error: Optional[str] = None
70
 
71
  # -------------------------------
72
+ # HELPER DE EXTRACCIÓN
73
+ # -------------------------------
74
+ def _extract_text_from_response(data: Any) -> Optional[str]:
75
+ if isinstance(data, str):
76
+ return data
77
+ 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 val
82
+ elif val is not None:
83
+ txt = _extract_text_from_response(val)
84
+ if txt:
85
+ return txt
86
+ for val in data.values():
87
+ txt = _extract_text_from_response(val)
88
+ if txt:
89
+ return txt
90
+ if isinstance(data, list):
91
+ for item in data:
92
+ txt = _extract_text_from_response(item)
93
+ if txt:
94
+ return txt
95
+ return None
96
+
97
+ # -------------------------------
98
+ # ENDPOINT /analyze
99
  # -------------------------------
100
  @app.post("/analyze", response_model=AnalyzeResponse)
101
  async 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": None
111
+ }
112
+ headers = {
113
+ "Content-Type": "application/json",
114
+ "User-Agent": "TrueEye-HuggingFace-Space/1.0",
115
+ "x-api-key": API_KEY
116
+ }
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 final
128
+ 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
+ # HEALTHCHECK
155
  # -------------------------------
156
  @app.get("/health")
157
  async def health_check():
158
  return {
159
  "status": "healthy",
160
+ "flow_url": bool(FLOW_API_URL),
161
+ "service": "TrueEye Reports"
162
  }