angelsg213 commited on
Commit
acaeb6e
·
verified ·
1 Parent(s): 95b72cc

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +410 -42
app.py CHANGED
@@ -13,6 +13,9 @@ from reportlab.platypus import SimpleDocTemplate, Table, TableStyle, Paragraph,
13
  from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
14
  from reportlab.lib.enums import TA_CENTER, TA_RIGHT, TA_LEFT
15
  import time
 
 
 
16
 
17
  # ============= EXTRAER TEXTO DEL PDF =============
18
  def extraer_texto_pdf(pdf_file):
@@ -88,18 +91,18 @@ Responde ahora:"""
88
 
89
  return "❌ No se pudo obtener respuesta del asistente IA", None
90
 
91
- # ============= GENERAR AUDIO DE LA RESPUESTA =============
92
  def generar_audio_respuesta(texto, client):
93
- """Convierte la respuesta de texto a audio usando TTS de Hugging Face"""
94
 
95
  modelos_tts = [
96
- "espnet/kan-bayashi_ljspeech_vits",
97
- "facebook/mms-tts-spa",
98
- "microsoft/speecht5_tts"
99
  ]
100
 
101
- # Limitar texto para TTS (máximo 500 caracteres)
102
- texto_corto = texto[:500] if len(texto) > 500 else texto
103
 
104
  for modelo in modelos_tts:
105
  try:
@@ -126,6 +129,203 @@ def generar_audio_respuesta(texto, client):
126
 
127
  return None
128
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
129
  # ============= ANALIZAR CON LLM Y CONVERTIR A JSON =============
130
  def analizar_y_convertir_json(texto):
131
  """El LLM lee la factura y devuelve JSON estructurado"""
@@ -311,7 +511,7 @@ def json_a_csv(datos_json):
311
 
312
  return pd.DataFrame(filas)
313
 
314
- # ============= GENERAR PDF TEMPLATES (MANTENIDOS DEL ORIGINAL) =============
315
  def generar_pdf_clasico(csv_file, datos_json):
316
  timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
317
  pdf_filename = f"factura_clasica_{timestamp}.pdf"
@@ -455,24 +655,21 @@ with gr.Blocks(title="Extractor de Facturas con IA Avanzada") as demo:
455
  pdf_input = gr.File(label="Seleccionar factura PDF", file_types=[".pdf"], type="filepath")
456
  btn_extraer = gr.Button("🚀 Extraer Datos de la Factura", variant="primary", size="lg")
457
 
458
- # Indicador de carga para extracción
459
  loading_extraccion = gr.HTML(visible=False, value="""
460
  <div style="text-align: center; padding: 20px;">
461
  <div class="spinner"></div>
462
  <p style="margin-top: 10px; color: #2196F3; font-weight: bold;">
463
  🔄 Procesando tu factura...
464
  </p>
465
- <audio autoplay loop>
466
- <source src="https://assets.mixkit.co/active_storage/sfx/2869/2869-preview.mp3" type="audio/mpeg">
467
- </audio>
468
  </div>
469
  <style>
470
  .spinner {
471
- border: 4px solid #f3f3f3;
472
- border-top: 4px solid #2196F3;
473
  border-radius: 50%;
474
- width: 40px;
475
- height: 40px;
476
  animation: spin 1s linear infinite;
477
  margin: 0 auto;
478
  }
@@ -512,7 +709,7 @@ with gr.Blocks(title="Extractor de Facturas con IA Avanzada") as demo:
512
  - ✅ Responder preguntas específicas sobre tu factura
513
  - ✅ Explicar conceptos contables (IVA, base imponible, etc.)
514
  - ✅ Dar consejos sobre gestión y pagos
515
- - ✅ **Leer la respuesta en voz alta** 🔊
516
  """)
517
 
518
  with gr.Row():
@@ -526,24 +723,21 @@ with gr.Blocks(title="Extractor de Facturas con IA Avanzada") as demo:
526
 
527
  btn_consulta_ia = gr.Button("🎤 Consultar y Escuchar Respuesta", variant="primary", size="lg")
528
 
529
- # Indicador de carga para IA
530
  loading_ia = gr.HTML(visible=False, value="""
