angelsg213 commited on
Commit
063cabc
·
verified ·
1 Parent(s): c25d302

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +256 -714
app.py CHANGED
@@ -16,21 +16,6 @@ import time
16
  import numpy as np
17
  import wave
18
 
19
- # Para TTS emocional con manipulación de audio
20
- try:
21
- from gtts import gTTS
22
- from pydub import AudioSegment
23
- from pydub.effects import speedup, normalize
24
- import soundfile as sf
25
- import librosa
26
- GTTS_AVAILABLE = True
27
- AUDIO_MANIPULATION = True
28
- except ImportError as e:
29
- GTTS_AVAILABLE = False
30
- AUDIO_MANIPULATION = False
31
- print(f"⚠️ Bibliotecas de audio no disponibles: {e}")
32
- print("Instala con: pip install gtts pydub soundfile librosa")
33
-
34
  # ============= EXTRAER TEXTO DEL PDF =============
35
  def extraer_texto_pdf(pdf_file):
36
  try:
@@ -46,258 +31,90 @@ def extraer_texto_pdf(pdf_file):
46
  # ============= GENERAR AUDIO CON EMOCIÓN MEJORADO =============
47
  # ============= GENERAR AUDIO CON EMOCIÓN MEJORADO =============
48
  # ============= GENERAR AUDIO CON EMOCIÓN Y ANÁLISIS DE SENTIMIENTO =============
49
- # ============= GENERAR AUDIO CON EMOCIÓN - VERSIÓN CORREGIDA =============
50
- # ============= APLICAR EMOCIÓN AL AUDIO =============
51
- def aplicar_emocion_audio(audio_path, emocion, confianza):
52
- """
53
- Modifica el audio según la emoción detectada
54
-
55
- Emociones y sus efectos:
56
- - joy/excitement: Más rápido, tono más alto, más energía
57
- - sadness: Más lento, tono más bajo, menos energía
58
- - anger: Más rápido, más volumen, más intensidad
59
- - fear: Ligeramente más rápido, tono variable
60
- - neutral: Sin modificaciones
61
- """
62
-
63
- if not AUDIO_MANIPULATION:
64
- print("⚠️ Manipulación de audio no disponible")
65
- return audio_path
66
-
67
- try:
68
- print(f"🎭 Aplicando emoción '{emocion}' al audio...")
69
-
70
- # Cargar audio
71
- audio = AudioSegment.from_mp3(audio_path)
72
-
73
- # Configuración de efectos según emoción
74
- efectos = {
75
- 'joy': {
76
- 'speed': 1.15, # 15% más rápido
77
- 'pitch': 1.5, # Tono más alto
78
- 'volume': 2.0, # Más volumen
79
- 'descripcion': 'alegre y enérgico'
80
- },
81
- 'excitement': {
82
- 'speed': 1.20, # 20% más rápido
83
- 'pitch': 2.0, # Tono mucho más alto
84
- 'volume': 3.0, # Bastante más volumen
85
- 'descripcion': 'emocionado y entusiasta'
86
- },
87
- 'sadness': {
88
- 'speed': 0.85, # 15% más lento
89
- 'pitch': -1.5, # Tono más bajo
90
- 'volume': -2.0, # Menos volumen
91
- 'descripcion': 'triste y melancólico'
92
- },
93
- 'anger': {
94
- 'speed': 1.10, # 10% más rápido
95
- 'pitch': 0.5, # Tono ligeramente alto
96
- 'volume': 4.0, # Más volumen
97
- 'descripcion': 'enfadado e intenso'
98
- },
99
- 'fear': {
100
- 'speed': 1.12, # 12% más rápido
101
- 'pitch': 1.0, # Tono normal-alto
102
- 'volume': 1.0, # Volumen normal
103
- 'descripcion': 'nervioso y temeroso'
104
- },
105
- 'surprise': {
106
- 'speed': 1.18, # 18% más rápido
107
- 'pitch': 2.5, # Tono muy alto
108
- 'volume': 2.5, # Más volumen
109
- 'descripcion': 'sorprendido'
110
- },
111
- 'neutral': {
112
- 'speed': 1.0, # Sin cambios
113
- 'pitch': 0, # Sin cambios
114
- 'volume': 0, # Sin cambios
115
- 'descripcion': 'neutral y calmado'
116
- }
117
- }
118
-
119
- # Obtener configuración de la emoción
120
- config = efectos.get(emocion, efectos['neutral'])
121
-
122
- # Ajustar efectos según nivel de confianza
123
- intensidad = confianza # 0.0 a 1.0
124
- speed_final = 1.0 + (config['speed'] - 1.0) * intensidad
125
- pitch_final = config['pitch'] * intensidad
126
- volume_final = config['volume'] * intensidad
127
-
128
- print(f" • Velocidad: {speed_final:.2f}x")
129
- print(f" • Tono: {pitch_final:+.1f} semitonos")
130
- print(f" • Volumen: {volume_final:+.1f} dB")
131
- print(f" • Estilo: {config['descripcion']}")
132
-
133
- # 1. Ajustar velocidad
134
- if speed_final != 1.0:
135
- audio = audio.speedup(playback_speed=speed_final)
136
-
137
- # 2. Ajustar volumen
138
- if volume_final != 0:
139
- audio = audio + volume_final
140
-
141
- # 3. Ajustar tono (pitch shifting)
142
- if pitch_final != 0:
143
- # Convertir a numpy array para procesamiento
144
- samples = audio.get_array_of_samples()
145
- audio_array = np.array(samples).astype(np.float32)
146
-
147
- # Normalizar
148
- if audio_array.max() > 0:
149
- audio_array = audio_array / np.abs(audio_array).max()
150
-
151
- # Pitch shifting con librosa
152
- sample_rate = audio.frame_rate
153
- n_steps = pitch_final # Semitonos
154
-
155
- audio_shifted = librosa.effects.pitch_shift(
156
- audio_array,
157
- sr=sample_rate,
158
- n_steps=n_steps
159
- )
160
-
161
- # Convertir de vuelta a AudioSegment
162
- audio_shifted = (audio_shifted * 32767).astype(np.int16)
163
- audio = AudioSegment(
164
- audio_shifted.tobytes(),
165
- frame_rate=sample_rate,
166
- sample_width=2,
167
- channels=1
168
- )
169
-
170
- # 4. Normalizar audio final
171
- audio = normalize(audio)
172
-
173
- # Guardar audio modificado
174
- output_path = audio_path.replace('.mp3', f'_emocional_{emocion}.mp3')
175
- audio.export(output_path, format="mp3", bitrate="128k")
176
-
177
- # Verificar archivo
178
- if os.path.exists(output_path) and os.path.getsize(output_path) > 1000:
179
- print(f"✅ Audio emocional creado: {output_path}")
180
- # Eliminar archivo original
181
- try:
182
- os.remove(audio_path)
183
- except:
184
- pass
185
- return output_path
186
- else:
187
- print("⚠️ Error al crear audio emocional, usando original")
188
- return audio_path
189
-
190
- except Exception as e:
191
- print(f"❌ Error aplicando emoción: {str(e)[:200]}")
192
- print(" Usando audio sin modificar")
193
- return audio_path
194
-
195
  def generar_audio_respuesta(texto, client):
