CVision-assitant commited on
Commit
b45593b
·
verified ·
1 Parent(s): c8925e8

Update modules/interface.py

Browse files
Files changed (1) hide show
  1. modules/interface.py +210 -161
modules/interface.py CHANGED
@@ -1,6 +1,6 @@
1
  import gradio as gr
2
  import re
3
- import time
4
  from modules.listen import transcribe_audio_file
5
  from modules.talker.talk import speak
6
 
@@ -12,52 +12,30 @@ from modules.utils.chat_utils import (
12
  document_chat
13
  )
14
 
15
- # ---------------- CONFIGURACIÓN Y ESTILOS ----------------
16
  PLACEHOLDER = "Escribe lo que necesites..."
17
 
18
- CUSTOM_CSS = """
19
- body { background: #0a0f1e; color: white; }
20
- .chatbot { height: 500px !important; overflow-y: auto; }
21
- /* Burbujas estilo WhatsApp */
22
- .chatbot .message.user {
23
- background-color: #005a53 !important;
24
- border-radius: 15px 15px 0 15px !important;
25
- margin-left: 20% !important;
26
- }
27
- .chatbot .message.bot {
28
- background-color: #1e2736 !important;
29
- border-radius: 15px 15px 15px 0 !important;
30
- border: 1px solid rgba(0, 210, 255, 0.2);
31
- margin-right: 20% !important;
32
- }
33
- .nebula-container-outer { display: flex; justify-content: center; width: 100%; margin-bottom: 20px; }
34
- .nebula-wrapper {
35
- background: radial-gradient(circle, rgba(0,210,255,0.05) 0%, rgba(0,0,0,0.6) 100%);
36
- border-radius: 50%; border: 2px solid rgba(0, 210, 255, 0.4);
37
- width: 300px; height: 300px; overflow: hidden; position: relative;
38
- }
39
- #nebulaCanvas { width: 100%; height: 100%; display: block; }
40
- """
41
-
42
- # ---------------- JAVASCRIPT: NÉBULA ----------------
43
- NEBULA_JS = """
44
  function createCircularNebula() {
45
  const canvas = document.getElementById('nebulaCanvas');
46
  if (!canvas) return;
47
  const ctx = canvas.getContext('2d');
48
  let particles = [];
 
49
  const NUM_PARTICLES = 110;
50
  const CONNECTION_DISTANCE = 100;
51
 
52
  function resize() {
53
- canvas.width = canvas.offsetWidth; canvas.height = canvas.offsetHeight;
 
54
  initParticles();
55
  }
56
  window.addEventListener('resize', resize);
57
-
58
  class Particle {
59
  constructor(angle, radius) {
60
- this.angle = angle; this.baseRadius = radius;
 
61
  this.speed = (Math.random() - 0.5) * 0.004;
62
  this.oscillation = Math.random() * 12;
63
  this.time = Math.random() * 100;
@@ -71,12 +49,17 @@ function createCircularNebula() {
71
  this.angle += this.speed;
72
  }
73
  draw() {
74
- ctx.shadowBlur = 8; ctx.shadowColor = 'rgba(0, 210, 255, 0.8)';
 
75
  ctx.fillStyle = 'rgba(0, 210, 255, 0.9)';
76
  ctx.beginPath();
77
- const s = 2.2; ctx.moveTo(this.x, this.y - s);
78
- ctx.lineTo(this.x + s, this.y + s); ctx.lineTo(this.x - s, this.y + s);
79
- ctx.closePath(); ctx.fill(); ctx.shadowBlur = 0;
 
 
 
 
80
  }
81
  }
82
 
@@ -90,153 +73,219 @@ function createCircularNebula() {
90
  }
91
  }
92
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
93
  function animate() {
94
  ctx.clearRect(0, 0, canvas.width, canvas.height);
95
- ctx.strokeStyle = 'rgba(0, 150, 255, 0.12)'; ctx.lineWidth = 0.7;
 
 
 
96
  for (let i = 0; i < particles.length; i++) {
97
- const p = particles[i]; p.update(); p.draw();
 
 
98
  for (let j = i + 1; j < particles.length; j++) {
99
  const d = Math.hypot(p.x - particles[j].x, p.y - particles[j].y);
100
  if (d < CONNECTION_DISTANCE) {
101
- ctx.beginPath(); ctx.moveTo(p.x, p.y);
102
- ctx.lineTo(particles[j].x, particles[j].y); ctx.stroke();
 
 
103
  }
104
  }
105
  }
 
 
106
  requestAnimationFrame(animate);
107
  }
108
- resize(); animate();
 
 
109
  }
110
  setTimeout(createCircularNebula, 1000);
 
111
  """