531
  <div style="text-align: center; padding: 20px;">
532
  <div class="spinner-ia"></div>
533
  <p style="margin-top: 10px; color: #9C27B0; font-weight: bold;">
534
  🧠 El asistente IA está pensando...
535
  </p>
536
- <audio autoplay loop>
537
- <source src="https://assets.mixkit.co/active_storage/sfx/2571/2571-preview.mp3" type="audio/mpeg">
538
- </audio>
539
  </div>
540
  <style>
541
  .spinner-ia {
542
- border: 4px solid #f3f3f3;
543
- border-top: 4px solid #9C27B0;
544
  border-radius: 50%;
545
- width: 50px;
546
- height: 50px;
547
  animation: spin 0.8s linear infinite;
548
  margin: 0 auto;
549
  }
@@ -578,35 +772,104 @@ with gr.Blocks(title="Extractor de Facturas con IA Avanzada") as demo:
578
 
579
  gr.Markdown("---")
580
  gr.Markdown("### 🔊 Escucha la Respuesta")
 
 
 
 
581
  audio_respuesta = gr.Audio(
582
- label="Audio de la respuesta",
583
  type="filepath",
584
- visible=True
 
585
  )
586
 
587
  gr.Markdown("""
588
- 💡 **Tip:** El asistente genera un archivo de audio que puedes:
589
- - ▶️ Reproducir directamente aquí
590
- - 📥 Descargar para escucharlo después
591
- - 🔄 Hacer nuevas preguntas cuando quieras
 
592
  """)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
593
 
594
  gr.Markdown("---")
595
  gr.Markdown("""
596
  ### 📚 Guía rápida de uso
597
 
598
  1. **📄 Extracción Automática:** Sube tu PDF y extrae todos los datos automáticamente
599
- 2. **🤖 Asistente IA con Voz:** Haz preguntas y escucha las respuestas en audio
 
 
600
 
601
  ---
602
 
603
- ### 🎯 Características del Asistente IA:
604
 
605
- - **🧠 Inteligente:** Entiende tu pregunta en lenguaje natural
606
- - **📖 Educativo:** Explica conceptos contables de forma sencilla
607
- - **💡 Útil:** Da consejos prácticos sobre gestión de facturas
608
- - **🔊 Accesible:** Convierte la respuesta a audio automáticamente
609
- - **⚡ Rápido:** Responde en segundos
 
 
 
610
 
611
  💡 **Empieza por la pestaña "Extracción Automática" para procesar tu factura.**
612
  """)
@@ -645,21 +908,126 @@ with gr.Blocks(title="Extractor de Facturas con IA Avanzada") as demo:
645
  # Asistente IA con voz y loading
646
  def consultar_ia_con_loading(texto, pregunta):
647
  if not texto:
648
- return "❌ Por favor, procesa una factura primero en la pestaña 'Extracción Automática'", None, gr.update(visible=False)
649
 
650
  # Mostrar loading
651
- yield "🔄 Consultando al asistente IA...", None, gr.update(visible=True)
652
 
653
  # Procesar consulta
 
654
  respuesta, audio = asistente_ia_factura(texto, pregunta)
655
 
 
 
 
656
  # Ocultar loading y mostrar resultados
657
- yield respuesta, audio, gr.update(visible=False)
658
 
659
  btn_consulta_ia.click(
660
  fn=consultar_ia_con_loading,
661
  inputs=[texto_extraido, pregunta_ia],
662
- outputs=[resultado_ia, audio_respuesta, loading_ia]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
663
  )
664
 
665
  if __name__ == "__main__":
 
13
  from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
14
  from reportlab.lib.enums import TA_CENTER, TA_RIGHT, TA_LEFT