196
- """TTS emocional FUNCIONAL con gTTS (Google Text-to-Speech) - Diciembre 2024"""
197
-
198
- try:
199
- # Limpiar y preparar texto
200
- texto_limpio = texto.replace("*", "").replace("#", "").replace("`", "").replace("€", " euros").strip()
201
- oraciones = re.split(r'[.!?]+', texto_limpio)
202
- oraciones = [o.strip() for o in oraciones if o.strip() and len(o.strip()) > 10]
203
- texto_audio = ". ".join(oraciones[:5]) + "." if len(oraciones) > 5 else ". ".join(oraciones) + "."
204
-
205
- if len(texto_audio) > 500:
206
- texto_audio = texto_audio[:497] + "..."
207
-
208
- print(f"🎤 Generando audio para: '{texto_audio[:100]}...'")
209
-
210
- # PASO 1: Análisis emocional
211
- emocion_detectada = "neutral"
212
- confianza = 0.5
213
-
214
- try:
215
- print("🧠 Analizando emoción...")
216
- emotion_response = client.text_classification(
217
- text=texto_audio[:512],
218
- model="finiteautomata/beto-sentiment-analysis"
219
- )
220
- if emotion_response and len(emotion_response) > 0:
221
- label = emotion_response[0]['label'].lower()
222
- sentiment_to_emotion = {
223
- 'pos': 'joy',
224
- 'positive': 'joy',
225
- 'neu': 'neutral',
226
- 'neutral': 'neutral',
227
- 'neg': 'sadness',
228
- 'negative': 'sadness'
229
- }
230
- emocion_detectada = sentiment_to_emotion.get(label, 'neutral')
231
- confianza = emotion_response[0]['score']
232
- print(f"😊 Emoción: {emocion_detectada} (confianza: {confianza:.2%})")
233
- except Exception as e:
234
- print(f"⚠️ Error en análisis emocional: {str(e)[:100]}")
235
-
236
- # PASO 2: Generar audio con gTTS
237
- print("🔊 Generando audio con Google TTS...")
238
-
239
- if GTTS_AVAILABLE:
240
- tts = gTTS(text=texto_audio, lang='es', slow=False)
241
- timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
242
- audio_path_temp = f"audio_temp_{timestamp}.mp3"
243
- tts.save(audio_path_temp)
244
-
245
- if os.path.exists(audio_path_temp) and os.path.getsize(audio_path_temp) > 1000:
246
- print(f"✅ Audio base generado: {audio_path_temp}")
247
-
248
- # PASO 3: Aplicar efectos emocionales
249
- audio_path_final = aplicar_emocion_audio(audio_path_temp, emocion_detectada, confianza)
250
-
251
- print(f"🎭 Audio final con emoción: {audio_path_final}")
252
- return audio_path_final, emocion_detectada, confianza
253
-
254
- except Exception as e:
255
- print(f"❌ Error general: {str(e)}")
256
- return None, "neutral", 0.5
257
-
258
-
259
- def generar_audio_alternativo(texto, client):
260
- """Método alternativo usando HuggingFace TTS"""
261
- emocion_detectada = "neutral"
262
- confianza = 0.5
263
 
 
264
  texto_limpio = texto.replace("*", "").replace("#", "").replace("`", "").replace("€", " euros").strip()
265
  oraciones = re.split(r'[.!?]+', texto_limpio)
266
  oraciones = [o.strip() for o in oraciones if o.strip() and len(o.strip()) > 10]
267
- texto_audio = ". ".join(oraciones[:3]) + "."
268
-
269
  if len(texto_audio) > 400:
270
  texto_audio = texto_audio[:397] + "..."
271
 
272
- modelos_tts = ["facebook/mms-tts-spa"]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
273
 
274
  for modelo in modelos_tts:
275
  try:
276
  print(f"🔊 Probando: {modelo}")
277
- audio_data = client.text_to_speech(text=texto_audio, model=modelo)
278
 
 
 
 
 
 
 
 
279
  timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
280
- audio_path = f"audio_{timestamp}.wav"
281
 
282
  with open(audio_path, "wb") as f:
283
  if isinstance(audio_data, bytes):
284
  f.write(audio_data)
285
  elif hasattr(audio_data, 'read'):
286
  f.write(audio_data.read())
 
 
287
  else:
 
288
  for chunk in audio_data:
289
  if chunk:
290
  f.write(chunk if isinstance(chunk, bytes) else bytes(chunk))
291
 
292
- if os.path.exists(audio_path) and os.path.getsize(audio_path) > 1000:
293
- print(f"✅ Audio generado con {modelo}")
294
- return audio_path, emocion_detectada, confianza
295
- else:
296
- if os.path.exists(audio_path):
 
 
 
 
 
297
  os.remove(audio_path)
 
298
  except Exception as e:
299
- print(f"❌ Error con {modelo}: {str(e)[:100]}")
 
 
300
 
 
301
  return None, emocion_detectada, confianza
302
 
303
  # ============= ASISTENTE IA CONVERSACIONAL =============
@@ -379,16 +196,12 @@ Responde ahora:"""
379
  f.write(f"\nArchivo de audio: {audio_path if audio_path else 'No generado'}\n")
380
  f.write("=" * 60 + "\n")
381
 
382
- if audio_path and os.path.exists(audio_path):
383
  print(f"✅ Audio generado correctamente: {audio_path}")
384
- return respuesta, audio_path, transcripcion_path, emocion, confianza
385
  else:
386
  print("⚠️ No se pudo generar el audio, pero la respuesta está disponible")
387
- timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
388
- audio_vacio = f"audio_no_disponible_{timestamp}.mp3"
389
- with open(audio_vacio, "w") as f:
390
- f.write("")
391
- return respuesta, audio_vacio, transcripcion_path, emocion, confianza
392
 
393
  except Exception as e:
394
  print(f"❌ Error con {modelo}: {str(e)}")
@@ -1540,305 +1353,41 @@ def generar_pdf_con_template(template, csv_file, datos_json):
1540
  return None, f"Error al generar PDF: {str(e)}"
1541
 
1542
  # ============= INTERFAZ GRADIO =============
1543
- # ============= CSS PERSONALIZADO PARA DISEÑO CON MENÚ LATERAL =============
1544
- custom_css = """
1545
- /* Contenedor principal */
1546
- .gradio-container {
1547
- max-width: 100% !important;
1548
- padding: 0 !important;
1549
- }
1550
-
1551
- /* Header fijo con logo */
1552
- #header-container {
1553
- position: fixed;
1554
- top: 0;
1555
- left: 0;
1556
- right: 0;
1557
- height: 70px;
1558
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
1559
- z-index: 1000;
1560
- display: flex;
1561
- align-items: center;
1562
- padding: 0 20px;
1563
- box-shadow: 0 2px 10px rgba(0,0,0,0.1);
1564
- }
1565
-
1566
- #app-title {
1567
- color: white;
1568
- font-size: 28px;
1569
- font-weight: bold;
1570
- margin-left: 280px;
1571
- transition: margin-left 0.3s ease;
1572
- }
1573
-
1574
- #app-title.menu-collapsed {
1575
- margin-left: 80px;
1576
- }
1577
-
1578
- /* Menú lateral */
1579
- #sidebar {
1580
- position: fixed;
1581
- left: 0;
1582
- top: 70px;
1583
- bottom: 0;
1584
- width: 260px;
1585
- background: linear-gradient(180deg, #2c3e50 0%, #34495e 100%);
1586
- box-shadow: 2px 0 10px rgba(0,0,0,0.1);
1587
- transition: width 0.3s ease;
1588
- overflow-y: auto;
1589
- overflow-x: hidden;
1590
- z-index: 999;
1591
- }
1592
-
1593
- #sidebar.collapsed {
1594
- width: 60px;
1595
- }
1596
-
1597
- /* Botón toggle menú */
1598
- #toggle-menu-btn {
1599
- position: absolute;
1600
- top: 15px;
1601
- right: 10px;
1602
- background: rgba(255,255,255,0.1);
1603
- border: none;
1604
- color: white;
1605
- width: 40px;
1606
- height: 40px;
1607
- border-radius: 8px;
1608
- cursor: pointer;
1609
- font-size: 20px;
1610
- transition: all 0.3s ease;
1611
- }
1612
-
1613
- #toggle-menu-btn:hover {
1614
- background: rgba(255,255,255,0.2);
1615
- transform: scale(1.1);
1616
- }
1617
-
1618
- /* Items del menú */
1619
- .menu-item {
1620
- padding: 15px 20px;
1621
- color: #ecf0f1;
1622
- cursor: pointer;
1623
- display: flex;
1624
- align-items: center;
1625
- gap: 15px;
1626
- transition: all 0.3s ease;
1627
- border-left: 3px solid transparent;
1628
- }
1629
-
1630
- .menu-item:hover {
1631
- background: rgba(255,255,255,0.1);
1632
- border-left-color: #667eea;
1633
- transform: translateX(5px);
1634
- }
1635
-
1636
- .menu-item.active {
1637
- background: rgba(102, 126, 234, 0.2);
1638
- border-left-color: #667eea;
1639
- }
1640
-
1641
- .menu-item-icon {
1642
- font-size: 24px;
1643
- min-width: 30px;
1644
- text-align: center;
1645
- }
1646
-
1647
- .menu-item-text {
1648
- font-size: 15px;
1649
- font-weight: 500;
1650
- white-space: nowrap;
1651
- transition: opacity 0.3s ease;
1652
- }
1653
-
1654
- #sidebar.collapsed .menu-item-text {
1655
- opacity: 0;
1656
- width: 0;
1657
- }
1658
-
1659
- #sidebar.collapsed .menu-item {
1660
- justify-content: center;
1661
- padding: 15px 10px;
1662
- }
1663
-
1664
- /* Contenido principal */
1665
- #main-content {
1666
- margin-left: 260px;
1667
- margin-top: 70px;
1668
- padding: 30px;
1669
- transition: margin-left 0.3s ease;
1670
- min-height: calc(100vh - 70px);
1671
- }
1672
-
1673
- #main-content.menu-collapsed {
1674
- margin-left: 60px;
1675
- }
1676
-
1677
- /* Secciones */
1678
- .section-container {
1679
- display: none;
1680
- animation: fadeIn 0.4s ease;
1681
- }
1682
-
1683
- .section-container.active {
1684
- display: block;
1685
- }
1686
-
1687
- @keyframes fadeIn {
1688
- from {
1689
- opacity: 0;
1690
- transform: translateY(20px);
1691
- }
1692
- to {
1693
- opacity: 1;
1694
- transform: translateY(0);
1695
- }
1696
- }
1697
-
1698
- /* Separador del menú */
1699
- .menu-separator {
1700
- height: 1px;
1701
- background: rgba(255,255,255,0.1);
1702
- margin: 10px 20px;
1703
- }
1704
-
1705
- /* Versión Beta badge */
1706
- .beta-badge {
1707
- background: rgba(255, 193, 7, 0.2);
1708
- color: #ffc107;
1709
- padding: 2px 8px;
1710
- border-radius: 4px;
1711
- font-size: 12px;
1712
- font-weight: bold;
1713
- margin-left: 8px;
1714
- }
1715
-
1716
- /* Scrollbar personalizado */
1717
- #sidebar::-webkit-scrollbar {
1718
- width: 6px;
1719
- }
1720
-
1721
- #sidebar::-webkit-scrollbar-track {
1722
- background: rgba(0,0,0,0.1);
1723
- }
1724
-
1725
- #sidebar::-webkit-scrollbar-thumb {
1726
- background: rgba(255,255,255,0.2);
1727
- border-radius: 3px;
1728
- }
1729
-
1730
- #sidebar::-webkit-scrollbar-thumb:hover {
1731
- background: rgba(255,255,255,0.3);
1732
- }
1733
-
1734
- /* Responsivo */
1735
- @media (max-width: 768px) {
1736
- #sidebar {
1737
- width: 60px;
1738
- }
1739
-
1740
- #main-content {
1741
- margin-left: 60px;
1742
- }
1743
-
1744
- #app-title {
1745
- margin-left: 80px;
1746
- font-size: 20px;
1747
- }
1748
 