112
 
113
- # ---------------- LÓGICA DE LIMPIEZA Y PROCESAMIENTO ----------------
114
- def clean_final_text(text):
115
- """Limpia el texto de metadatos, rutas de archivos y basura de código."""
116
- if not text: return ""
117
  text = str(text)
118
- # Eliminar rutas de archivos .mp3
119
- text = re.sub(r"'/tmp/.*?\.mp3'", "", text)
120
- text = re.sub(r"\(.*?\.mp3\)", "", text)
121
- # Limpiar sintaxis de generadores y listas
122
  text = re.sub(r"<generator object.*?>", "", text)
123
- text = text.replace("(['", "").replace("'])", "").replace("['", "").replace("']", "")
124
- # Eliminar caracteres especiales sobrantes
125
- text = text.strip("[]()', ")
126
- return text
127
 
128
- def get_assistant_response(chat_history, user_input, chat_func):
129
- """Extrae el contenido real de un generador o lista de LangChain."""
130
  try:
131
- response_gen = chat_func(chat_history, user_input)
132
- final_text = ""
133
-
134
- # Si es un generador, iteramos hasta el final para obtener el texto completo
135
- if hasattr(response_gen, "__iter__") and not isinstance(response_gen, (list, tuple)):
136
- for part in response_gen:
137
- if isinstance(part, list) and len(part) > 0:
138
- # Gradio Chatbot suele devolver listas de pares [user, bot]
139
- final_text = part[-1][1]
140
- else:
141
- final_text = str(part)
142
- else:
143
- # Si ya es una lista o string
144
- if isinstance(response_gen, list) and len(response_gen) > 0:
145
- final_text = response_gen[-1][1] if isinstance(response_gen[-1], (list, tuple)) else response_gen[-1]
146
- else:
147
- final_text = str(response_gen)
148
-
149
- return clean_final_text(final_text)
150
  except Exception as e:
151
- return f"Error de conexión: {str(e)}"
152
-
153
- # ---------------- FUNCIONES DE LA INTERFAZ ----------------
154
- def chat_wrapper(chat_history, user_text, mode, doc=None):
155
- if not user_text: return chat_history, ""
156
-
157
- # Seleccionar función según pestaña
158
- modes = {
159
- "general": regular_chat,
160
- "web": browser_chat,
161
- "productividad": productivity_chat,
162
- "cientifico": scientist_chat,
163
- "pdf": lambda h, t: document_chat(h, t, doc)
164
- }
165
-
166
- selected_func = modes.get(mode, regular_chat)
167
- bot_reply = get_assistant_response(chat_history, user_text, selected_func)
168
-
169
- # Actualizar historial para burbujas tipo WhatsApp
170
- chat_history.append((user_text, bot_reply))
171
- return chat_history, ""
172
-
173
- def voice_wrapper(chat_history, audio_path, mode, doc=None):
174
- if not audio_path: return chat_history, None, ""
175
-
176
- # Transcripción
177
- user_text = transcribe_audio_file(audio_path)
178
- if not user_text: return chat_history, None, ""
179
-
180
- # Obtener respuesta
181
- new_history, _ = chat_wrapper(chat_history, user_text, mode, doc)
182
- bot_reply = new_history[-1][1]
183
-
184
- # Generar audio de salida limpio
185
- clean_for_speech = bot_reply.replace("*", "").replace("#", "")
186
- out_audio = speak(clean_for_speech[:300])
187
-
188
- return new_history, out_audio, user_text
189
-
190
- # ---------------- INTERFAZ GRADIO ----------------
191
- with gr.Blocks(css=CUSTOM_CSS, head=f"<script>{NEBULA_JS}</script>") as assistant:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
192
  with gr.Row():