15
  import time
16
+ from PIL import Image
17
+ import io
18
+ import base64
19
 
20
  # ============= EXTRAER TEXTO DEL PDF =============
21
  def extraer_texto_pdf(pdf_file):
 
91
 
92
  return "❌ No se pudo obtener respuesta del asistente IA", None
93
 
94
+ # ============= GENERAR AUDIO DE LA RESPUESTA CON EMOCIONES =============
95
  def generar_audio_respuesta(texto, client):
96
+ """Convierte la respuesta de texto a audio usando TTS avanzado con emociones"""
97
 
98
  modelos_tts = [
99
+ "facebook/mms-tts-spa", # Mejor calidad para español
100
+ "microsoft/speecht5_tts",
101
+ "suno/bark-small" # Modelo con emociones y naturalidad
102
  ]
103
 
104
+ # Limitar texto para TTS (máximo 400 caracteres para mejor calidad)
105
+ texto_corto = texto[:400] if len(texto) > 400 else texto
106
 
107
  for modelo in modelos_tts:
108
  try:
 
129
 
130
  return None
131
 
132
+ # ============= ANÁLISIS DE SENTIMIENTO DE FACTURA =============
133
+ def analizar_sentimiento_factura(texto, client):
134
+ """Analiza si la factura tiene alertas, urgencias o problemas"""
135
+
136
+ prompt = f"""Analiza esta factura y determina si hay algo preocupante o urgente.
137
+ TEXTO: {texto[:3000]}
138
+
139
+ Responde en formato JSON:
140
+ {{
141
+ "sentimiento": "positivo/neutral/alerta",
142
+ "urgencia": "alta/media/baja",
143
+ "razon": "explicación breve",
144
+ "recomendacion": "qué hacer"
145
+ }}"""
146
+
147
+ try:
148
+ response = client.chat.completions.create(
149
+ model="Qwen/Qwen2.5-72B-Instruct",
150
+ messages=[{"role": "user", "content": prompt}],
151
+ max_tokens=300,
152
+ temperature=0.3
153
+ )
154
+
155
+ resultado = response.choices[0].message.content
156
+ resultado = re.sub(r'```json\s*', '', resultado)
157
+ resultado = re.sub(r'```\s*', '', resultado).strip()
158
+
159
+ match = re.search(r'\{.*\}', resultado, re.DOTALL)
160
+ if match:
161
+ return json.loads(match.group(0))
162
+ except:
163
+ pass
164
+
165
+ return {"sentimiento": "neutral", "urgencia": "baja", "razon": "Análisis no disponible", "recomendacion": "Revisar manualmente"}
166
+
167
+ # ============= COMPARADOR DE FACTURAS =============
168
+ def comparar_facturas(json1, json2):
169
+ """Compara dos facturas y muestra diferencias clave"""
170
+
171
+ if not json1 or not json2:
172
+ return "❌ Necesitas procesar dos facturas para compararlas"
173
+
174
+ try:
175
+ total1 = json1.get('totales', {}).get('total', json1.get('total', 0))
176
+ total2 = json2.get('totales', {}).get('total', json2.get('total', 0))
177
+
178
+ diferencia = total2 - total1
179
+ porcentaje = (diferencia / total1 * 100) if total1 > 0 else 0
180
+
181
+ comparacion = f"""## 📊 Comparación de Facturas
182
+
183
+ ### Factura 1
184
+ - **Número:** {json1.get('numero_factura', 'N/A')}
185
+ - **Fecha:** {json1.get('fecha', 'N/A')}
186
+ - **Total:** {total1}€
187
+
188
+ ### Factura 2
189
+ - **Número:** {json2.get('numero_factura', 'N/A')}
190
+ - **Fecha:** {json2.get('fecha', 'N/A')}
191
+ - **Total:** {total2}€
192
+
193
+ ---
194
+
195
+ ### 💰 Diferencia
196
+ - **Variación:** {diferencia:+.2f}€ ({porcentaje:+.1f}%)
197
+ - **Análisis:** {"⬆️ Incremento" if diferencia > 0 else "⬇️ Reducción" if diferencia < 0 else "✅ Sin cambios"}
198
+
199
+ """
200
+
201
+ return comparacion
202
+
203
+ except Exception as e:
204
+ return f"❌ Error al comparar: {str(e)}"
205
+
206
+ # ============= SUGERENCIAS INTELIGENTES =============
207
+ def generar_sugerencias_ia(datos_json, client):
208
+ """Genera sugerencias personalizadas basadas en la factura"""
209
+
210
+ prompt = f"""Basándote en esta factura, da 3 sugerencias útiles y prácticas:
211
+
212
+ DATOS: {json.dumps(datos_json, indent=2)}
213
+
214
+ Responde en español con:
215
+ 1. Sugerencia sobre organización
216
+ 2. Sugerencia sobre pagos o plazos
217
+ 3. Sugerencia sobre optimización o ahorro
218
+
219
+ Sé breve (máximo 150 palabras total):"""
220
+
221
+ try:
222
+ response = client.chat.completions.create(
223
+ model="Qwen/Qwen2.5-72B-Instruct",
224
+ messages=[{"role": "user", "content": prompt}],
225
+ max_tokens=400,
226
+ temperature=0.7
227
+ )
228
+
229
+ return response.choices[0].message.content
230
+ except:
231
+ return "💡 Sugerencias: Mantén tus facturas organizadas por fecha, verifica los plazos de pago, y considera digitalizar todos tus documentos."
232
+
233
+ # ============= EXTRACTOR DE CATEGORÍAS =============
234
+ def extraer_categorias_gasto(datos_json, client):
235
+ """Categoriza automáticamente el tipo de gasto"""
236
+
237
+ productos = datos_json.get('productos', [])
238
+ texto_productos = " ".join([p.get('descripcion', '') for p in productos[:5]])
239
+
240
+ prompt = f"""Clasifica esta factura en UNA categoría de gasto:
241
+
242
+ Productos/Servicios: {texto_productos}
243
+ Total: {datos_json.get('totales', {}).get('total', 0)}€
244
+
245
+ Categorías posibles:
246
+ - Oficina y suministros
247
+ - Tecnología e IT
248
+ - Servicios profesionales
249
+ - Marketing y publicidad
250
+ - Viajes y transporte
251
+ - Alimentación y hostelería
252
+ - Mantenimiento y reparaciones
253
+ - Otros gastos
254
+
255
+ Responde solo con el nombre de la categoría:"""
256
+
257
+ try:
258
+ response = client.chat.completions.create(
259
+ model="Qwen/Qwen2.5-72B-Instruct",
260
+ messages=[{"role": "user", "content": prompt}],
261
+ max_tokens=50,
262
+ temperature=0.3
263
+ )
264
+
265
+ categoria = response.choices[0].message.content.strip()
266
+ return f"📁 **Categoría:** {categoria}"
267
+ except:
268
+ return "📁 **Categoría:** No clasificada"
269
+
270
+ # ============= TRADUCTOR MULTIIDIOMA =============
271
+ def traducir_factura(texto, idioma_destino, client):
272
+ """Traduce el contenido de la factura a otro idioma"""
273
+
274
+ idiomas = {
275
+ "Inglés": "English",
276
+ "Francés": "Français",
277
+ "Alemán": "Deutsch",
278
+ "Italiano": "Italiano",
279
+ "Portugués": "Português"
280
+ }
281
+
282
+ idioma = idiomas.get(idioma_destino, "English")
283
+
284
+ prompt = f"""Traduce este resumen de factura al {idioma}. Mantén el formato y estructura:
285
+
286
+ {texto[:2000]}
287
+
288
+ Traducción:"""
289
+
290
+ try:
291
+ response = client.chat.completions.create(
292
+ model="Qwen/Qwen2.5-72B-Instruct",
293
+ messages=[{"role": "user", "content": prompt}],
294
+ max_tokens=1000,
295
+ temperature=0.3
296
+ )
297
+
298
+ return response.choices[0].message.content
299
+ except:
300
+ return "❌ Error en la traducción"
301
+
302
+ # ============= VISUALIZACIÓN DE IMAGEN A TEXTO =============
303
+ def analizar_imagen_factura(imagen_path, client):
304
+ """Analiza una imagen de factura usando Vision Language Model"""
305
+
306
+ try:
307
+ # Cargar y codificar la imagen
308
+ with Image.open(imagen_path) as img:
309
+ # Redimensionar si es muy grande
310
+ max_size = (1024, 1024)
311
+ img.thumbnail(max_size, Image.Resampling.LANCZOS)
312
+
313
+ # Convertir a bytes
314
+ buffered = io.BytesIO()
315
+ img.save(buffered, format="PNG")
316
+ img_bytes = buffered.getvalue()
317
+
318
+ # Usar modelo de visión
319
+ response = client.image_to_text(
320
+ image=img_bytes,
321
+ model="Salesforce/blip-image-captioning-large"
322
+ )
323
+
324
+ return f"🖼️ **Análisis visual:** {response}"
325
+
326
+ except Exception as e:
327
+ return f"❌ No se pudo analizar la imagen: {str(e)}"
328
+
329
  # ============= ANALIZAR CON LLM Y CONVERTIR A JSON =============