1749
- .menu-item-text {
1750
- display: none;
1751
- }
1752
- }
1753
- """
1754
-
1755
- with gr.Blocks(title="DeepBill ~ Extractor Inteligente") as demo:
1756
-
1757
- # Inyectar CSS personalizado
1758
- gr.HTML(f"<style>{custom_css}</style>")
1759
- # Estados
1760
- datos_json_state = gr.State()
1761
- csv_file_state = gr.State()
1762
-
1763
- # Estados
1764
  datos_json_state = gr.State()
1765
  csv_file_state = gr.State()
1766
  pdf_path_state = gr.State()
1767
  texto_state = gr.State()
1768
- seccion_actual = gr.State("extraccion") # Estado para controlar sección visible
1769
-
1770
- # ============= HEADER FIJO =============
1771
- with gr.Row(elem_id="header-container"):
1772
- gr.HTML("""
1773
- <div id="app-title">
1774
- DeepBill<span class="beta-badge">Beta</span>
1775
- </div>
1776
- """)
1777
-
1778
- # ============= MENÚ LATERAL =============
1779
- with gr.Column(elem_id="sidebar", scale=0):
1780
- gr.HTML("""
1781
- <button id="toggle-menu-btn" onclick="toggleMenu()">☰</button>
1782
- """)
1783
-
1784
- # Items del menú
1785
- btn_menu_extraccion = gr.Button(
1786
- value="📄 Extracción",
1787
- elem_classes="menu-item active",
1788
- elem_id="menu-extraccion"
1789
- )
1790
-
1791
- btn_menu_asistente = gr.Button(
1792
- value="🤖 Asistente IA",
1793
- elem_classes="menu-item",
1794
- elem_id="menu-asistente"
1795
- )
1796
-
1797
- btn_menu_analisis = gr.Button(
1798
- value="🔬 Análisis Avanzado",
1799
- elem_classes="menu-item",
1800
- elem_id="menu-analisis"
1801
- )
1802
-
1803
- btn_menu_traduccion = gr.Button(
1804
- value="🌍 Traducción",
1805
- elem_classes="menu-item",
1806
- elem_id="menu-traduccion"
1807
- )
1808
-
1809
- gr.HTML('<div class="menu-separator"></div>')
1810
-
1811
- btn_menu_ayuda = gr.Button(
1812
- value="❓ Ayuda",
1813
- elem_classes="menu-item",
1814
- elem_id="menu-ayuda"
1815
- )
1816
 
1817
- # ============= CONTENIDO PRINCIPAL =============
1818
- with gr.Column(elem_id="main-content"):
1819
-
1820
- # ===== SECCIÓN 1: EXTRACCIÓN =====
1821
- with gr.Column(elem_classes="section-container active", elem_id="section-extraccion"):
1822
- gr.Markdown("# 📄 Extracción Automática de Datos")
1823
- gr.Markdown("Sube tu factura en PDF y obtén los datos estructurados automáticamente")
1824
-
 
 
1825
  with gr.Row():
1826
  with gr.Column(scale=1):
1827
  gr.Markdown("### 📤 Subir Factura PDF")
1828
  pdf_input = gr.File(label="Seleccionar factura PDF", file_types=[".pdf"], type="filepath")
1829
  btn_extraer = gr.Button("🚀 Extraer Datos de la Factura", variant="primary", size="lg")
1830
 
 
1831
  loading_extraccion = gr.HTML(visible=False, value="""
1832
  <div style="text-align: center; padding: 20px;">
1833
  <div class="spinner"></div>
1834
- <p style="margin-top: 10px; color: #667eea; font-weight: bold;">
1835
  🔄 Procesando tu factura...
1836
  </p>
1837
  </div>
1838
  <style>