193
- with gr.Column(scale=1): gr.Image("assets/CVision logo.png", width=100, show_label=False)
194
- with gr.Column(scale=4):
195
- gr.Markdown("# CVision Intelligence")
196
- gr.Markdown("### Sistema Multimodal Corregido v2.0")
197
-
198
- gr.HTML('<div class="nebula-container-outer"><div class="nebula-wrapper"><canvas id="nebulaCanvas"></canvas></div></div>')
199
-
200
- def build_tab(mode_name):
201
- chatbot = gr.Chatbot(height=450, bubble_full_width=False)
202
- voice_output = gr.Audio(visible=False, autoplay=True)
203
-
204
- with gr.Row():
205
- txt_msg = gr.Textbox(placeholder=PLACEHOLDER, scale=7, show_label=False)
206
- btn_send = gr.Button("Enviar", variant="primary", scale=1)
207
-
208
- with gr.Row():
209
- audio_input = gr.Audio(sources=["microphone"], type="filepath", scale=4)
210
- btn_voice = gr.Button("🎤 Procesar Voz", scale=2)
211
- btn_clear = gr.Button("Limpiar", scale=2)
212
-
213
- # Acciones
214
- btn_send.click(
215
- chat_wrapper, [chatbot, txt_msg, gr.State(mode_name)], [chatbot, txt_msg]
216
- )
217
- txt_msg.submit(
218
- chat_wrapper, [chatbot, txt_msg, gr.State(mode_name)], [chatbot, txt_msg]
219
- )
220
- btn_voice.click(
221
- voice_wrapper, [chatbot, audio_input, gr.State(mode_name)], [chatbot, voice_output, txt_msg]
222
- )
223
- btn_clear.click(lambda: ([], None, ""), None, [chatbot, voice_output, txt_msg])
224
-
225
- with gr.Tab("💬 General"): build_tab("general")
226
- with gr.Tab("🌐 Web"): build_tab("web")
227
- with gr.Tab("📈 Productividad"): build_tab("productividad")
228
- with gr.Tab("🔬 Científico"): build_tab("cientifico")
229
- with gr.Tab("📄 Chat PDF"):
230
- chatbot_pdf = gr.Chatbot(height=450, bubble_full_width=False)
231
- voice_pdf = gr.Audio(visible=False, autoplay=True)
232
- file_pdf = gr.File(label="Sube tu documento PDF", file_types=[".pdf"])
233
- txt_pdf = gr.Textbox(placeholder=PLACEHOLDER)
234
- with gr.Row():
235
- b1 = gr.Button("Enviar", variant="primary"); b2 = gr.Button("🎤 Voz"); b3 = gr.Button("Limpiar")
236
-
237
- b1.click(chat_wrapper, [chatbot_pdf, txt_pdf, gr.State("pdf"), file_pdf], [chatbot_pdf, txt_pdf])
238
- b2.click(voice_wrapper, [chatbot_pdf, gr.Audio(sources=["microphone"], type="filepath"), gr.State("pdf"), file_pdf], [chatbot_pdf, voice_pdf, txt_pdf])
239
- b3.click(lambda: ([], None, ""), None, [chatbot_pdf, voice_pdf, txt_pdf])
240
 
241
  if __name__ == "__main__":
242
  assistant.launch()
 
1
  import gradio as gr
2
  import re
3
+
4
  from modules.listen import transcribe_audio_file
5
  from modules.talker.talk import speak
6
 
 
12
  document_chat
13
  )
14
 
 
15
  PLACEHOLDER = "Escribe lo que necesites..."
16
 