330
  def analizar_y_convertir_json(texto):
331
  """El LLM lee la factura y devuelve JSON estructurado"""
 
511
 
512
  return pd.DataFrame(filas)
513
 
514
+ # ============= GENERAR PDF TEMPLATES =============
515
  def generar_pdf_clasico(csv_file, datos_json):
516
  timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
517
  pdf_filename = f"factura_clasica_{timestamp}.pdf"
 
655
  pdf_input = gr.File(label="Seleccionar factura PDF", file_types=[".pdf"], type="filepath")
656
  btn_extraer = gr.Button("🚀 Extraer Datos de la Factura", variant="primary", size="lg")
657
 
658
+ # Indicador de carga para extracción (SIN audio)
659
  loading_extraccion = gr.HTML(visible=False, value="""
660
  <div style="text-align: center; padding: 20px;">
661
  <div class="spinner"></div>
662
  <p style="margin-top: 10px; color: #2196F3; font-weight: bold;">
663
  🔄 Procesando tu factura...
664
  </p>
 
 
 
665
  </div>
666
  <style>
667
  .spinner {
668
+ border: 3px solid #f3f3f3;
669
+ border-top: 3px solid #2196F3;
670
  border-radius: 50%;
671
+ width: 35px;
672
+ height: 35px;
673
  animation: spin 1s linear infinite;
674
  margin: 0 auto;
675
  }
 
709
  - ✅ Responder preguntas específicas sobre tu factura
710
  - ✅ Explicar conceptos contables (IVA, base imponible, etc.)
711
  - ✅ Dar consejos sobre gestión y pagos
712
+ - ✅ **Leer la respuesta en voz alta con emociones** 🔊
713
  """)
