Migue1804 commited on
Commit
617f328
·
verified ·
1 Parent(s): fee09d5

Upload 2 files

Browse files
Files changed (2) hide show
  1. app.py +590 -0
  2. requirements.txt +4 -0
app.py ADDED
@@ -0,0 +1,590 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import pandas as pd
3
+ import plotly.express as px
4
+ import plotly.graph_objects as go
5
+ from plotly.subplots import make_subplots
6
+ import requests
7
+ from prophet import Prophet
8
+ from datetime import datetime
9
+ import numpy as np
10
+
11
+ # Configuración de la aplicación
12
+ st.set_page_config(page_title="Análisis de Datos del Banco Mundial", layout="wide")
13
+ st.cache_data.clear()
14
+
15
+ # Diccionario de indicadores por área de enfoque
16
+ INDICADORES = {
17
+ "Personas": [
18
+ {
19
+ "Indicador": "SP.POP.TOTL",
20
+ "Nombre": "Población Total"
21
+ },
22
+ {
23
+ "Indicador": "SP.POP.GROW",
24
+ "Nombre": "Crecimiento Poblacional (%)"
25
+ },
26
+ {
27
+ "Indicador": "SP.RUR.TOTL.ZS",
28
+ "Nombre": "Porcentaje de Población Rural (%)"
29
+ },
30
+ {
31
+ "Indicador": "SP.DYN.LE00.IN",
32
+ "Nombre": "Esperanza de Vida al Nacer (años)"
33
+ },
34
+ {
35
+ "Indicador": "SP.POP.1564.TO",
36
+ "Nombre": "Población en Edad de Trabajar"
37
+ }
38
+ ],
39
+ "Prosperidad": [
40
+ {
41
+ "Indicador": "NY.GDP.PCAP.CD",
42
+ "Nombre": "PIB per cápita (US$)"
43
+ },
44
+ {
45
+ "Indicador": "NY.GDP.MKTP.CD",
46
+ "Nombre": "PIB Total (US$)"
47
+ },
48
+ {
49
+ "Indicador": "SL.UEM.TOTL.ZS",
50
+ "Nombre": "Tasa de Desempleo (%)"
51
+ },
52
+ {
53
+ "Indicador": "SI.POV.DDAY",
54
+ "Nombre": "Porcentaje de Población en Pobreza (%)"
55
+ },
56
+ {
57
+ "Indicador": "NE.EXP.GNFS.ZS",
58
+ "Nombre": "Exportaciones de Bienes y Servicios (% del PIB)"
59
+ }
60
+ ],
61
+ "Planeta": [
62
+ {
63
+ "Indicador": "AG.LND.FRST.ZS",
64
+ "Nombre": "Tasa de deforestación (%) anual"
65
+ },
66
+ {
67
+ "Indicador": "EN.ATM.PM25.MC.M3",
68
+ "Nombre": "Concentración de partículas PM2.5 (µg/m³)"
69
+ },
70
+ {
71
+ "Indicador": "EG.USE.PCAP.KG.OE",
72
+ "Nombre": "Uso de Energía per Cápita (kg petróleo equiv.)"
73
+ },
74
+ {
75
+ "Indicador": "ER.LND.PTLD.ZS",
76
+ "Nombre": "Áreas Terrestres Protegidas (%)"
77
+ },
78
+ {
79
+ "Indicador": "AG.LND.TOTL.K2",
80
+ "Nombre": "Superficie Total de Tierra (km²)"
81
+ }
82
+ ],
83
+ "Infraestructura": [
84
+ {
85
+ "Indicador": "EG.ELC.ACCS.ZS",
86
+ "Nombre": "Acceso a Electricidad (% de población)"
87
+ },
88
+ {
89
+ "Indicador": "IT.MFD.TOTL.ZS",
90
+ "Nombre": "Acceso a Tecnología Móvil (%)"
91
+ },
92
+ {
93
+ "Indicador": "IT.NET.USER.ZS",
94
+ "Nombre": "Acceso a Internet (%)"
95
+ },
96
+ {
97
+ "Indicador": "SL.TLF.TOTL.IN",
98
+ "Nombre": "Fuerza Laboral Total"
99
+ },
100
+ {
101
+ "Indicador": "EG.USE.PCAP.KG.OE",
102
+ "Nombre": "Uso de Energía per Cápita (kg petróleo equiv.)"
103
+ }
104
+ ],
105
+ "Digital": [
106
+ {
107
+ "Indicador": "IT.NET.USER.ZS",
108
+ "Nombre": "Usuarios de Internet (%)"
109
+ },
110
+ {
111
+ "Indicador": "IT.CEL.SETS.P2",
112
+ "Nombre": "Suscripciones Móviles (por 100 personas)"
113
+ },
114
+ {
115
+ "Indicador": "SP.DYN.TFRT.IN", # Corregido el código del indicador
116
+ "Nombre": "Tasa de Fertilidad (nacimientos por mujer)"
117
+ },
118
+ {
119
+ "Indicador": "IT.MFD.TOTL.ZS",
120
+ "Nombre": "Acceso a Tecnología Móvil (%)"
121
+ },
122
+ {
123
+ "Indicador": "IT.NET.BBND.P2", # Corregido el código del indicador
124
+ "Nombre": "Acceso a Internet de Banda Ancha (%)"
125
+ }
126
+ ]
127
+ }
128
+
129
+ # Lista de categorías no deseadas (agregaciones regionales, etc.)
130
+ CATEGORIAS_NO_DESEADAS = [
131
+ ]
132
+
133
+ @st.cache_data(ttl=3600) # Cache con tiempo de vida de 1 hora
134
+ def obtener_datos(indicador):
135
+ """Obtiene datos del Banco Mundial para todos los países."""
136
+ url = f"http://api.worldbank.org/v2/country/all/indicator/{indicador}?format=json&per_page=5000"
137
+ try:
138
+ response = requests.get(url, timeout=10)
139
+ response.raise_for_status()
140
+ data = response.json()[1]
141
+
142
+ if not data:
143
+ st.error(f"No se encontraron datos para el indicador {indicador}.")
144
+ return None
145
+
146
+ df = pd.json_normalize(data)
147
+ df['country.value'] = df['country.value'].str.strip().str.title()
148
+ df = df[~df['country.value'].isin([cat.strip().title() for cat in CATEGORIAS_NO_DESEADAS])]
149
+
150
+ if df.empty:
151
+ st.warning("No hay datos disponibles después de filtrar categorías no deseadas.")
152
+ return None
153
+
154
+ return df
155
+
156
+ except requests.Timeout:
157
+ st.error("Tiempo de espera agotado al conectar con el Banco Mundial.")
158
+ return None
159
+ except requests.RequestException as e:
160
+ st.error(f"Error al conectar con el Banco Mundial: {str(e)}")
161
+ return None
162
+ except (IndexError, KeyError, TypeError) as e:
163
+ st.error(f"Error al procesar los datos: {str(e)}")
164
+ return None
165
+
166
+ @st.cache_data(ttl=3600)
167
+ def obtener_datos_mundo(indicador):
168
+ """Obtiene datos del Banco Mundial solo para el mundo."""
169
+ url = f"http://api.worldbank.org/v2/country/WLD/indicator/{indicador}?format=json&per_page=5000"
170
+ try:
171
+ response = requests.get(url, timeout=10)
172
+ response.raise_for_status()
173
+ data = response.json()[1]
174
+
175
+ if not data:
176
+ st.error(f"No se encontraron datos mundiales para el indicador {indicador}.")
177
+ return None
178
+
179
+ df = pd.json_normalize(data)
180
+ return df
181
+
182
+ except Exception as e:
183
+ st.error(f"Error al obtener datos mundiales: {str(e)}")
184
+ return None
185
+
186
+ def prepare_prophet_data(df):
187
+ """Prepara los datos para Prophet."""
188
+ try:
189
+ df = df.rename(columns={'date': 'ds', 'value': 'y'})
190
+ df['ds'] = pd.to_datetime(df['ds'], format='%Y')
191
+ df = df[['ds', 'y']].sort_values('ds')
192
+ df = df.dropna()
193
+ return df
194
+ except Exception as e:
195
+ st.error(f"Error al preparar datos para Prophet: {str(e)}")
196
+ return None
197
+
198
+ def make_forecast(df, periods=60):
199
+ """Realiza la predicción con Prophet."""
200
+ try:
201
+ model = Prophet(
202
+ yearly_seasonality=True,
203
+ weekly_seasonality=False,
204
+ daily_seasonality=False,
205
+ seasonality_mode='multiplicative',
206
+ interval_width=0.95
207
+ )
208
+ model.fit(df)
209
+ future = model.make_future_dataframe(periods=periods, freq='Y')
210
+ forecast = model.predict(future)
211
+ return forecast
212
+ except Exception as e:
213
+ st.error(f"Error al realizar la predicción: {str(e)}")
214
+ return None
215
+
216
+ def plot_forecast_comparison(historical_data, forecast_data, title):
217
+ """Crea un gráfico comparativo de datos históricos y predicción."""
218
+ try:
219
+ fig = make_subplots(
220
+ rows=1, cols=1,
221
+ #subplot_titles=('Datos Históricos y Predicción'),
222
+ vertical_spacing=0.15
223
+ )
224
+
225
+ # Datos históricos y predicción
226
+ fig.add_trace(
227
+ go.Scatter(
228
+ x=historical_data['ds'],
229
+ y=historical_data['y'],
230
+ name='Datos Históricos',
231
+ line=dict(color='blue')
232
+ ),
233
+ row=1, col=1
234
+ )
235
+
236
+ fig.add_trace(
237
+ go.Scatter(
238
+ x=forecast_data['ds'],
239
+ y=forecast_data['yhat'],
240
+ name='Predicción',
241
+ line=dict(color='red')
242
+ ),
243
+ row=1, col=1
244
+ )
245
+
246
+ # Intervalos de confianza
247
+ fig.add_trace(
248
+ go.Scatter(
249
+ x=forecast_data['ds'],
250
+ y=forecast_data['yhat_upper'],
251
+ fill=None,
252
+ mode='lines',
253
+ line=dict(color='rgba(255,0,0,0.2)'),
254
+ name='Límite Superior'
255
+ ),
256
+ row=1, col=1
257
+ )
258
+
259
+ fig.add_trace(
260
+ go.Scatter(
261
+ x=forecast_data['ds'],
262
+ y=forecast_data['yhat_lower'],
263
+ fill='tonexty',
264
+ mode='lines',
265
+ line=dict(color='rgba(255,0,0,0.2)'),
266
+ name='Límite Inferior'
267
+ ),
268
+ row=1, col=1
269
+ )
270
+
271
+
272
+ fig.update_layout(
273
+ height=600,
274
+ title_text=title,
275
+ showlegend=True
276
+ )
277
+
278
+ return fig
279
+ except Exception as e:
280
+ st.error(f"Error al crear el gráfico: {str(e)}")
281
+ return None
282
+
283
+ # Interfaz de usuario
284
+ st.title("📊 Análisis de Datos del Banco Mundial por Áreas de Enfoque")
285
+
286
+ # Selección de área de enfoque y indicador
287
+ area_seleccionada = st.selectbox("Selecciona un área de enfoque", list(INDICADORES.keys()))
288
+ indicador_seleccionado = st.selectbox(
289
+ "Selecciona un indicador",
290
+ [i["Nombre"] for i in INDICADORES[area_seleccionada]]
291
+ )
292
+
293
+ # Obtener el indicador correspondiente
294
+ indicador_info = next(
295
+ i for i in INDICADORES[area_seleccionada]
296
+ if i["Nombre"] == indicador_seleccionado
297
+ )
298
+
299
+ # Obtener datos mundiales y realizar predicción
300
+ df_mundo = obtener_datos_mundo(indicador_info["Indicador"])
301
+ if df_mundo is not None:
302
+ # Preparar datos para visualización histórica
303
+ df_mundo_hist = df_mundo[['date', 'value']].copy()
304
+ df_mundo_hist['date'] = pd.to_datetime(df_mundo_hist['date'], format='%Y')
305
+ df_mundo_hist = df_mundo_hist.sort_values(by='date', ascending=True)
306
+
307
+ # Preparar datos para Prophet y realizar predicción
308
+ df_prophet = prepare_prophet_data(df_mundo[['date', 'value']])
309
+
310
+ if df_prophet is not None:
311
+ forecast = make_forecast(df_prophet)
312
+
313
+ if forecast is not None:
314
+ # Mostrar gráficos en pestañas
315
+ tab1, tab2 = st.tabs(["📈 Datos Históricos", "🔮 Predicción"])
316
+
317
+ with tab1:
318
+ st.subheader("📅 Evolución del Indicador a lo Largo de los Años")
319
+ fig_hist = px.line(
320
+ df_mundo_hist,
321
+ x='date',
322
+ y='value',
323
+ title=f"Evolución de {indicador_info['Nombre']} (Mundial)",
324
+ labels={'date': 'Año', 'value': indicador_info['Nombre']}
325
+ )
326
+ st.plotly_chart(fig_hist, use_container_width=True)
327
+
328
+ with tab2:
329
+ st.subheader("🔮 Predicción para los Próximos 60 Años")
330
+ fig_forecast = plot_forecast_comparison(
331
+ df_prophet,
332
+ forecast,
333
+ f"Predicción de {indicador_info['Nombre']} - Mundial"
334
+ )
335
+ if fig_forecast is not None:
336
+ st.plotly_chart(fig_forecast, use_container_width=True)
337
+
338
+ # Métricas de predicción
339
+ st.subheader("📊 Métricas Clave de la Predicción")
340
+ col1, col2, col3 = st.columns(3)
341
+
342
+ with col1:
343
+ ultimo_valor = df_prophet['y'].iloc[-1]
344
+ st.metric("Último Valor Histórico", f"{ultimo_valor:.2f}")
345
+
346
+ with col2:
347
+ valor_predicho = forecast['yhat'].iloc[-1]
348
+ st.metric("Valor Predicho (60 años)", f"{valor_predicho:.2f}")
349
+
350
+ with col3:
351
+ cambio_porcentual = ((valor_predicho - ultimo_valor) / ultimo_valor) * 100
352
+ st.metric("Cambio Porcentual Esperado", f"{cambio_porcentual:.1f}%")
353
+
354
+ # Información sobre la predicción
355
+ st.info("""
356
+ 📈 **Información sobre la Predicción**
357
+ - La predicción se realiza utilizando Facebook Prophet
358
+ - Se consideran tendencias anuales y patrones históricos
359
+ - El área sombreada representa el intervalo de confianza de la predicción
360
+ - Las tendencias se calculan utilizando medias móviles para datos históricos
361
+ """)
362
+
363
+ # Obtener y mostrar datos de países
364
+ df_paises = obtener_datos(indicador_info["Indicador"])
365
+ if df_paises is not None and not df_paises.empty:
366
+ # Filtrar los datos más recientes
367
+ df_paises = df_paises[df_paises['value'].notna()]
368
+ ultimo_anio = df_paises['date'].max()
369
+ df_paises = df_paises[df_paises['date'] == ultimo_anio]
370
+
371
+ # Ordenar de mayor a menor
372
+ df_paises = df_paises.sort_values(by='value', ascending=False)
373
+
374
+ # Mostrar gráfico de barras y tablas comparativas
375
+ st.subheader(f"🌍 {indicador_info['Nombre']} - Comparativa por Países ({ultimo_anio})")
376
+
377
+ col1, col2 = st.columns(2)
378
+
379
+ with col1:
380
+ st.write("📈 **Top 20 Valores Más Altos**")
381
+ top_20 = df_paises[['country.value', 'value']].head(20).rename(
382
+ columns={'country.value': 'País', 'value': 'Valor'}
383
+ )
384
+ st.dataframe(
385
+ top_20.style.format({'Valor': '{:.2f}'}),
386
+ hide_index=True,
387
+ use_container_width=True
388
+ )
389
+
390
+ with col2:
391
+ st.write("📉 **Top 20 Valores Más Bajos**")
392
+ bottom_20 = df_paises[['country.value', 'value']].tail(20).rename(
393
+ columns={'country.value': 'País', 'value': 'Valor'}
394
+ )
395
+ st.dataframe(
396
+ bottom_20.style.format({'Valor': '{:.2f}'}),
397
+ hide_index=True,
398
+ use_container_width=True
399
+ )
400
+
401
+ # Visualización interactiva de los top 20 países
402
+ fig_paises = px.bar(
403
+ df_paises.head(20),
404
+ x='country.value',
405
+ y='value',
406
+ title=f"{indicador_info['Nombre']} por País (Top 20)",
407
+ labels={
408
+ 'country.value': 'País',
409
+ 'value': indicador_info['Nombre']
410
+ },
411
+ text='value'
412
+ )
413
+
414
+ fig_paises.update_traces(
415
+ texttemplate='%{text:.2f}',
416
+ textposition='outside'
417
+ )
418
+
419
+ fig_paises.update_layout(
420
+ xaxis_tickangle=-45,
421
+ height=600,
422
+ showlegend=False
423
+ )
424
+
425
+ st.plotly_chart(fig_paises, use_container_width=True)
426
+
427
+ else:
428
+ st.warning(f"No hay datos disponibles de países para el indicador {indicador_info['Nombre']} en esta área de enfoque.")
429
+
430
+
431
+ # Agregar una sección de chatbot basado en el contexto del indicador seleccionado
432
+ st.markdown("---")
433
+ st.subheader("💬 Consulta a nuestro asistente virtual sobre este indicador")
434
+
435
+ # Configuración de la API de Hugging Face
436
+ API_URL = "https://api-inference.huggingface.co/models/deepseek-ai/DeepSeek-R1-Distill-Qwen-32B"
437
+
438
+ # Inicializar la sesión state para el historial de chat si no existe
439
+ if 'chat_history' not in st.session_state:
440
+ st.session_state.chat_history = []
441
+
442
+ # Función para obtener la API key de Hugging Face desde secrets
443
+ @st.cache_resource
444
+ def get_huggingface_api_key():
445
+ """Obtener la API key de Hugging Face desde secrets"""
446
+ try:
447
+ # Intenta acceder al token usando la clave HF_TOKEN
448
+ return st.secrets["HF_TOKEN"]
449
+ except KeyError:
450
+ # Si no está disponible con esa clave, intenta el formato anterior
451
+ try:
452
+ return st.secrets["huggingface"]["api_key"]
453
+ except:
454
+ return None
455
+
456
+ # Obtener la API key
457
+ api_key = get_huggingface_api_key()
458
+
459
+ if not api_key:
460
+ api_key = st.text_input("Ingresa tu API key de Hugging Face:", type="password")
461
+ if not api_key:
462
+ st.warning("Por favor ingresa una API key de Hugging Face para usar el chatbot.")
463
+ st.stop()
464
+
465
+ # Función para enviar solicitudes a la API de Hugging Face
466
+ def query_huggingface(payload):
467
+ """Envía una solicitud a la API de Hugging Face y retorna la respuesta"""
468
+ headers = {"Authorization": f"Bearer {api_key}"}
469
+ try:
470
+ response = requests.post(API_URL, headers=headers, json=payload, timeout=60)
471
+ response.raise_for_status()
472
+ return response.json()
473
+ except requests.exceptions.Timeout:
474
+ st.error("La solicitud a la API de Hugging Face ha excedido el tiempo de espera.")
475
+ return None
476
+ except requests.exceptions.HTTPError as e:
477
+ st.error(f"Error HTTP: {e.response.status_code} - {e.response.text}")
478
+ return None
479
+ except Exception as e:
480
+ st.error(f"Error al comunicarse con la API de Hugging Face: {str(e)}")
481
+ return None
482
+
483
+ # Preparar el contexto basado en los datos seleccionados
484
+ def prepare_context():
485
+ """Prepara el contexto para el chatbot basado en el indicador seleccionado"""
486
+ context = f"""
487
+ Información sobre el indicador '{indicador_seleccionado}' ({indicador_info['Indicador']}):
488
+ - Área de enfoque: {area_seleccionada}
489
+ """
490
+
491
+ # Verificar si las variables existen en el contexto actual antes de usarlas
492
+ if 'ultimo_anio' in locals() or 'ultimo_anio' in globals():
493
+ context += f"- Último año con datos: {ultimo_anio}\n"
494
+
495
+ # Agregar información sobre valores mundiales si está disponible
496
+ if ('df_mundo_hist' in locals() or 'df_mundo_hist' in globals()) and 'df_mundo_hist' is not None and not df_mundo_hist.empty:
497
+ ultimo_valor_mundial = df_mundo_hist.iloc[-1]['value'] if not df_mundo_hist.empty else "No disponible"
498
+ context += f"- Último valor mundial registrado: {ultimo_valor_mundial}\n"
499
+
500
+ # Agregar información sobre predicción si está disponible
501
+ if ('forecast' in locals() or 'forecast' in globals()) and forecast is not None:
502
+ valor_predicho = forecast['yhat'].iloc[-1]
503
+ context += f"- Valor predicho para dentro de 60 años: {valor_predicho:.2f}\n"
504
+
505
+ if 'cambio_porcentual' in locals() or 'cambio_porcentual' in globals():
506
+ context += f"- Cambio porcentual esperado: {cambio_porcentual:.1f}%\n"
507
+
508
+ # Agregar información sobre países top si está disponible
509
+ if ('top_20' in locals() or 'top_20' in globals()) and top_20 is not None and not top_20.empty:
510
+ top_3_paises = top_20.head(3)
511
+ context += "- Top 3 países con valores más altos:\n"
512
+ for _, row in top_3_paises.iterrows():
513
+ context += f" * {row['País']}: {row['Valor']:.2f}\n"
514
+
515
+ return context
516
+
517
+ # Interfaz del chatbot
518
+ st.info("Puedes preguntar cualquier cosa sobre este indicador, su evolución histórica, predicciones futuras o comparar países.")
519
+
520
+ # Crear el widget de entrada de usuario
521
+ user_input = st.text_input("Tu pregunta:", key="user_query", placeholder="Ej: ¿Cuál es la tendencia esperada para este indicador?")
522
+
523
+ # Crear un contenedor para el historial de chat
524
+ chat_container = st.container()
525
+
526
+ # Procesar la entrada del usuario
527
+ if user_input:
528
+ # Preparar el contexto
529
+ context = prepare_context()
530
+
531
+ # Construir el prompt para el modelo
532
+ prompt = f"""
533
+ Eres un asistente especializado en datos del Banco Mundial y análisis económico.
534
+
535
+ CONTEXTO:
536
+ {context}
537
+
538
+ PREGUNTA DEL USUARIO:
539
+ {user_input}
540
+
541
+ Responde de manera concisa y útil, basándote en el contexto proporcionado.
542
+ """
543
+
544
+ # Mostrar un mensaje de espera personalizado con icono
545
+ with st.spinner("🧠 Pensando..."):
546
+ # Llamar a la API de Hugging Face
547
+ payload = {
548
+ "inputs": prompt,
549
+ "parameters": {
550
+ "max_new_tokens": 250,
551
+ "temperature": 0.7,
552
+ "top_p": 0.9,
553
+ "do_sample": True
554
+ }
555
+ }
556
+
557
+ response = query_huggingface(payload)
558
+
559
+ if response:
560
+ # Extraer la respuesta del modelo
561
+ if isinstance(response, list) and len(response) > 0:
562
+ bot_response = response[0].get("generated_text", "")
563
+ # Intentar extraer solo la respuesta del asistente (después del prompt)
564
+ try:
565
+ bot_response = bot_response.split("Responde de manera concisa y útil")[-1]
566
+ if "PREGUNTA DEL USUARIO:" in bot_response:
567
+ bot_response = bot_response.split("PREGUNTA DEL USUARIO:")[-1]
568
+ bot_response = bot_response.strip()
569
+ except:
570
+ # Si falla la extracción, usar la respuesta completa
571
+ pass
572
+ else:
573
+ bot_response = str(response)
574
+
575
+ # Agregar al historial de chat
576
+ st.session_state.chat_history.append({"role": "user", "content": user_input})
577
+ st.session_state.chat_history.append({"role": "assistant", "content": bot_response})
578
+
579
+ # Mostrar el historial de chat
580
+ with chat_container:
581
+ for message in st.session_state.chat_history:
582
+ if message["role"] == "user":
583
+ st.markdown(f"**😀 Tú:** {message['content']}")
584
+ else:
585
+ st.markdown(f"**🤖 Asistente:** {message['content']}")
586
+
587
+
588
+ # Información sobre el modelo
589
+ st.markdown("---")
590
+ st.caption("Asistente virtual potenciado por deepseek-ai/DeepSeek-R1-Distill-Qwen-32B a través de Hugging Face")
requirements.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ streamlit
2
+ pandas
3
+ plotly
4
+ prophet