17
+ # ---------------- NEBULA (TU ORIGINAL COMPLETA) ----------------
18
+ NEBULA_JS = """<script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  function createCircularNebula() {
20
  const canvas = document.getElementById('nebulaCanvas');
21
  if (!canvas) return;
22
  const ctx = canvas.getContext('2d');
23
  let particles = [];
24
+
25
  const NUM_PARTICLES = 110;
26
  const CONNECTION_DISTANCE = 100;
27
 
28
  function resize() {
29
+ canvas.width = canvas.offsetWidth;
30
+ canvas.height = canvas.offsetHeight;
31
  initParticles();
32
  }
33
  window.addEventListener('resize', resize);
34
+
35
  class Particle {
36
  constructor(angle, radius) {
37
+ this.angle = angle;
38
+ this.baseRadius = radius;
39
  this.speed = (Math.random() - 0.5) * 0.004;
40
  this.oscillation = Math.random() * 12;
41
  this.time = Math.random() * 100;
 
49
  this.angle += this.speed;
50
  }
51
  draw() {
52
+ ctx.shadowBlur = 8;
53
+ ctx.shadowColor = 'rgba(0, 210, 255, 0.8)';
54
  ctx.fillStyle = 'rgba(0, 210, 255, 0.9)';
55
  ctx.beginPath();
56
+ const s = 2.2;
57
+ ctx.moveTo(this.x, this.y - s);
58
+ ctx.lineTo(this.x + s, this.y + s);
59
+ ctx.lineTo(this.x - s, this.y + s);
60
+ ctx.closePath();
61
+ ctx.fill();
62
+ ctx.shadowBlur = 0;
63
  }
64
  }
65
 
 
73
  }
74
  }
75
 
76
+ function drawCenterLogo() {
77
+ const centerX = canvas.width / 2;
78
+ const centerY = canvas.height / 2;
79
+
80
+ const gradient = ctx.createRadialGradient(centerX, centerY, 0, centerX, centerY, 80);
81
+ gradient.addColorStop(0, 'rgba(0, 210, 255, 0.2)');
82
+ gradient.addColorStop(1, 'rgba(0, 210, 255, 0)');
83
+ ctx.fillStyle = gradient;
84
+ ctx.beginPath();
85
+ ctx.arc(centerX, centerY, 80, 0, Math.PI * 2);
86
+ ctx.fill();
87
+
88
+ ctx.font = "bold 32px 'Segoe UI'";
89
+ ctx.textAlign = "center";
90
+ ctx.textBaseline = "middle";
91
+ ctx.shadowBlur = 15;
92
+ ctx.shadowColor = 'rgba(0, 210, 255, 0.9)';
93
+
94
+ const textGradient = ctx.createLinearGradient(centerX - 50, 0, centerX + 50, 0);
95
+ textGradient.addColorStop(0, '#ffffff');
96
+ textGradient.addColorStop(1, '#00d2ff');
97
+
98
+ ctx.fillStyle = textGradient;
99
+ ctx.fillText("CVision", centerX, centerY);
100
+ ctx.shadowBlur = 0;
101
+ }
102
+
103
  function animate() {
104
  ctx.clearRect(0, 0, canvas.width, canvas.height);
105
+
106
+ ctx.strokeStyle = 'rgba(0, 150, 255, 0.12)';
107
+ ctx.lineWidth = 0.7;
108
+
109
  for (let i = 0; i < particles.length; i++) {
110
+ const p = particles[i];
111
+ p.update();
112
+ p.draw();
113
  for (let j = i + 1; j < particles.length; j++) {
114
  const d = Math.hypot(p.x - particles[j].x, p.y - particles[j].y);
115
  if (d < CONNECTION_DISTANCE) {
116
+ ctx.beginPath();
117
+ ctx.moveTo(p.x, p.y);
118
+ ctx.lineTo(particles[j].x, particles[j].y);
119
+ ctx.stroke();
120
  }
121
  }
122
  }
123
+
124
+ drawCenterLogo();
125
  requestAnimationFrame(animate);
126
  }
127
+
128
+ resize();
129
+ animate();
130
  }
131
  setTimeout(createCircularNebula, 1000);
132
+ </script>
133
  """
134
 
135
+ # ---------------- LIMPIEZA ----------------
136
+ def clean_text(text):
137
+ if not text:
138
+ return ""
139
  text = str(text)
 
 
 
 
140
  text = re.sub(r"<generator object.*?>", "", text)
141
+ text = re.sub(r"/tmp/.*?\.mp3", "", text)
142
+ text = re.sub(r"\[\[.*?\]\]", "", text)
143
+ return text.strip()
 
144
 
145
+ # 🔥 FIX PRINCIPAL
146
+ def extract_text(result):
147
  try:
148
+ if hasattr(result, "__iter__") and not isinstance(result, list):
149
+ for step in result:
150
+ result = step
151
+
152
+ if isinstance(result, list) and len(result) > 0:
153
+ last = result[-1]
154
+ if isinstance(last, (list, tuple)) and len(last) == 2:
155
+ return last[1]
156
+
157
+ return str(result)
158
+
 
 
 
 
 
 
 
 
159
  except Exception as e:
160
+ return f"Error: {e}"
161
+
162
+ # ---------------- CHAT TEXT ----------------
163
+ def handle_text(chat_history, user_text, fn):
164
+ if chat_history is None:
165
+ chat_history = []
166
+
167
+ if not user_text:
168
+ return chat_history
169
+
170
+ result = fn(chat_history, user_text)
171
+ response = clean_text(extract_text(result))
172
+
173
+ chat_history.append([user_text, response])
174
+ return chat_history
175
+
176
+ # ---------------- VOZ ----------------
177
+ def handle_voice(chat_history, audio, mode, file=None):
178
+ if chat_history is None:
179
+ chat_history = []
180
+
181
+ if audio is None:
182
+ return chat_history, None
183
+
184
+ text = transcribe_audio_file(audio)
185
+
186
+ if not text:
187
+ return chat_history, None
188
+
189
+ if mode == "web":
190
+ fn = browser_chat
191
+ elif mode == "productividad":
192
+ fn = productivity_chat
193
+ elif mode == "cientifico":
194
+ fn = scientist_chat
195
+ elif mode == "pdf":
196
+ fn = lambda h, t: document_chat(h, t, file)
197
+ else:
198
+ fn = regular_chat
199
+
200
+ result = fn(chat_history, text)
201
+ response = clean_text(extract_text(result))
202
+
203
+ chat_history.append([text, response])
204
+
205
+ audio_path = speak(response)
206
+
207
+ return chat_history, audio_path
208
+
209
+ # ---------------- STT ----------------
210
+ def speech_to_text(audio):
211
+ if audio is None:
212
+ return ""
213
+ try:
214
+ return transcribe_audio_file(audio)
215
+ except:
216
+ return ""
217
+
218
+ # ---------------- CSS ----------------
219
+ CUSTOM_CSS = """
220
+ body { background: #0a0f1e; color: white; }
221
+ .chatbot { height: 500px !important; }
222
+ .nebula-container-outer {
223
+ display: flex;
224
+ justify-content: center;
225
+ margin-bottom: 25px;
226
+ }
227
+ .nebula-wrapper {
228
+ width: 320px;
229
+ height: 320px;
230
+ border-radius: 50%;
231
+ border: 2px solid rgba(0,210,255,0.4);
232
+ overflow: hidden;
233
+ }
234
+ """
235
+
236
+ # ---------------- TAB ----------------
237
+ def create_tab(fn, mode):
238
+ chatbot = gr.Chatbot(height=500)
239
+ audio = gr.Audio(visible=False, autoplay=True)
240
+
241
+ text = gr.Textbox(placeholder=PLACEHOLDER)
242
+ mic = gr.Audio(sources=["microphone"], type="filepath")
243
+
244
  with gr.Row():
245
+ send = gr.Button("Enviar")
246
+ voice = gr.Button("🎤 Voz")
247
+ clear = gr.Button("Limpiar")
248
+
249
+ send.click(lambda h, t: handle_text(h, t, fn), [chatbot, text], chatbot)
250
+ voice.click(lambda h, a: handle_voice(h, a, mode), [chatbot, mic], [chatbot, audio])
251
+ clear.click(lambda: ([], None), outputs=[chatbot, audio])
252
+
253
+ mic.change(speech_to_text, mic, text)
254
+
255
+ # ---------------- APP ----------------
256
+ with gr.Blocks(css=CUSTOM_CSS, head=NEBULA_JS) as assistant:
257
+
258
+ gr.Markdown("# 🤖 Asistente CVision")
259
+
260
+ gr.HTML("""
261
+ <div class="nebula-container-outer">
262
+ <div class="nebula-wrapper">
263
+ <canvas id="nebulaCanvas"></canvas>
264
+ </div>
265
+ </div>
266
+ """)
267
+
268
+ with gr.Tab("💬 General"):
269
+ create_tab(regular_chat, "general")
270
+
271
+ with gr.Tab("🌐 Web"):
272
+ create_tab(browser_chat, "web")
273
+
274
+ with gr.Tab("📈 Productividad"):
275
+ create_tab(productivity_chat, "productividad")
276
+
277
+ with gr.Tab("🔬 Científico"):
278
+ create_tab(scientist_chat, "cientifico")
279
+
280
+ with gr.Tab("📄 PDF"):
281
+ chatbot = gr.Chatbot()
282
+ audio = gr.Audio(visible=False, autoplay=True)
283
+ file = gr.File()
284
+ text = gr.Textbox()
285
+
286
+ btn = gr.Button("Enviar")
287
+ btn.click(lambda h, t, f: handle_text(h, t, lambda hh, tt: document_chat(hh, tt, f)),
288
+ [chatbot, text, file], chatbot)
 
 
 
289
 
290
  if __name__ == "__main__":
291
  assistant.launch()