CVision-assitant commited on
Commit
a7a0f84
·
verified ·
1 Parent(s): b3771f2

Update modules/interface.py

Browse files
Files changed (1) hide show
  1. modules/interface.py +86 -177
modules/interface.py CHANGED
@@ -12,65 +12,47 @@ from modules.utils.chat_utils import (
12
 
13
  PLACEHOLDER = "Escribe lo que necesites..."
14
 
15
- // ---------------- JAVASCRIPT PARA LA NÉBULA/RED NEURONAL CIRCULAR AZUL FOTURISTA ----------------
16
- // Reemplaza la sección NEBULA_JS anterior con este nuevo código.
17
-
18
  NEBULA_JS = """
19
  function createCircularNebula() {
20
  const canvas = document.getElementById('nebulaCanvas');
21
  if (!canvas) return;
22
  const ctx = canvas.getContext('2d');
23
  let particles = [];
24
- let animationFrameId;
25
 
26
- // Parámetros de la forma circular
27
- const NUM_PARTICLES = 120; // Más partículas para un anillo más denso
28
- const INNER_RADIUS_PERCENT = 0.35; // Radio interior del anillo
29
- const OUTER_RADIUS_PERCENT = 0.50; // Radio exterior del anillo
30
- const CONNECTION_DISTANCE = 110;
31
 
32
  function resize() {
33
- // Asegurar que el canvas tenga un tamaño para la corona circular
34
  canvas.width = canvas.offsetWidth;
35
  canvas.height = canvas.offsetHeight;
36
- if (particles.length > 0) initParticles(); // Reinicializar si cambia el tamaño
37
  }
38
  window.addEventListener('resize', resize);
39
- resize();
40
-
41
  class Particle {
42
- constructor(angle, currentRadius) {
43
  this.angle = angle;
44
- this.baseRadius = currentRadius;
45
- this.angleSpeed = (Math.random() - 0.5) * 0.003; // Velocidad angular lenta
46
- this.radiusOscillation = Math.random() * 5 + 2; // Oscilación radial pequeña
47
- this.radiusFreq = Math.random() * 0.05 + 0.01;
48
  this.time = Math.random() * 100;
49
-
50
- // Posición inicial
51
- this.updatePosition();
52
  }
53
-
54
- updatePosition() {
55
- this.time += 1;
56
- // Un poco de movimiento orbital y oscilación radial
57
- const osc = Math.sin(this.time * this.radiusFreq) * this.radiusOscillation;
58
- const r = this.baseRadius + osc;
59
-
60
- this.x = canvas.width / 2 + r * Math.cos(this.angle);
61
- this.y = canvas.height / 2 + r * Math.sin(this.angle);
62
-
63
- this.angle += this.angleSpeed;
64
  }
65
-
66
  draw() {
67
- ctx.fillStyle = 'rgba(0, 210, 255, 0.9)'; // Azul Neón Intenso
68
  ctx.beginPath();
69
- // Dibujar un pequeño triángulo
70
- const size = 2.5;
71
- ctx.moveTo(this.x, this.y - size);
72
- ctx.lineTo(this.x + size, this.y + size);
73
- ctx.lineTo(this.x - size, this.y + size);
74
  ctx.closePath();
75
  ctx.fill();
76
  }
@@ -78,192 +60,134 @@ function createCircularNebula() {
78
 
79
  function initParticles() {
80
  particles = [];
81
- const canvasMinDim = Math.min(canvas.width, canvas.height);
82
- const innerRadius = canvasMinDim * INNER_RADIUS_PERCENT;
83
- const outerRadius = canvasMinDim * OUTER_RADIUS_PERCENT;
84
-
85
  for (let i = 0; i < NUM_PARTICLES; i++) {
86
- const angle = (i / NUM_PARTICLES) * Math.PI * 2;
87
- const currentRadius = innerRadius + Math.random() * (outerRadius - innerRadius);
88
- particles.push(new Particle(angle, currentRadius));
89
  }
90
  }
91
 
92
- initParticles();
93
-
94
  function animate() {
95
  ctx.clearRect(0, 0, canvas.width, canvas.height);
96
- ctx.strokeStyle = 'rgba(50, 150, 255, 0.2)'; // Enlaces azules más sutiles
97
  ctx.lineWidth = 0.8;
98
-
99
  particles.forEach((p, i) => {
100
- p.updatePosition();
101
  p.draw();
102
-
103
  for (let j = i + 1; j < particles.length; j++) {
104
- const p2 = particles[j];
105
- const dist = Math.hypot(p.x - p2.x, p.y - p2.y);
106
- if (dist < CONNECTION_DISTANCE) {
107
  ctx.beginPath();
108
  ctx.moveTo(p.x, p.y);
109
- ctx.lineTo(p2.x, p2.y);
110
  ctx.stroke();
111
  }
112
  }
113
  });
114
- animationFrameId = requestAnimationFrame(animate);
115
- }
116
-
117
- // Cancelar la animación anterior si existe antes de empezar una nueva
118
- if (window.nebulaAnimationFrameId) {
119
- cancelAnimationFrame(window.nebulaAnimationFrameId);
120
  }
121
- window.nebulaAnimationFrameId = animationFrameId;
122
-
123
  animate();
124
  }
125
-
126
- // Ejecutar con un pequeño retraso para asegurar que Gradio haya montado el canvas
127
  setTimeout(createCircularNebula, 1000);
128
  """
129
 
130
- # ---------------- VOZ A TEXTO ----------------
131
  def speech_to_text(audio):
132
- if audio is None:
133
- return ""
134
- try:
135
- return transcribe_audio_file(audio)
136
  except Exception as e:
137
  print("Error STT:", e)
138
  return ""
139
 
140
- # ---------------- RESUMEN PARA VOZ ----------------
141
  def summarize_for_voice(text, max_chars=250):
142
- if not text:
143
- return ""
144
  text = str(text).strip()
145
- if len(text) <= max_chars:
146
- return text
147
  summary = text[:max_chars]
148
- if "." in summary:
149
- summary = summary.rsplit(".", 1)[0] + "."
150
- else:
151
- summary = summary.rsplit(" ", 1)[0]
152
  return summary + " ..."
153
 
154
- # ---------------- PROCESAR GENERATOR ----------------
155
  def process_chat_result(result):
156
  final_response = []
157
  try:
158
  if hasattr(result, "__iter__") and not isinstance(result, list):
159
- for update in result:
160
- final_response = update
161
- else:
162
- final_response = result
163
- except Exception as e:
164
- print("Error generator:", e)
165
- if final_response is None:
166
- final_response = []
167
- if not isinstance(final_response, list):
168
- final_response = [["", str(final_response)]]
169
  return final_response
170
 
171
- # ---------------- NORMALIZAR CHAT ----------------
172
  def normalize_chat_format(chat_data):
173
- if not isinstance(chat_data, list):
174
- return [["", str(chat_data)]]
175
  normalized = []
176
  for item in chat_data:
177
- if isinstance(item, list) and len(item) == 2:
178
- normalized.append(item)
179
- elif isinstance(item, tuple) and len(item) == 2:
180
- normalized.append([item[0], item[1]])
181
- else:
182
- normalized.append(["", str(item)])
183
  return normalized
184
 
185
- # ---------------- ENVÍO TEXTO ----------------
186
  def handle_text_chat(chat_history, user_text, chat_function):
187
- if chat_history is None:
188
- chat_history = []
189
  result = chat_function(chat_history, user_text)
190
  final_response = process_chat_result(result)
191
  return normalize_chat_format(final_response)
192
 
193
- # ---------------- FUNCIÓN CENTRAL VOZ ----------------
194
  def voice_chat(chat_history, audio, mode, uploaded_document=None):
195
- if chat_history is None:
196
- chat_history = []
197
- if audio is None:
198
- return chat_history, None
199
- try:
200
- user_text = transcribe_audio_file(audio)
201
- except Exception as e:
202
- print("Error audio:", e)
203
- return chat_history + [["", "❌ Error al transcribir audio"]], None
204
- if not user_text:
205
- return chat_history + [["", "⚠️ No se pudo entender el audio"]], None
206
-
207
- if mode == "productividad":
208
- result = productivity_chat(chat_history, user_text)
209
- elif mode == "web":
210
- result = browser_chat(chat_history, user_text)
211
- elif mode == "cientifico":
212
- result = scientist_chat(chat_history, user_text)
213
- elif mode == "pdf":
214
- result = document_chat(chat_history, user_text, uploaded_document)
215
- else:
216
- result = regular_chat(chat_history, user_text)
217
 
218
- final_response = process_chat_result(result)
219
- final_response = normalize_chat_format(final_response)
220
  audio_path = None
221
  try:
222
- last_msg = final_response[-1][1]
223
- voice_text = summarize_for_voice(last_msg)
224
  audio_path = speak(voice_text)
225
- except Exception as e:
226
- print("Error TTS:", e)
227
  return final_response, audio_path
228
 
229
- # ---------------- CSS ----------------
230
  CUSTOM_CSS = """
231
- body {
232
- background: #0a0f1e;
233
- color: white;
234
- }
235
- .chatbot {
236
- height: 500px !important;
 
237
  }
238
  .nebula-wrapper {
239
  background: rgba(0, 0, 0, 0.4);
240
- border-radius: 15px;
241
- border: 1px solid rgba(0, 255, 150, 0.2);
242
  overflow: hidden;
 
 
243
  position: relative;
244
- width: 100%;
245
- height: 250px;
246
- margin-bottom: 20px;
247
- }
248
- #nebulaCanvas {
249
- display: block;
250
- width: 100%;
251
- height: 100%;
252
  }
 
253
  """
254
 
255
- # ---------------- TAB REUTILIZABLE ----------------
256
  def create_chat_tab(chat_function, mode_name):
257
  chatbot = gr.Chatbot(height=500)
258
  audio_player = gr.Audio(label="🔊 Respuesta por voz", autoplay=True)
259
  text_input = gr.Textbox(placeholder=PLACEHOLDER)
260
  mic = gr.Audio(sources=["microphone"], type="filepath")
261
-
262
  with gr.Row():
263
  text_button = gr.Button("Enviar")
264
  voice_button = gr.Button("🎤 Voz")
265
  clear_button = gr.Button("Limpiar")
266
-
267
  text_button.click(lambda h, t: handle_text_chat(h, t, chat_function), inputs=[chatbot, text_input], outputs=chatbot)
268
  voice_button.click(lambda h, a: voice_chat(h, a, mode_name), inputs=[chatbot, mic], outputs=[chatbot, audio_player])
269
  clear_button.click(lambda: ([], None), outputs=[chatbot, audio_player])
@@ -279,42 +203,27 @@ with gr.Blocks(css=CUSTOM_CSS, head=f"<script>{NEBULA_JS}</script>") as assistan
279
  gr.Markdown("# 🤖 Asistente Virtual CVision")
280
  gr.Markdown("### Sistema multimodal con voz")
281
 
282
- # CONTENEDOR DE LA ANIMACIÓN NÉBULA
283
  gr.HTML("""
284
- <div class="nebula-wrapper">
285
- <canvas id="nebulaCanvas"></canvas>
 
 
286
  </div>
287
  """)
288
 
289
- # GENERAL
290
- with gr.Tab("💬 General"):
291
- create_chat_tab(regular_chat, "general")
292
-
293
- # WEB
294
- with gr.Tab("🌐 Web"):
295
- create_chat_tab(browser_chat, "web")
296
-
297
- # PRODUCTIVIDAD
298
- with gr.Tab("📈 Productividad"):
299
- create_chat_tab(productivity_chat, "productividad")
300
-
301
- # CIENTÍFICO
302
- with gr.Tab("🔬 Científico"):
303
- create_chat_tab(scientist_chat, "cientifico")
304
-
305
- # PDF
306
  with gr.Tab("📄 Chat PDF"):
307
  chatbot_pdf = gr.Chatbot(height=500)
308
  audio_pdf = gr.Audio(label="🔊 Respuesta PDF", autoplay=True)
309
  uploaded_document = gr.File(file_types=[".pdf"], label="Sube tu PDF")
310
  text_pdf = gr.Textbox(placeholder=PLACEHOLDER)
311
  mic_pdf = gr.Audio(sources=["microphone"], type="filepath")
312
-
313
  with gr.Row():
314
- btn_text = gr.Button("Enviar")
315
- btn_voice = gr.Button("🎤 Voz")
316
- btn_clear = gr.Button("Limpiar")
317
-
318
  btn_text.click(lambda h, t, f: handle_text_chat(h, t, lambda hh, tt: document_chat(hh, tt, f)), inputs=[chatbot_pdf, text_pdf, uploaded_document], outputs=chatbot_pdf)
319
  btn_voice.click(lambda h, a, f: voice_chat(h, a, "pdf", f), inputs=[chatbot_pdf, mic_pdf, uploaded_document], outputs=[chatbot_pdf, audio_pdf])
320
  btn_clear.click(lambda: ([], None), outputs=[chatbot_pdf, audio_pdf])
 
12
 
13
  PLACEHOLDER = "Escribe lo que necesites..."
14
 
15
+ # ---------------- JAVASCRIPT: NÉBULA CIRCULAR AZUL ----------------
 
 
16
  NEBULA_JS = """
17
  function createCircularNebula() {
18
  const canvas = document.getElementById('nebulaCanvas');
19
  if (!canvas) return;
20
  const ctx = canvas.getContext('2d');
21
  let particles = [];
 
22
 
23
+ const NUM_PARTICLES = 100;
24
+ const CONNECTION_DISTANCE = 90;
 
 
 
25
 
26
  function resize() {
 
27
  canvas.width = canvas.offsetWidth;
28
  canvas.height = canvas.offsetHeight;
29
+ initParticles();
30
  }
31
  window.addEventListener('resize', resize);
32
+
 
33
  class Particle {
34
+ constructor(angle, radius) {
35
  this.angle = angle;
36
+ this.baseRadius = radius;
37
+ this.speed = (Math.random() - 0.5) * 0.005;
38
+ this.oscillation = Math.random() * 10;
 
39
  this.time = Math.random() * 100;
40
+ this.update();
 
 
41
  }
42
+ update() {
43
+ this.time += 0.02;
44
+ const r = this.baseRadius + Math.sin(this.time) * this.oscillation;
45
+ this.x = canvas.width / 2 + Math.cos(this.angle) * r;
46
+ this.y = canvas.height / 2 + Math.sin(this.angle) * r;
47
+ this.angle += this.speed;
 
 
 
 
 
48
  }
 
49
  draw() {
50
+ ctx.fillStyle = 'rgba(0, 210, 255, 0.8)';
51
  ctx.beginPath();
52
+ const s = 2;
53
+ ctx.moveTo(this.x, this.y - s);
54
+ ctx.lineTo(this.x + s, this.y + s);
55
+ ctx.lineTo(this.x - s, this.y + s);
 
56
  ctx.closePath();
57
  ctx.fill();
58
  }
 
60
 
61
  function initParticles() {
62
  particles = [];
63
+ const minDim = Math.min(canvas.width, canvas.height) / 2;
 
 
 
64
  for (let i = 0; i < NUM_PARTICLES; i++) {
65
+ const angle = Math.random() * Math.PI * 2;
66
+ const r = minDim * (0.5 + Math.random() * 0.3);
67
+ particles.push(new Particle(angle, r));
68
  }
69
  }
70
 
 
 
71
  function animate() {
72
  ctx.clearRect(0, 0, canvas.width, canvas.height);
73
+ ctx.strokeStyle = 'rgba(0, 150, 255, 0.15)';
74
  ctx.lineWidth = 0.8;
 
75
  particles.forEach((p, i) => {
76
+ p.update();
77
  p.draw();
 
78
  for (let j = i + 1; j < particles.length; j++) {
79
+ const d = Math.hypot(p.x - particles[j].x, p.y - particles[j].y);
80
+ if (d < CONNECTION_DISTANCE) {
 
81
  ctx.beginPath();
82
  ctx.moveTo(p.x, p.y);
83
+ ctx.lineTo(particles[j].x, particles[j].y);
84
  ctx.stroke();
85
  }
86
  }
87
  });
88
+ requestAnimationFrame(animate);
 
 
 
 
 
89
  }
90
+ resize();
 
91
  animate();
92
  }
 
 
93
  setTimeout(createCircularNebula, 1000);
94
  """
95
 
96
+ # ---------------- FUNCIONES DE SOPORTE (SIN CAMBIOS) ----------------
97
  def speech_to_text(audio):
98
+ if audio is None: return ""
99
+ try: return transcribe_audio_file(audio)
 
 
100
  except Exception as e:
101
  print("Error STT:", e)
102
  return ""
103
 
 
104
  def summarize_for_voice(text, max_chars=250):
105
+ if not text: return ""
 
106
  text = str(text).strip()
107
+ if len(text) <= max_chars: return text
 
108
  summary = text[:max_chars]
109
+ if "." in summary: summary = summary.rsplit(".", 1)[0] + "."
110
+ else: summary = summary.rsplit(" ", 1)[0]
 
 
111
  return summary + " ..."
112
 
 
113
  def process_chat_result(result):
114
  final_response = []
115
  try:
116
  if hasattr(result, "__iter__") and not isinstance(result, list):
117
+ for update in result: final_response = update
118
+ else: final_response = result
119
+ except Exception as e: print("Error generator:", e)
120
+ if final_response is None: final_response = []
121
+ if not isinstance(final_response, list): final_response = [["", str(final_response)]]
 
 
 
 
 
122
  return final_response
123
 
 
124
  def normalize_chat_format(chat_data):
125
+ if not isinstance(chat_data, list): return [["", str(chat_data)]]
 
126
  normalized = []
127
  for item in chat_data:
128
+ if isinstance(item, list) and len(item) == 2: normalized.append(item)
129
+ elif isinstance(item, tuple) and len(item) == 2: normalized.append([item[0], item[1]])
130
+ else: normalized.append(["", str(item)])
 
 
 
131
  return normalized
132
 
 
133
  def handle_text_chat(chat_history, user_text, chat_function):
134
+ if chat_history is None: chat_history = []
 
135
  result = chat_function(chat_history, user_text)
136
  final_response = process_chat_result(result)
137
  return normalize_chat_format(final_response)
138
 
 
139
  def voice_chat(chat_history, audio, mode, uploaded_document=None):
140
+ if chat_history is None: chat_history = []
141
+ if audio is None: return chat_history, None
142
+ try: user_text = transcribe_audio_file(audio)
143
+ except Exception as e: return chat_history + [["", "❌ Error"]], None
144
+
145
+ if mode == "productividad": result = productivity_chat(chat_history, user_text)
146
+ elif mode == "web": result = browser_chat(chat_history, user_text)
147
+ elif mode == "cientifico": result = scientist_chat(chat_history, user_text)
148
+ elif mode == "pdf": result = document_chat(chat_history, user_text, uploaded_document)
149
+ else: result = regular_chat(chat_history, user_text)
 
 
 
 
 
 
 
 
 
 
 
 
150
 
151
+ final_response = normalize_chat_format(process_chat_result(result))
 
152
  audio_path = None
153
  try:
154
+ voice_text = summarize_for_voice(final_response[-1][1])
 
155
  audio_path = speak(voice_text)
156
+ except: pass
 
157
  return final_response, audio_path
158
 
159
+ # ---------------- CSS MEJORADO (CENTRADOS) ----------------
160
  CUSTOM_CSS = """
161
+ body { background: #0a0f1e; color: white; }
162
+ .chatbot { height: 500px !important; }
163
+ .nebula-container-outer {
164
+ display: flex;
165
+ justify-content: center;
166
+ width: 100%;
167
+ margin-bottom: 20px;
168
  }
169
  .nebula-wrapper {
170
  background: rgba(0, 0, 0, 0.4);
171
+ border-radius: 50%; /* Forma circular para el fondo también */
172
+ border: 1px solid rgba(0, 210, 255, 0.3);
173
  overflow: hidden;
174
+ width: 300px; /* Tamaño del recuadro controlado */
175
+ height: 300px;
176
  position: relative;
177
+ box-shadow: 0 0 20px rgba(0, 210, 255, 0.1);
 
 
 
 
 
 
 
178
  }
179
+ #nebulaCanvas { width: 100%; height: 100%; display: block; }
180
  """
181
 
 
182
  def create_chat_tab(chat_function, mode_name):
183
  chatbot = gr.Chatbot(height=500)
184
  audio_player = gr.Audio(label="🔊 Respuesta por voz", autoplay=True)
185
  text_input = gr.Textbox(placeholder=PLACEHOLDER)
186
  mic = gr.Audio(sources=["microphone"], type="filepath")
 
187
  with gr.Row():
188
  text_button = gr.Button("Enviar")
189
  voice_button = gr.Button("🎤 Voz")
190
  clear_button = gr.Button("Limpiar")
 
191
  text_button.click(lambda h, t: handle_text_chat(h, t, chat_function), inputs=[chatbot, text_input], outputs=chatbot)
192
  voice_button.click(lambda h, a: voice_chat(h, a, mode_name), inputs=[chatbot, mic], outputs=[chatbot, audio_player])
193
  clear_button.click(lambda: ([], None), outputs=[chatbot, audio_player])
 
203
  gr.Markdown("# 🤖 Asistente Virtual CVision")
204
  gr.Markdown("### Sistema multimodal con voz")
205
 
206
+ # ANIMACIÓN NÉBULA CENTRADA
207
  gr.HTML("""
208
+ <div class="nebula-container-outer">
209
+ <div class="nebula-wrapper">
210
+ <canvas id="nebulaCanvas"></canvas>
211
+ </div>
212
  </div>
213
  """)
214
 
215
+ with gr.Tab("💬 General"): create_chat_tab(regular_chat, "general")
216
+ with gr.Tab("🌐 Web"): create_chat_tab(browser_chat, "web")
217
+ with gr.Tab("📈 Productividad"): create_chat_tab(productivity_chat, "productividad")
218
+ with gr.Tab("🔬 Científico"): create_chat_tab(scientist_chat, "cientifico")
 
 
 
 
 
 
 
 
 
 
 
 
 
219
  with gr.Tab("📄 Chat PDF"):
220
  chatbot_pdf = gr.Chatbot(height=500)
221
  audio_pdf = gr.Audio(label="🔊 Respuesta PDF", autoplay=True)
222
  uploaded_document = gr.File(file_types=[".pdf"], label="Sube tu PDF")
223
  text_pdf = gr.Textbox(placeholder=PLACEHOLDER)
224
  mic_pdf = gr.Audio(sources=["microphone"], type="filepath")
 
225
  with gr.Row():
226
+ btn_text = gr.Button("Enviar"); btn_voice = gr.Button("🎤 Voz"); btn_clear = gr.Button("Limpiar")
 
 
 
227
  btn_text.click(lambda h, t, f: handle_text_chat(h, t, lambda hh, tt: document_chat(hh, tt, f)), inputs=[chatbot_pdf, text_pdf, uploaded_document], outputs=chatbot_pdf)
228
  btn_voice.click(lambda h, a, f: voice_chat(h, a, "pdf", f), inputs=[chatbot_pdf, mic_pdf, uploaded_document], outputs=[chatbot_pdf, audio_pdf])
229
  btn_clear.click(lambda: ([], None), outputs=[chatbot_pdf, audio_pdf])