714
 
715
  with gr.Row():
 
723
 
724
  btn_consulta_ia = gr.Button("🎤 Consultar y Escuchar Respuesta", variant="primary", size="lg")
725
 
726
+ # Indicador de carga para IA (SIN audio)
727
  loading_ia = gr.HTML(visible=False, value="""
728
  <div style="text-align: center; padding: 20px;">
729
  <div class="spinner-ia"></div>
730
  <p style="margin-top: 10px; color: #9C27B0; font-weight: bold;">
731
  🧠 El asistente IA está pensando...
732
  </p>
 
 
 
733
  </div>
734
  <style>
735
  .spinner-ia {
736
+ border: 3px solid #f3f3f3;
737
+ border-top: 3px solid #9C27B0;
738
  border-radius: 50%;
739
+ width: 40px;
740
+ height: 40px;
741
  animation: spin 0.8s linear infinite;
742
  margin: 0 auto;
743
  }
 
772
 
773
  gr.Markdown("---")
774
  gr.Markdown("### 🔊 Escucha la Respuesta")
775
+
776
+ # Notificación de audio completado
777
+ audio_status = gr.Markdown(value="", visible=True)
778
+
779
  audio_respuesta = gr.Audio(
780
+ label="Audio de la respuesta (se genera automáticamente al completar el análisis)",
781
  type="filepath",
782
+ visible=True,
783
+ autoplay=False
784
  )
785
 
786
  gr.Markdown("""
787
+ 💡 **Tip:**
788
+ - ▶️ El audio se genera automáticamente cuando la IA termina de responder
789
+ - 🔊 Usa el reproductor para escuchar la respuesta con voz natural y emociones
790
+ - 📥 Descarga el audio si quieres guardarlo
791
+ - 🔄 Haz nuevas preguntas cuando quieras
792
  """)
793
+
794
+ # ============= TAB 3: ANÁLISIS AVANZADO IA =============
795
+ with gr.Tab("🔬 Análisis Avanzado IA"):
796
+ gr.Markdown("""
797
+ # 🎯 Herramientas IA Avanzadas para Facturas
798
+ ### Análisis inteligente, comparación, traducción y más
799
+ """)
800
+
801
+ with gr.Row():
802
+ with gr.Column():
803
+ gr.Markdown("### 📊 Análisis de Sentimiento")
804
+ btn_sentimiento = gr.Button("🔍 Analizar Riesgos y Urgencias", variant="primary")
805
+ resultado_sentimiento = gr.Markdown()
806
+
807
+ gr.Markdown("---")
808
+ gr.Markdown("### 💡 Sugerencias Personalizadas")
809
+ btn_sugerencias = gr.Button("✨ Generar Sugerencias IA", variant="primary")
810
+ resultado_sugerencias = gr.Markdown()
811
+
812
+ gr.Markdown("---")
813
+ gr.Markdown("### 📁 Categorización Automática")
814
+ btn_categoria = gr.Button("🏷️ Clasificar Tipo de Gasto", variant="primary")
815
+ resultado_categoria = gr.Markdown()
816
+
817
+ with gr.Column():
818
+ gr.Markdown("### 🌍 Traducción Multiidioma")
819
+ idioma_selector = gr.Dropdown(
820
+ choices=["Inglés", "Francés", "Alemán", "Italiano", "Portugués"],
821
+ value="Inglés",
822
+ label="Idioma de destino"
823
+ )
824
+ btn_traducir = gr.Button("🌐 Traducir Factura", variant="secondary")
825
+ resultado_traduccion = gr.Textbox(label="Traducción", lines=10)
826
+
827
+ gr.Markdown("---")
828
+ gr.Markdown("### 🖼️ Análisis de Imagen (opcional)")
829
+ imagen_input = gr.Image(label="Sube una imagen de la factura", type="filepath")
830
+ btn_analizar_imagen = gr.Button("👁️ Analizar Imagen", variant="secondary")
831
+ resultado_imagen = gr.Markdown()
832
+
833
+ # ============= TAB 4: COMPARADOR DE FACTURAS =============
834
+ with gr.Tab("⚖️ Comparador de Facturas"):
835
+ gr.Markdown("""
836
+ # 📊 Compara Dos Facturas
837
+ ### Analiza diferencias, variaciones de precio y tendencias
838
+ """)
839
+
840
+ gr.Markdown("**Nota:** Primero procesa una factura en la pestaña 'Extracción Automática', luego sube una segunda aquí.")
841
+
842
+ with gr.Row():
843
+ with gr.Column():
844
+ pdf_input_comparar = gr.File(label="📄 Segunda Factura PDF", file_types=[".pdf"], type="filepath")
845
+ btn_comparar = gr.Button("⚖️ Comparar Facturas", variant="primary", size="lg")
846
+
847
+ with gr.Column():
848
+ resultado_comparacion = gr.Markdown(value="*Sube una segunda factura para comparar*")
849
+
850
+ datos_json_comparar_state = gr.State()
851
 
852
  gr.Markdown("---")
853
  gr.Markdown("""
854
  ### 📚 Guía rápida de uso
855
 
856
  1. **📄 Extracción Automática:** Sube tu PDF y extrae todos los datos automáticamente
857
+ 2. **🤖 Asistente IA con Voz:** Haz preguntas y escucha las respuestas en audio natural con emociones
858
+ 3. **🔬 Análisis Avanzado:** Análisis de sentimiento, sugerencias IA, categorización y traducción
859
+ 4. **⚖️ Comparador:** Compara dos facturas para ver diferencias y tendencias
860
 
861
  ---
862
 
863
+ ### 🎯 Nuevas Características IA:
864
 
865
+ - **🔊 Audio con Emociones:** Voz natural y expresiva en español (Bark, MMS-TTS)
866
+ - **🔍 Análisis de Riesgos:** Detecta urgencias y problemas en facturas
867
+ - **💡 Sugerencias IA:** Consejos personalizados de optimización
868
+ - **📁 Categorización:** Clasifica automáticamente el tipo de gasto
869
+ - **🌐 Traducción:** Traduce facturas a 5 idiomas
870
+ - **👁️ Análisis Visual:** Extrae información de imágenes de facturas
871
+ - **⚖️ Comparador:** Compara dos facturas y analiza diferencias
872
+ - **🎨 Interfaz Silenciosa:** Sin sonidos molestos, control total del audio
873
 
874
  💡 **Empieza por la pestaña "Extracción Automática" para procesar tu factura.**
875
  """)
 
908
  # Asistente IA con voz y loading
909
  def consultar_ia_con_loading(texto, pregunta):
910
  if not texto:
911
+ return "❌ Por favor, procesa una factura primero en la pestaña 'Extracción Automática'", None, "⚠️ Factura no cargada", gr.update(visible=False)
912
 
913
  # Mostrar loading
914
+ yield "🔄 Consultando al asistente IA...", None, "", gr.update(visible=True)
915
 
916
  # Procesar consulta
917
+ time.sleep(0.3)
918
  respuesta, audio = asistente_ia_factura(texto, pregunta)
919
 
920
+ # Mensaje de estado del audio
921
+ audio_msg = "✅ Audio generado correctamente. ¡Escúchalo abajo!" if audio else "⚠️ No se pudo generar el audio"
922
+
923
  # Ocultar loading y mostrar resultados
924
+ yield respuesta, audio, audio_msg, gr.update(visible=False)
925
 
926
  btn_consulta_ia.click(
927
  fn=consultar_ia_con_loading,
928
  inputs=[texto_extraido, pregunta_ia],
929
+ outputs=[resultado_ia, audio_respuesta, audio_status, loading_ia]
930
+ )
931
+
932
+ # Funciones de análisis avanzado
933
+ def ejecutar_sentimiento(texto):
934
+ if not texto:
935
+ return "❌ Procesa una factura primero"
936
+
937
+ token = os.getenv("aa")
938
+ if not token:
939
+ return "❌ Error de configuración"
940
+
941
+ client = InferenceClient(token=token)
942
+ resultado = analizar_sentimiento_factura(texto, client)
943
+
944
+ emoji_sentimiento = {"positivo": "✅", "neutral": "⚪", "alerta": "⚠️"}
945
+ emoji_urgencia = {"alta": "🔴", "media": "🟡", "baja": "🟢"}
946
+
947
+ return f"""### {emoji_sentimiento.get(resultado['sentimiento'], '⚪')} Análisis de Sentimiento
948
+
949
+ **Estado:** {resultado['sentimiento'].upper()}
950
+ **Urgencia:** {emoji_urgencia.get(resultado['urgencia'], '⚪')} {resultado['urgencia'].upper()}
951
+
952
+ **Razón:** {resultado['razon']}
953
+
954
+ **Recomendación:** {resultado['recomendacion']}
955
+ """
956
+
957
+ def ejecutar_sugerencias(datos_json):
958
+ if not datos_json:
959
+ return "❌ Procesa una factura primero"
960
+
961
+ token = os.getenv("aa")
962
+ if not token:
963
+ return "❌ Error de configuración"
964
+
965
+ client = InferenceClient(token=token)
966
+ return f"### 💡 Sugerencias Personalizadas\n\n{generar_sugerencias_ia(datos_json, client)}"
967
+
968
+ def ejecutar_categoria(datos_json):
969
+ if not datos_json:
970
+ return "❌ Procesa una factura primero"
971
+
972
+ token = os.getenv("aa")
973
+ if not token:
974
+ return "❌ Error de configuración"
975
+
976
+ client = InferenceClient(token=token)
977
+ return f"### 🏷️ Categorización Automática\n\n{extraer_categorias_gasto(datos_json, client)}"
978
+
979
+ def ejecutar_traduccion(texto, idioma):
980
+ if not texto:
981
+ return "❌ Procesa una factura primero"
982
+
983
+ token = os.getenv("aa")
984
+ if not token:
985
+ return "❌ Error de configuración"
986
+
987
+ client = InferenceClient(token=token)
988
+ return traducir_factura(texto, idioma, client)
989
+
990
+ def ejecutar_analisis_imagen(imagen_path):
991
+ if not imagen_path:
992
+ return "❌ Sube una imagen primero"
993
+
994
+ token = os.getenv("aa")
995
+ if not token:
996
+ return "❌ Error de configuración"
997
+
998
+ client = InferenceClient(token=token)
999
+ return analizar_imagen_factura(imagen_path, client)
1000
+
1001
+ def procesar_segunda_factura(pdf_file, datos_json_primera):
1002
+ if not pdf_file:
1003
+ return "❌ Sube una segunda factura", None
1004
+
1005
+ if not datos_json_primera:
1006
+ return "❌ Procesa la primera factura en la pestaña 'Extracción Automática'", None
1007
+
1008
+ # Procesar segunda factura
1009
+ texto = extraer_texto_pdf(pdf_file)
1010
+ datos_json_segunda, _, _ = analizar_y_convertir_json(texto)
1011
+
1012
+ if not datos_json_segunda:
1013
+ return "❌ No se pudo procesar la segunda factura", None
1014
+
1015
+ # Comparar
1016
+ resultado = comparar_facturas(datos_json_primera, datos_json_segunda)
1017
+ return resultado, datos_json_segunda
1018
+
1019
+ # Conectar eventos de análisis avanzado
1020
+ btn_sentimiento.click(fn=ejecutar_sentimiento, inputs=[texto_extraido], outputs=[resultado_sentimiento])
1021
+ btn_sugerencias.click(fn=ejecutar_sugerencias, inputs=[datos_json_state], outputs=[resultado_sugerencias])
1022
+ btn_categoria.click(fn=ejecutar_categoria, inputs=[datos_json_state], outputs=[resultado_categoria])
1023
+ btn_traducir.click(fn=ejecutar_traduccion, inputs=[texto_extraido, idioma_selector], outputs=[resultado_traduccion])
1024
+ btn_analizar_imagen.click(fn=ejecutar_analisis_imagen, inputs=[imagen_input], outputs=[resultado_imagen])
1025
+
1026
+ # Comparador
1027
+ btn_comparar.click(
1028
+ fn=procesar_segunda_factura,
1029
+ inputs=[pdf_input_comparar, datos_json_state],
1030
+ outputs=[resultado_comparacion, datos_json_comparar_state]
1031
  )
1032
 
1033
  if __name__ == "__main__":