1839
  .spinner {
1840
  border: 3px solid #f3f3f3;
1841
- border-top: 3px solid #667eea;
1842
  border-radius: 50%;
1843
  width: 35px;
1844
  height: 35px;
@@ -1854,7 +1403,7 @@ with gr.Blocks(title="DeepBill ~ Extractor Inteligente") as demo:
1854
 
1855
  gr.Markdown("---")
1856
  gr.Markdown("### 📥 Descargar Archivos")
1857
- csv_output = gr.File(label="📊 CSV Tabular")
1858
 
1859
  gr.Markdown("---")
1860
  gr.Markdown("### 🎨 Rediseñar PDF")
@@ -1873,16 +1422,19 @@ with gr.Blocks(title="DeepBill ~ Extractor Inteligente") as demo:
1873
  gr.Markdown("---")
1874
  with gr.Tabs():
1875
  with gr.Tab("📋 Vista Previa CSV"):
1876
- tabla_preview = gr.DataFrame(label="Datos estructurados", wrap=True)
1877
  with gr.Tab("📝 Texto Original"):
1878
  texto_extraido = gr.Textbox(label="Texto extraído del PDF", lines=18)
1879
  with gr.Tab("🔍 Información Técnica"):
1880
- resumen_tecnico = gr.Markdown(label="Estructura de datos")
1881
 
1882
- # ===== SECCIÓN 2: ASISTENTE IA =====
1883
- with gr.Column(elem_classes="section-container", elem_id="section-asistente", visible=False):
1884
- gr.Markdown("# 🤖 Asistente Virtual con Análisis Emocional")
1885
- gr.Markdown("Pregunta cualquier cosa sobre tu factura y recibe respuestas con voz emocional")
 
 
 
1886
 
1887
  with gr.Row():
1888
  with gr.Column(scale=1):
@@ -1895,6 +1447,7 @@ with gr.Blocks(title="DeepBill ~ Extractor Inteligente") as demo:
1895
 
1896
  btn_consulta_ia = gr.Button("🎤 Consultar Asistente IA", variant="primary", size="lg")
1897
 
 
1898
  loading_ia = gr.HTML(visible=False, value="""
1899
  <div style="text-align: center; padding: 20px;">
1900
  <div class="spinner-ia"></div>
@@ -1922,22 +1475,29 @@ with gr.Blocks(title="DeepBill ~ Extractor Inteligente") as demo:
1922
  - ¿Qué es la base imponible?
1923
  - ¿Cuándo debo pagar esta factura?
1924
  - ¿Hay algún descuento aplicado?
 
1925
  """)
1926
 
 
1927
  emocion_detectada = gr.Markdown(value="", visible=True)
1928
 
1929
  with gr.Column(scale=2):
1930
- gr.Markdown("### 🤖 Avatar Virtual")
1931
 
 
1932
  avatar_html = gr.HTML(value="""
1933
- <div style="text-align: center; padding: 30px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 20px;">
1934
- <div class="robot-avatar">
1935
  <svg width="120" height="120" viewBox="0 0 120 120">
 
1936
  <rect x="30" y="35" width="60" height="50" rx="10" fill="#ffffff" stroke="#667eea" stroke-width="3"/>
 
1937
  <line x1="60" y1="35" x2="60" y2="20" stroke="#667eea" stroke-width="3"/>
1938
  <circle cx="60" cy="15" r="5" fill="#764ba2"/>
 
1939
  <circle cx="45" cy="55" r="6" fill="#667eea" class="eye-blink"/>
1940
  <circle cx="75" cy="55" r="6" fill="#667eea" class="eye-blink"/>
 
1941
  <rect x="40" y="70" width="40" height="8" rx="4" fill="#667eea"/>
1942
  </svg>
1943
  </div>
@@ -1946,7 +1506,9 @@ with gr.Blocks(title="DeepBill ~ Extractor Inteligente") as demo:
1946
  </p>
1947
  </div>
1948
  <style>
1949
- .eye-blink { animation: blink 3s infinite; }
 
 
1950
  @keyframes blink {
1951
  0%, 49%, 51%, 100% { opacity: 1; }
1952
  50% { opacity: 0.3; }
@@ -1955,21 +1517,44 @@ with gr.Blocks(title="DeepBill ~ Extractor Inteligente") as demo:
1955
  """)
1956
 
1957
  gr.Markdown("### 📝 Respuesta del Asistente")
1958
- resultado_ia = gr.Markdown(value="*Haz una pregunta...*")
 
 
1959
 
1960
  gr.Markdown("---")
1961
- gr.Markdown("### 🔊 Audio y Transcripción")
1962
 
1963
  with gr.Row():
1964
- audio_respuesta = gr.Audio(label="🎧 Audio", type="filepath", autoplay=True)
1965
- transcripcion_output = gr.File(label="📄 Transcripción")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1966
 
1967
- # ===== SECCIÓN 3: ANÁLISIS AVANZADO =====
1968
- with gr.Column(elem_classes="section-container", elem_id="section-analisis", visible=False):
1969
- gr.Markdown("# 🔬 Suite de Análisis Avanzado IA")
1970
- gr.Markdown("12 herramientas profesionales de análisis automático")
 
 
1971
 
1972
  with gr.Tabs():
 
1973
  with gr.Tab("💰 Análisis Financiero"):
1974
  with gr.Row():
1975
  with gr.Column():
@@ -1977,12 +1562,14 @@ with gr.Blocks(title="DeepBill ~ Extractor Inteligente") as demo:
1977
  btn_sentimiento = gr.Button("🔍 Analizar Riesgos", variant="primary")
1978
  resultado_sentimiento = gr.Markdown()
1979
 
 
1980
  gr.Markdown("### 💰 Gastos Deducibles")
1981
- btn_deducibles = gr.Button("💸 Calcular", variant="primary")
1982
  resultado_deducibles = gr.Markdown()
1983
 
 
1984
  gr.Markdown("### 💵 Impacto Presupuestario")
1985
- btn_impacto = gr.Button("📊 Calcular", variant="primary")
1986
  resultado_impacto = gr.Markdown()
1987
 
1988
  with gr.Column():
@@ -1990,187 +1577,156 @@ with gr.Blocks(title="DeepBill ~ Extractor Inteligente") as demo:
1990
  btn_prediccion = gr.Button("🔮 Fecha Óptima", variant="primary")
1991
  resultado_prediccion = gr.Markdown()
1992
 
 
1993
  gr.Markdown("### 💡 Sugerencias IA")
1994
- btn_sugerencias = gr.Button("✨ Generar", variant="primary")
1995
  resultado_sugerencias = gr.Markdown()
1996
 
 
1997
  gr.Markdown("### 📁 Categorización")
1998
- btn_categoria = gr.Button("🏷️ Clasificar", variant="primary")
1999
  resultado_categoria = gr.Markdown()
2000
 
2001
- with gr.Tab("🛡️ Seguridad"):
 
2002
  with gr.Row():
2003
  with gr.Column():
2004
  gr.Markdown("### 🚨 Detector de Fraude")
2005
- btn_fraude = gr.Button("🛡️ Detectar", variant="primary")
2006
  resultado_fraude = gr.Markdown()
2007
 
2008
- gr.Markdown("### 🔍 Duplicados")
2009
- btn_duplicados = gr.Button("🔄 Buscar", variant="primary")
 
2010
  resultado_duplicados = gr.Markdown()
2011
 
 
2012
  gr.Markdown("### ✅ Validador Fiscal")
2013
- btn_validador = gr.Button("📋 Validar", variant="primary")
2014
  resultado_validador = gr.Markdown()
2015
 
2016
  with gr.Column():
2017
- gr.Markdown("### 📝 Condiciones Pago")
2018
- btn_condiciones = gr.Button("📄 Analizar", variant="primary")
2019
  resultado_condiciones = gr.Markdown()
2020
 
2021
- gr.Markdown("### 🔔 Recordatorios")
2022
- btn_recordatorios = gr.Button(" Generar", variant="primary")
 
2023
  resultado_recordatorios = gr.Markdown()
2024
 
 
2025
  gr.Markdown("### 📊 Resumen Ejecutivo")
2026
- btn_ejecutivo = gr.Button("📈 Dashboard", variant="primary")
2027
  resultado_ejecutivo = gr.Markdown()
2028
 
2029
- with gr.Tab("📈 Mercado"):
2030
- gr.Markdown("### 💲 Comparador de Precios")
2031
- btn_mercado = gr.Button("🏪 Comparar", variant="primary", size="lg")
 
2032
  resultado_mercado = gr.Markdown()
 
 
 
 
 
 
 
 
 
2033
 
2034
- # ===== SECCIÓN 4: TRADUCCIÓN =====
2035
- with gr.Column(elem_classes="section-container", elem_id="section-traduccion", visible=False):
2036
- gr.Markdown("# 🌍 Traducción Internacional")
2037
- gr.Markdown("Traduce tu factura a 5 idiomas con exportación a CSV")
 
 
2038
 
2039
  with gr.Row():
2040
  with gr.Column():
 
2041
  idioma_selector = gr.Dropdown(
2042
  choices=["Inglés", "Francés", "Alemán", "Italiano", "Portugués"],
2043
  value="Inglés",
2044
  label="🗣️ Idioma de destino"
2045
  )
2046
  btn_traducir = gr.Button("🌐 Traducir Factura", variant="primary", size="lg")
2047
- csv_traduccion_output = gr.File(label="📊 Descargar CSV Traducido")
 
 
 
2048
 
2049
  with gr.Column():
2050
  gr.Markdown("### 📊 Vista Tabular Traducida")
2051
- tabla_traduccion = gr.DataFrame(label="Factura traducida", wrap=True)
 
 
 
 
 
2052
  gr.Markdown("### 📝 Texto Traducido")
2053
- resultado_traduccion = gr.Textbox(label="Resumen", lines=10)
2054
-
2055
- # ===== SECCIÓN 5: AYUDA =====
2056
- with gr.Column(elem_classes="section-container", elem_id="section-ayuda", visible=False):
2057
- gr.Markdown("# ❓ Guía de Uso - DeepBill")
2058
- gr.Markdown("""
2059
- ## 🚀 Inicio Rápido
2060
-
2061
- 1. **📄 Extracción:** Sube tu PDF para obtener datos estructurados
2062
- 2. **🤖 Asistente IA:** Haz preguntas sobre tu factura
2063
- 3. **🔬 Análisis:** Usa 12 herramientas profesionales
2064
- 4. **🌍 Traducción:** Exporta en 5 idiomas
2065
-
2066
- ## 🎯 Funcionalidades Principales
2067
-
2068
- ### 💰 Análisis Financiero
2069
- - Análisis de riesgos y urgencias
2070
- - Cálculo de gastos deducibles
2071
- - Impacto presupuestario
2072
- - Predicción de fechas de pago
2073
- - Sugerencias personalizadas
2074
- - Categorización automática
2075
-
2076
- ### 🛡️ Seguridad y Validación
2077
- - Detección de fraude
2078
- - Búsqueda de duplicados
2079
- - Validación fiscal
2080
- - Análisis de condiciones
2081
- - Recordatorios inteligentes
2082
-
2083
- ### 📊 Formatos
2084
- - CSV tabular con comas
2085
- - PDF rediseñado
2086
- - Traducciones multiidioma
2087
-
2088
- ## 💡 Consejos
2089
-
2090
- - Usa PDFs de buena calidad
2091
- - Revisa la transcripción del audio
2092
- - Aprovecha las sugerencias IA
2093
- - Exporta y guarda los análisis
2094
- """)
2095
 
2096
- # JavaScript para control del menú
2097
- gr.HTML("""
2098
- <script>
2099
- function toggleMenu() {
2100
- const sidebar = document.getElementById('sidebar');
2101
- const mainContent = document.getElementById('main-content');
2102
- const appTitle = document.getElementById('app-title');
2103
-
2104
- sidebar.classList.toggle('collapsed');
2105
- mainContent.classList.toggle('menu-collapsed');
2106
- appTitle.classList.toggle('menu-collapsed');
2107
- }
2108
-
2109
- // Hacer items del menú clickeables
2110
- document.addEventListener('DOMContentLoaded', function() {
2111
- const menuItems = document.querySelectorAll('.menu-item');
2112
- menuItems.forEach(item => {
2113
- item.addEventListener('click', function() {
2114
- menuItems.forEach(i => i.classList.remove('active'));
2115
- this.classList.add('active');
2116
- });
2117
- });
2118
- });
2119
- </script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2120
  """)
2121
- # ============= FUNCIONES PARA NAVEGACIÓN =============
2122
- def mostrar_seccion(seccion_nombre):
2123
- """Cambia la sección visible"""
2124
- secciones = {
2125
- "extraccion": gr.update(visible=True),
2126
- "asistente": gr.update(visible=False),
2127
- "analisis": gr.update(visible=False),
2128
- "traduccion": gr.update(visible=False),
2129
- "ayuda": gr.update(visible=False)
2130
- }
2131
-
2132
- # Actualizar visibilidad
2133
- updates = {}
2134
- for nombre in secciones:
2135
- updates[nombre] = gr.update(visible=(nombre == seccion_nombre))
2136
-
2137
- return [updates[k] for k in ["extraccion", "asistente", "analisis", "traduccion", "ayuda"]]
2138
- # ============= CONECTAR EVENTOS =============
2139
- # ============= CONECTAR EVENTOS =============
2140
-
2141
- # Navegación del menú
2142
- # ============= CONECTAR EVENTOS =============
2143
-
2144
- # Navegación del menú - usando los componentes directamente
2145
- btn_menu_extraccion.click(
2146
- fn=lambda: (gr.update(visible=True), gr.update(visible=False), gr.update(visible=False),
2147
- gr.update(visible=False), gr.update(visible=False)),
2148
- outputs=[loading_extraccion, seccion_asistente, seccion_analisis, seccion_traduccion, seccion_ayuda]
2149
- )
2150
-
2151
- btn_menu_asistente.click(
2152
- fn=lambda: (gr.update(visible=False), gr.update(visible=True), gr.update(visible=False),
2153
- gr.update(visible=False), gr.update(visible=False)),
2154
- outputs=[seccion_extraccion, seccion_asistente, seccion_analisis, seccion_traduccion, seccion_ayuda]
2155
- )
2156
-
2157
- btn_menu_analisis.click(
2158
- fn=lambda: (gr.update(visible=False), gr.update(visible=False), gr.update(visible=True),
2159
- gr.update(visible=False), gr.update(visible=False)),
2160
- outputs=[seccion_extraccion, seccion_asistente, seccion_analisis, seccion_traduccion, seccion_ayuda]
2161
- )
2162
 
2163
- btn_menu_traduccion.click(
2164
- fn=lambda: (gr.update(visible=False), gr.update(visible=False), gr.update(visible=False),
2165
- gr.update(visible=True), gr.update(visible=False)),
2166
- outputs=[seccion_extraccion, seccion_asistente, seccion_analisis, seccion_traduccion, seccion_ayuda]
2167
- )
2168
 
2169
- btn_menu_ayuda.click(
2170
- fn=lambda: (gr.update(visible=False), gr.update(visible=False), gr.update(visible=False),
2171
- gr.update(visible=False), gr.update(visible=True)),
2172
- outputs=[seccion_extraccion, seccion_asistente, seccion_analisis, seccion_traduccion, seccion_ayuda]
2173
- )
2174
  # Extracción automática
2175
  def procesar_con_loading(pdf_file):
2176
  if pdf_file is None:
@@ -2202,23 +1758,22 @@ with gr.Blocks(title="DeepBill ~ Extractor Inteligente") as demo:
2202
 
2203
  yield ("🔄 El asistente está analizando tu pregunta...", None, None, "", gr.update(visible=True))
2204
  time.sleep(0.3)
 
2205
 
2206
- try:
2207
- respuesta, audio, transcripcion, emocion, confianza = asistente_ia_factura(texto, pregunta)
2208
-
2209
- emotion_map = {
2210
- "joy": ("😊", "#4CAF50", "Alegría"),
2211
- "excitement": ("🎉", "#FF9800", "Emoción"),
2212
- "anger": ("😠", "#F44336", "Enfado"),
2213
- "sadness": ("😢", "#2196F3", "Tristeza"),
2214
- "fear": ("😰", "#9C27B0", "Miedo"),
2215
- "surprise": ("😮", "#FF5722", "Sorpresa"),
2216
- "neutral": ("😐", "#607D8B", "Neutral")
2217
- }
2218
-
2219
- emoji, color, nombre = emotion_map.get(emocion, ("😐", "#607D8B", "Neutral"))
2220
-
2221
- emocion_info = f"""
2222
  ### 🎭 Análisis Emocional
2223
 
2224
  <div style="background: linear-gradient(135deg, {color}22 0%, {color}44 100%); padding: 15px; border-radius: 10px; border-left: 4px solid {color};">
@@ -2230,21 +1785,8 @@ with gr.Blocks(title="DeepBill ~ Extractor Inteligente") as demo:
2230
  </p>
2231
  </div>
2232
  """
2233
-
2234
- audio_final = audio if (audio and os.path.exists(audio) and os.path.getsize(audio) > 100) else None
2235
-
2236
- if audio_final:
2237
- print(f"✅ Audio disponible: {audio_final}")
2238
- else:
2239
- print("⚠️ Audio no disponible")
2240
- emocion_info += "\n\n⚠️ *El audio no pudo generarse, pero la respuesta está en texto.*"
2241
-
2242
- yield (respuesta, audio_final, transcripcion, emocion_info, gr.update(visible=False))
2243
-
2244
- except Exception as e:
2245
- error_msg = f"❌ Error: {str(e)[:200]}"
2246
- print(f"Error completo: {str(e)}")
2247
- yield (error_msg, None, None, "", gr.update(visible=False))
2248
 
2249
  btn_consulta_ia.click(
2250
  fn=consultar_ia_con_loading,
 
16
  import numpy as np
17
  import wave
18
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  # ============= EXTRAER TEXTO DEL PDF =============
20
  def extraer_texto_pdf(pdf_file):
21
  try:
 
31
  # ============= GENERAR AUDIO CON EMOCIÓN MEJORADO =============
32
  # ============= GENERAR AUDIO CON EMOCIÓN MEJORADO =============
33
  # ============= GENERAR AUDIO CON EMOCIÓN Y ANÁLISIS DE SENTIMIENTO =============
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
  def generar_audio_respuesta(texto, client):
35
+ """TTS emocional FUNCIONAL para español - Actualizado diciembre 2025"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
 
37
+ # Limpiar y preparar texto (mismo que antes)
38
  texto_limpio = texto.replace("*", "").replace("#", "").replace("`", "").replace("€", " euros").strip()
39
  oraciones = re.split(r'[.!?]+', texto_limpio)
40
  oraciones = [o.strip() for o in oraciones if o.strip() and len(o.strip()) > 10]
41
+ texto_audio = ". ".join(oraciones[:3]) + "." if len(oraciones) > 3 else ". ".join(oraciones) + "."
 
42
  if len(texto_audio) > 400:
43
  texto_audio = texto_audio[:397] + "..."
44
 
45
+ print(f"🎤 Generando audio para: '{texto_audio[:80]}...'")
46
+
47
+ # PASO 1: Análisis emocional (modelo español que SÍ funciona)
48
+ try:
49
+ print("🧠 Analizando emoción...")
50
+ emotion_response = client.text_classification(
51
+ text=texto_audio,
52
+ model="dariolopez/roberta-base-bne-finetuned-EmotionAnalysisSpanish" # Español nativo
53
+ )
54
+ if emotion_response and len(emotion_response) > 0:
55
+ emocion_detectada = emotion_response[0]['label']
56
+ confianza = emotion_response[0]['score']
57
+ print(f"😊 Emoción: {emocion_detectada} (confianza: {confianza:.2%})")
58
+ else:
59
+ emocion_detectada = "neutral"
60
+ confianza = 0.5
61
+ except Exception as e:
62
+ print(f"⚠️ Error emocional: {str(e)[:100]}. Usando neutral.")
63
+ emocion_detectada = "neutral"
64
+ confianza = 0.5
65
+
66
+ # PASO 2: Modelos TTS que SÍ funcionan en 2025 (español prioritario)
67
+ modelos_tts = [
68
+ "facebook/mms-tts-spa", # Español oficial de Meta - Siempre funciona
69
+ "myshell-ai/MeloTTS-Spanish", # Alta calidad, multi-idioma
70
+ "coqui/XTTS-v2" # Fallback versátil (soporta español)
71
+ ]
72
 
73
  for modelo in modelos_tts:
74
  try:
75
  print(f"🔊 Probando: {modelo}")
 
76
 
77
+ # Generar audio
78
+ audio_data = client.text_to_speech(
79
+ text=texto_audio,
80
+ model=modelo
81
+ )
82
+
83
+ # Guardar archivo (mejorado para streams/bytes)
84
  timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
85
+ audio_path = f"audio_emocional_{emocion_detectada}_{timestamp}.wav"
86
 
87
  with open(audio_path, "wb") as f:
88
  if isinstance(audio_data, bytes):
89
  f.write(audio_data)
90
  elif hasattr(audio_data, 'read'):
91
  f.write(audio_data.read())
92
+ elif hasattr(audio_data, 'content'):
93
+ f.write(audio_data.content)
94
  else:
95
+ # Para iteradores/chunks
96
  for chunk in audio_data:
97
  if chunk:
98
  f.write(chunk if isinstance(chunk, bytes) else bytes(chunk))
99
 
100
+ # Verificar
101
+ if os.path.exists(audio_path):
102
+ size = os.path.getsize(audio_path)
103
+ print(f"📁 Creado: {audio_path} ({size} bytes)")
104
+
105
+ if size > 2000: # Umbral más bajo para MMS
106
+ print(f"✅ ¡AUDIO GENERADO EXITOSAMENTE!")
107
+ return audio_path, emocion_detectada, confianza
108
+ else:
109
+ print(f"⚠️ Archivo pequeño ({size} bytes), borrando...")
110
  os.remove(audio_path)
111
+
112
  except Exception as e:
113
+ error_msg = str(e)
114
+ print(f"❌ Error con {modelo}: {error_msg[:100]}")
115
+ continue
116
 
117
+ print("⚠️ No se generó audio. Verifica límites de API o conexión.")
118
  return None, emocion_detectada, confianza
119
 
120
  # ============= ASISTENTE IA CONVERSACIONAL =============
 
196
  f.write(f"\nArchivo de audio: {audio_path if audio_path else 'No generado'}\n")
197
  f.write("=" * 60 + "\n")
198
 
199
+ if audio_path:
200
  print(f"✅ Audio generado correctamente: {audio_path}")
 
201
  else:
202
  print("⚠️ No se pudo generar el audio, pero la respuesta está disponible")
203
+
204
+ return respuesta, audio_path, transcripcion_path, emocion, confianza
 
 
 
205
 
206
  except Exception as e:
207
  print(f"❌ Error con {modelo}: {str(e)}")
 
1353
  return None, f"Error al generar PDF: {str(e)}"
1354
 
1355
  # ============= INTERFAZ GRADIO =============
1356
+ with gr.Blocks(title="Extractor de Facturas con IA Avanzada") as demo:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1357
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1358
  datos_json_state = gr.State()
1359
  csv_file_state = gr.State()
1360
  pdf_path_state = gr.State()
1361
  texto_state = gr.State()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1362
 
1363
+ gr.Markdown("""
1364
+ # 🤖 Extractor Inteligente de Facturas con IA
1365
+ ### Sistema avanzado de análisis documental con múltiples modelos de IA
1366
+ """)
1367
+
1368
+ gr.Markdown("---")
1369
+
1370
+ with gr.Tabs():
1371
+ # ============= TAB 1: EXTRACCIÓN AUTOMÁTICA =============
1372
+ with gr.Tab("📄 Extracción Automática"):
1373
  with gr.Row():
1374
  with gr.Column(scale=1):
1375
  gr.Markdown("### 📤 Subir Factura PDF")
1376
  pdf_input = gr.File(label="Seleccionar factura PDF", file_types=[".pdf"], type="filepath")
1377
  btn_extraer = gr.Button("🚀 Extraer Datos de la Factura", variant="primary", size="lg")
1378
 
1379
+ # Indicador de carga silencioso
1380
  loading_extraccion = gr.HTML(visible=False, value="""
1381
  <div style="text-align: center; padding: 20px;">
1382
  <div class="spinner"></div>
1383
+ <p style="margin-top: 10px; color: #2196F3; font-weight: bold;">
1384
  🔄 Procesando tu factura...
1385
  </p>
1386
  </div>
1387
  <style>
1388
  .spinner {
1389
  border: 3px solid #f3f3f3;
1390
+ border-top: 3px solid #2196F3;
1391
  border-radius: 50%;
1392
  width: 35px;
1393
  height: 35px;
 
1403
 
1404
  gr.Markdown("---")
1405
  gr.Markdown("### 📥 Descargar Archivos")
1406
+ csv_output = gr.File(label="📊 CSV Tabular (separado por comas)")
1407
 
1408
  gr.Markdown("---")
1409
  gr.Markdown("### 🎨 Rediseñar PDF")
 
1422
  gr.Markdown("---")
1423
  with gr.Tabs():
1424
  with gr.Tab("📋 Vista Previa CSV"):
1425
+ tabla_preview = gr.DataFrame(label="Datos estructurados en formato tabular", wrap=True)
1426
  with gr.Tab("📝 Texto Original"):
1427
  texto_extraido = gr.Textbox(label="Texto extraído del PDF", lines=18)
1428
  with gr.Tab("🔍 Información Técnica"):
1429
+ resumen_tecnico = gr.Markdown(label="Estructura de datos y metadatos")
1430
 
1431
+ # ============= TAB 2: ASISTENTE IA CON VOZ Y AVATAR =============
1432
+ # ============= TAB 2: ASISTENTE IA CON ANÁLISIS EMOCIONAL =============
1433
+ with gr.Tab("🤖 Asistente IA con Análisis Emocional"):
1434
+ gr.Markdown("""
1435
+ # 💬 Asistente Virtual con Análisis de Emociones
1436
+ ### Pregúntale cualquier cosa sobre tu factura y recibe respuestas con análisis emocional
1437
+ """)
1438
 
1439
  with gr.Row():
1440
  with gr.Column(scale=1):
 
1447
 
1448
  btn_consulta_ia = gr.Button("🎤 Consultar Asistente IA", variant="primary", size="lg")
1449
 
1450
+ # Indicador de carga
1451
  loading_ia = gr.HTML(visible=False, value="""
1452
  <div style="text-align: center; padding: 20px;">
1453
  <div class="spinner-ia"></div>
 
1475
  - ¿Qué es la base imponible?
1476
  - ¿Cuándo debo pagar esta factura?
1477
  - ¿Hay algún descuento aplicado?
1478
+ - ¿Quién emitió esta factura?
1479
  """)
1480
 
1481
+ # Indicador de emoción
1482
  emocion_detectada = gr.Markdown(value="", visible=True)
1483
 
1484
  with gr.Column(scale=2):
1485
+ gr.Markdown("### 🤖 Avatar Virtual del Asistente")
1486
 
1487
+ # Avatar robótico visual
1488
  avatar_html = gr.HTML(value="""
1489
+ <div style="text-align: center; padding: 30px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 20px; margin-bottom: 20px;">
1490
+ <div class="robot-avatar" style="margin: 0 auto;">
1491
  <svg width="120" height="120" viewBox="0 0 120 120">
1492
+ <!-- Cabeza del robot -->
1493
  <rect x="30" y="35" width="60" height="50" rx="10" fill="#ffffff" stroke="#667eea" stroke-width="3"/>
1494
+ <!-- Antena -->
1495
  <line x1="60" y1="35" x2="60" y2="20" stroke="#667eea" stroke-width="3"/>
1496
  <circle cx="60" cy="15" r="5" fill="#764ba2"/>
1497
+ <!-- Ojos -->
1498
  <circle cx="45" cy="55" r="6" fill="#667eea" class="eye-blink"/>
1499
  <circle cx="75" cy="55" r="6" fill="#667eea" class="eye-blink"/>
1500
+ <!-- Boca -->
1501
  <rect x="40" y="70" width="40" height="8" rx="4" fill="#667eea"/>
1502
  </svg>
1503
  </div>
 
1506
  </p>
1507
  </div>
1508
  <style>
1509
+ .eye-blink {
1510
+ animation: blink 3s infinite;
1511
+ }
1512
  @keyframes blink {
1513
  0%, 49%, 51%, 100% { opacity: 1; }
1514
  50% { opacity: 0.3; }
 
1517
  """)
1518
 
1519
  gr.Markdown("### 📝 Respuesta del Asistente")
1520
+ resultado_ia = gr.Markdown(
1521
+ value="*Haz una pregunta y el asistente te responderá aquí...*"
1522
+ )
1523
 
1524
  gr.Markdown("---")
1525
+ gr.Markdown("### 🔊 Audio Generado y Transcripción")
1526
 
1527
  with gr.Row():
1528
+ with gr.Column():
1529
+ audio_respuesta = gr.Audio(
1530
+ label="🎧 Reproducir respuesta en audio",
1531
+ type="filepath",
1532
+ visible=True,
1533
+ autoplay=False
1534
+ )
1535
+ with gr.Column():
1536
+ transcripcion_output = gr.File(
1537
+ label="📄 Descargar Transcripción (TXT)"
1538
+ )
1539
+
1540
+ gr.Markdown("""
1541
+ 💡 **Características mejoradas:**
1542
+ - 🎭 Análisis emocional del texto
1543
+ - 🎤 Voz sintetizada de alta calidad
1544
+ - 📝 Transcripción descargable en TXT
1545
+ - 🤖 Avatar interactivo que responde visualmente
1546
+ - 📊 Indicador de emoción detectada
1547
+ """)
1548
 
1549
+ # ============= TAB 3: HERRAMIENTAS IA AVANZADAS =============
1550
+ with gr.Tab("🔬 Análisis Avanzado IA"):
1551
+ gr.Markdown("""
1552
+ # 🎯 Suite Completa de Herramientas IA
1553
+ ### 12 herramientas profesionales de análisis automático
1554
+ """)
1555
 
1556
  with gr.Tabs():
1557
+ # Sub-tab 1: Análisis Financiero
1558
  with gr.Tab("💰 Análisis Financiero"):
1559
  with gr.Row():
1560
  with gr.Column():
 
1562
  btn_sentimiento = gr.Button("🔍 Analizar Riesgos", variant="primary")
1563
  resultado_sentimiento = gr.Markdown()
1564
 
1565
+ gr.Markdown("---")
1566
  gr.Markdown("### 💰 Gastos Deducibles")
1567
+ btn_deducibles = gr.Button("💸 Calcular Deducciones", variant="primary")
1568
  resultado_deducibles = gr.Markdown()
1569
 
1570
+ gr.Markdown("---")
1571
  gr.Markdown("### 💵 Impacto Presupuestario")
1572
+ btn_impacto = gr.Button("📊 Calcular Impacto", variant="primary")
1573
  resultado_impacto = gr.Markdown()
1574
 
1575
  with gr.Column():
 
1577
  btn_prediccion = gr.Button("🔮 Fecha Óptima", variant="primary")
1578
  resultado_prediccion = gr.Markdown()
1579
 
1580
+ gr.Markdown("---")
1581
  gr.Markdown("### 💡 Sugerencias IA")
1582
+ btn_sugerencias = gr.Button("✨ Generar Recomendaciones", variant="primary")
1583
  resultado_sugerencias = gr.Markdown()
1584
 
1585
+ gr.Markdown("---")
1586
  gr.Markdown("### 📁 Categorización")
1587
+ btn_categoria = gr.Button("🏷️ Clasificar Gasto", variant="primary")
1588
  resultado_categoria = gr.Markdown()
1589
 
1590
+ # Sub-tab 2: Seguridad y Validación
1591
+ with gr.Tab("🛡️ Seguridad y Validación"):
1592
  with gr.Row():
1593
  with gr.Column():
1594
  gr.Markdown("### 🚨 Detector de Fraude")
1595
+ btn_fraude = gr.Button("🛡️ Detectar Irregularidades", variant="primary")
1596
  resultado_fraude = gr.Markdown()
1597
 
1598
+ gr.Markdown("---")
1599
+ gr.Markdown("### 🔍 Detector de Duplicados")
1600
+ btn_duplicados = gr.Button("🔄 Buscar Duplicados", variant="primary")
1601
  resultado_duplicados = gr.Markdown()
1602
 
1603
+ gr.Markdown("---")
1604
  gr.Markdown("### ✅ Validador Fiscal")
1605
+ btn_validador = gr.Button("📋 Validar Datos Fiscales", variant="primary")
1606
  resultado_validador = gr.Markdown()
1607
 
1608
  with gr.Column():
1609
+ gr.Markdown("### 📝 Condiciones de Pago")
1610
+ btn_condiciones = gr.Button("📄 Analizar Condiciones", variant="primary")
1611
  resultado_condiciones = gr.Markdown()
1612
 
1613
+ gr.Markdown("---")
1614
+ gr.Markdown("### 🔔 Recordatorios de Pago")
1615
+ btn_recordatorios = gr.Button("⏰ Generar Recordatorios", variant="primary")
1616
  resultado_recordatorios = gr.Markdown()
1617
 
1618
+ gr.Markdown("---")
1619
  gr.Markdown("### 📊 Resumen Ejecutivo")
1620
+ btn_ejecutivo = gr.Button("📈 Dashboard Gerencial", variant="primary")
1621
  resultado_ejecutivo = gr.Markdown()
1622
 
1623
+ # Sub-tab 3: Comparación y Mercado
1624
+ with gr.Tab("📈 Análisis de Mercado"):
1625
+ gr.Markdown("### 💲 Comparador de Precios con Mercado")
1626
+ btn_mercado = gr.Button("🏪 Comparar con Mercado", variant="primary", size="lg")
1627
  resultado_mercado = gr.Markdown()
1628
+
1629
+ gr.Markdown("""
1630
+ ---
1631
+ **Esta herramienta analiza:**
1632
+ - ✅ Precios competitivos vs mercado
1633
+ - ✅ Productos con precios elevados
1634
+ - ✅ Ahorro potencial estimado
1635
+ - ✅ Recomendaciones de negociación
1636
+ """)
1637
 
1638
+ # ============= TAB 4: TRADUCCIÓN MULTIIDIOMA CON TABLA =============
1639
+ with gr.Tab("🌍 Traducción Internacional"):
1640
+ gr.Markdown("""
1641
+ # 🌐 Traductor Profesional de Facturas
1642
+ ### Traduce tu factura a 5 idiomas con vista tabular y exporta a CSV
1643
+ """)
1644
 
1645
  with gr.Row():
1646
  with gr.Column():
1647
+ gr.Markdown("### Seleccionar Idioma")
1648
  idioma_selector = gr.Dropdown(
1649
  choices=["Inglés", "Francés", "Alemán", "Italiano", "Portugués"],
1650
  value="Inglés",
1651
  label="🗣️ Idioma de destino"
1652
  )
1653
  btn_traducir = gr.Button("🌐 Traducir Factura", variant="primary", size="lg")
1654
+
1655
+ gr.Markdown("---")
1656
+ gr.Markdown("### Exportar Traducción")
1657
+ csv_traduccion_output = gr.File(label="📊 Descargar CSV Tabular Traducido")
1658
 
1659
  with gr.Column():
1660
  gr.Markdown("### 📊 Vista Tabular Traducida")
1661
+ tabla_traduccion = gr.DataFrame(
1662
+ label="Factura traducida en formato tabular",
1663
+ wrap=True
1664
+ )
1665
+
1666
+ gr.Markdown("---")
1667
  gr.Markdown("### 📝 Texto Traducido")
1668
+ resultado_traduccion = gr.Textbox(
1669
+ label="Resumen en texto",
1670
+ lines=10,
1671
+ placeholder="La traducción aparecerá aquí..."
1672
+ )
1673
+
1674
+ gr.Markdown("""
1675
+ 💡 **Características:**
1676
+ - 🌍 Traducción profesional automática
1677
+ - 📊 Vista tabular igual que la factura original
1678
+ - 📥 Exporta a CSV con el mismo formato
1679
+ - Mantiene la estructura original
1680
+ """)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1681
 
1682
+ gr.Markdown("---")
1683
+ gr.Markdown("""
1684
+ ### 📚 Guía Completa de Uso
1685
+
1686
+ 1. **📄 Extracción Automática:** Sube tu PDF y obtén datos estructurados en CSV tabular (separado por comas)
1687
+ 2. **🤖 Asistente IA con Avatar:** Pregunta cualquier duda y escucha la respuesta con voz robótica
1688
+ 3. **🔬 Análisis Avanzado:** 12 herramientas profesionales de análisis
1689
+ 4. **🌍 Traducción:** Traduce y exporta a CSV en 5 idiomas
1690
+
1691
+ ---
1692
+
1693
+ ### 🎯 12 Funcionalidades IA Profesionales:
1694
+
1695
+ **💰 Análisis Financiero:**
1696
+ - 🔍 **Análisis de Riesgos:** Detecta urgencias y alertas
1697
+ - 💸 **Gastos Deducibles:** Calcula deducciones fiscales
1698
+ - 📊 **Impacto Presupuestario:** Analiza impacto en presupuesto
1699
+ - 🔮 **Predicción de Pagos:** Fecha óptima de pago
1700
+ - **Sugerencias IA:** Recomendaciones personalizadas
1701
+ - 🏷️ **Categorización:** Clasifica gastos automáticamente
1702
+
1703
+ **🛡️ Seguridad y Validación:**
1704
+ - 🚨 **Detector de Fraude:** Identifica irregularidades
1705
+ - 🔄 **Detector de Duplicados:** Busca facturas duplicadas
1706
+ - ✅ **Validador Fiscal:** Verifica datos fiscales
1707
+ - 📄 **Análisis de Condiciones:** Evalúa términos de pago
1708
+ - ⏰ **Recordatorios:** Plan de recordatorios inteligente
1709
+ - 📈 **Dashboard Ejecutivo:** Resumen para gerencia
1710
+
1711
+ **📈 Análisis de Mercado:**
1712
+ - 💲 **Comparador de Precios:** Compara con mercado
1713
+
1714
+ **📊 Formatos Mejorados:**
1715
+ - CSV tabular con comas como separador
1716
+ - Estructura clara: Sección, Campo, Valor, Tipo
1717
+ - Compatible con Excel, Google Sheets, etc.
1718
+
1719
+ **🌐 Traducción Avanzada:**
1720
+ - Traduce a 5 idiomas
1721
+ - Vista tabular traducida
1722
+ - Exporta traducciones a CSV
1723
+ - Mantiene formato profesional
1724
+
1725
+ 💡 **¡Empieza subiendo tu factura en la primera pestaña!**
1726
  """)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1727
 
1728
+ # ============= CONECTAR EVENTOS =============
 
 
 
 
1729
 
 
 
 
 
 
1730
  # Extracción automática
1731
  def procesar_con_loading(pdf_file):
1732
  if pdf_file is None:
 
1758
 
1759
  yield ("🔄 El asistente está analizando tu pregunta...", None, None, "", gr.update(visible=True))
1760
  time.sleep(0.3)
1761
+ respuesta, audio, transcripcion, emocion, confianza = asistente_ia_factura(texto, pregunta)
1762
 
1763
+ # Mapeo de emociones a emojis y colores
1764
+ emotion_map = {
1765
+ "joy": ("😊", "#4CAF50", "Alegría"),
1766
+ "excitement": ("🎉", "#FF9800", "Emoción"),
1767
+ "anger": ("😠", "#F44336", "Enfado"),
1768
+ "sadness": ("😢", "#2196F3", "Tristeza"),
1769
+ "fear": ("😰", "#9C27B0", "Miedo"),
1770
+ "surprise": ("😮", "#FF5722", "Sorpresa"),
1771
+ "neutral": ("😐", "#607D8B", "Neutral")
1772
+ }
1773
+
1774
+ emoji, color, nombre = emotion_map.get(emocion, ("😐", "#607D8B", "Neutral"))
1775
+
1776
+ emocion_info = f"""
 
 
1777
  ### 🎭 Análisis Emocional
1778
 
1779
  <div style="background: linear-gradient(135deg, {color}22 0%, {color}44 100%); padding: 15px; border-radius: 10px; border-left: 4px solid {color};">
 
1785
  </p>
1786
  </div>
1787
  """
1788
+
1789
+ yield (respuesta, audio, transcripcion, emocion_info, gr.update(visible=False))
 
 
 
 
 
 
 
 
 
 
 
 
 
1790
 
1791
  btn_consulta_ia.click(
1792
  fn=consultar_ia_con_loading,