# ===================================================================== # SECCIÓN 1: IMPORTACIONES ESTÁNDAR Y DE SISTEMA # ===================================================================== import gradio as gr import re import os # ===================================================================== # SECCIÓN 2: MÓDULOS DE AUDIO Y TRANSCRIPCIÓN (CVISION) # ===================================================================== from modules.listen import transcribe_audio_file from modules.talker.talk import speak # ===================================================================== # SECCIÓN 3: ACOPLAMIENTO DEL MÓDULO DE VOZ MODULAR (DESDE 0) # ===================================================================== from modules.langchain_assistant.voice_module import create_voice_tab # ════════════════════════════════════════════════════════ # FALLBACK CHAT — Módulos de respaldo si falla la importación # ════════════════════════════════════════════════════════ def _fallback_chat(history, message, *args, **kwargs): yield (history or []) + [[message, "⚠️ Módulo no disponible. Revisa los logs del servidor."]], None try: from modules.utils.chat_utils import ( productivity_chat, browser_chat, scientist_chat, regular_chat, document_chat, ) print("✅ Módulos de chat importados correctamente") except Exception as e: print(f"❌ Error importando módulos de chat: {e}") regular_chat = _fallback_chat browser_chat = _fallback_chat productivity_chat = _fallback_chat scientist_chat = _fallback_chat document_chat = _fallback_chat # ════════════════════════════════════════════════════════ # CONSTANTES GLOBALES # ════════════════════════════════════════════════════════ PLACEHOLDER = "Escribe tu mensaje para CVision..." AVATAR_URL = "https://huggingface.co/spaces/CVision-assitant/asistente-virtual-CVision/resolve/main/assets/robot-bckound-black.png" # ════════════════════════════════════════════════════════ # CSS GLOBAL — Estilos de toda la interfaz # ════════════════════════════════════════════════════════ CUSTOM_CSS = """ @import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700;900&family=Exo+2:wght@300;400;500&family=Share+Tech+Mono&display=swap'); /* ══ VARIABLES CSS GLOBALES ══ Modifica aquí los colores principales de toda la app */ :root { --cyan:#00d4ff; /* Color principal cian */ --cyan2:#00a8e8; /* Cian secundario */ --cg:#00ff88; /* Color verde neón (online/activo) */ --bg-main:#020408; /* Fondo principal oscuro */ --border:rgba(0,212,255,0.16); --font-d:'Orbitron',monospace; /* Fuente display/títulos */ --font-b:'Exo 2',sans-serif; /* Fuente body */ --font-m:'Share Tech Mono',monospace; /* Fuente monospace/datos */ } /* ══ RESET Y BASE ══ */ html,body { overflow-x:hidden !important; } body,.gradio-container { background:var(--bg-main) !important; color:white !important; font-family:'Exo 2','Segoe UI',sans-serif; } /* ══ LÍNEA DE ESCANEO ANIMADA (scan line global) ══ Desactivar: comentar este bloque */ .gradio-container::after { content:''; position:fixed; top:0; left:0; width:100%; height:2px; background:linear-gradient(90deg,transparent,rgba(0,212,255,0.30),transparent); animation:cvScanDown 7s ease-in-out infinite; z-index:9999; pointer-events:none; } /* ══ KEYFRAMES — Todas las animaciones CSS ══ Modifica aquí velocidades/efectos de animación */ @keyframes cvScanDown { 0%{top:-2px;opacity:0} 8%{opacity:1} 92%{opacity:.4} 100%{top:100vh;opacity:0} } @keyframes cvSpin { to{transform:rotate(360deg)} } @keyframes cvBlink { 0%,100%{opacity:.25} 50%{opacity:1} } @keyframes cvOrbit { to{transform:rotate(360deg)} } @keyframes cvFloat { 0%,100%{transform:translateY(0)} 50%{transform:translateY(-4px)} } @keyframes pulse { 0%,100%{transform:scale(1);opacity:1} 50%{transform:scale(1.3);opacity:.7} } @keyframes bglow { 0%,100%{transform:scale(1);opacity:.5} 50%{transform:scale(1.2);opacity:1} } @keyframes radarSpin { to{transform:rotate(360deg)} } @keyframes headerShimmer { 0%,100%{opacity:0} 50%{opacity:1} } @keyframes btnGlow { 0%,100%{box-shadow:0 0 12px rgba(0,212,255,.3),0 0 24px rgba(0,212,255,.1)} 50%{box-shadow:0 0 22px rgba(0,212,255,.7),0 0 44px rgba(0,212,255,.25)} } @keyframes avatar-pulse { 0% { border-color:rgba(0,212,255,0.3); box-shadow:0 0 5px rgba(0,212,255,0.2); } 50% { border-color:rgba(0,212,255,1); box-shadow:0 0 18px rgba(0,212,255,0.6); } 100%{ border-color:rgba(0,212,255,0.3); box-shadow:0 0 5px rgba(0,212,255,0.2); } } /* ══ HEADER — Barra superior con logo y título ══ Modificar padding/height aquí para cambiar tamaño del header */ #cvision-header { background:rgba(1,4,12,0.98); border-bottom:1px solid rgba(0,212,255,0.18); padding:12px 28px; display:flex; align-items:center; gap:16px; backdrop-filter:blur(24px); position:relative; z-index:10; flex-shrink:0; box-shadow:0 4px 40px rgba(0,0,0,.8),0 0 30px rgba(0,212,255,.04); } /* Línea inferior animada del header */ #cvision-header::after { content:''; position:absolute; bottom:0; left:0; right:0; height:1px; background:linear-gradient(90deg,transparent,rgba(0,212,255,.5) 30%,rgba(0,255,200,.7) 50%,rgba(0,212,255,.5) 70%,transparent); animation:headerShimmer 3s ease-in-out infinite; } /* ══ NEBULA SECTION — Contenedor de la sección hero (nebula + info + panel derecho) ══ Modificar padding/gap aquí para espaciado de la sección */ #cvision-nebula-section { position:relative; display:flex; align-items:center; gap:24px; padding:28px 32px; border-bottom:1px solid rgba(0,212,255,0.10); background:linear-gradient(180deg,rgba(0,8,20,0.97),rgba(1,6,16,0.99)); flex-shrink:0; overflow:hidden; } /* ══ GRID CANVAS — Canvas de fondo con cuadrícula y puntos ══ Es el fondo animado de la sección nebula */ #cv-grid-canvas { position:absolute; inset:0; width:100% !important; height:100% !important; pointer-events:none; z-index:0; } /* ══ NEBULA CANVAS WRAPPER — Contenedor del canvas de la esfera nebula ══ Modificar width/height aquí para cambiar tamaño de la nebula */ #cv-nebula-wrap { position:relative; width:420px; height:420px; flex-shrink:0; z-index:2; } /* Tamaño del canvas de la nebula (debe coincidir con el wrapper) */ #nebulaCanvas { width:420px !important; height:420px !important; display:block; position:relative; z-index:2; } /* ══ HERO INFO — Bloque de texto a la derecha de la nebula ══ Título, descripción y chips de capacidades */ #cv-hero-info { position:relative; z-index:2; display:flex; flex-direction:column; gap:13px; flex:1; min-width:0; } /* Texto de descripción del hero — modificar font-size para tamaño */ #cv-hero-info .cv-desc { font-family:'Exo 2',sans-serif; font-size:1.08rem !important; color:rgba(90,160,190,.85) !important; line-height:1.85 !important; font-weight:300; max-width:460px; } /* ══ RIGHT PANEL — Panel lateral derecho (Núcleo Neural + Conectividad) ══ Modificar width para cambiar ancho del panel */ #cv-right-panel { position:relative; z-index:2; display:flex; flex-direction:column; gap:10px; width:210px; flex-shrink:0; } /* ══ RCARD — Tarjetas del panel derecho ══ Estilos compartidos por Núcleo Neural y Conectividad */ .rcard { background:rgba(2,7,18,.92); border:1px solid rgba(0,212,255,.12); border-radius:12px; overflow:hidden; position:relative; box-shadow:0 0 30px rgba(0,0,0,.5); } .rcard::before { content:''; position:absolute; top:0; left:0; right:0; height:1px; background:linear-gradient(90deg,transparent,rgba(0,212,255,.4),rgba(0,255,200,.2),transparent); } .rcard::after { content:''; position:absolute; bottom:0; left:0; right:0; height:1px; background:linear-gradient(90deg,transparent,rgba(0,212,255,.1),transparent); } /* ══ RCARD HEADER — Cabecera de las tarjetas del panel derecho ══ */ .rh { display:flex; align-items:center; gap:6px; padding:8px 12px; border-bottom:1px solid rgba(0,212,255,.06); } .rh-dot { width:5px; height:5px; border-radius:50%; background:var(--cyan); box-shadow:0 0 6px var(--cyan); flex-shrink:0; animation:pulse 2s infinite; } .rh-t { font-family:'Orbitron',monospace; font-size:8px; font-weight:700; color:rgba(0,190,240,.6); letter-spacing:.15em; } .rh-s { margin-left:auto; font-family:'Share Tech Mono',monospace; font-size:7.5px; color:rgba(0,150,200,.35); letter-spacing:.1em; } /* ══ BRAIN BOX — Contenedor del canvas del cerebro neural ══ Modificar height para cambiar altura del área del cerebro */ .brain-box { padding:6px; display:flex; align-items:center; justify-content:center; height:180px; position:relative; } /* Glow del fondo del cerebro */ .bglow { position:absolute; width:150px; height:150px; border-radius:50%; background:radial-gradient(circle,rgba(0,100,255,.12),transparent 70%); animation:bglow 2s ease-in-out infinite; } /* Tamaño del canvas del cerebro — modificar width/height */ #brainCvs { width:198px; height:168px; } /* Anillos de radar decorativos */ .rring { position:absolute; border-radius:50%; border:1px solid rgba(0,212,255,.08); } .rring1 { inset:10px; animation:radarSpin 8s linear infinite; } .rring2 { inset:25px; animation:radarSpin 12s linear infinite reverse; } /* ══ CONNECTIVITY CARD — Tarjeta de conectividad/mapa mundial ══ */ .conn-body { padding:9px 12px; } .cstat { display:flex; align-items:center; gap:7px; margin-bottom:5px; } .cdot { width:8px; height:8px; border-radius:50%; background:var(--cg); box-shadow:0 0 12px var(--cg); animation:pulse 1.5s infinite; } .cst { font-family:'Share Tech Mono',monospace; font-size:9px; color:var(--cg); letter-spacing:.12em; } .clat { font-family:'Share Tech Mono',monospace; font-size:8px; color:rgba(0,150,200,.4); letter-spacing:.1em; margin-bottom:7px; } /* Canvas del mapa mundial — modificar width/height */ #worldC { width:100%; height:52px; } /* ══ STATUS BAR — Barra de estado inferior fija ══ Modificar bottom para subir/bajar, font-size para tamaño */ #cvision-statusbar { position:fixed; bottom:4px; left:50%; transform:translateX(-50%); font-family:var(--font-m); font-size:0.75rem; letter-spacing:0.14em; color:rgba(0,212,255,0.75); display:flex; align-items:center; gap:14px; pointer-events:none; z-index:500; white-space:nowrap; padding:6px 20px; background:rgba(0,6,16,0.80); backdrop-filter:blur(12px); border:1px solid rgba(0,212,255,0.22); border-radius:8px; box-shadow:0 0 16px rgba(0,212,255,0.10); } /* Puntos separadores de la status bar */ .cv-sdot { width:4px; height:4px; border-radius:50%; background:rgba(0,212,255,0.5); animation:cvBlink 2s ease-in-out infinite; display:inline-block; } /* ══════════════════════════════════════════════════════ CHATBOT — Contenedor principal y burbujas de mensajes ══════════════════════════════════════════════════════ */ /* ── Contenedor externo del chatbot ── */ .chatbot-container { border-radius:16px !important; border:1px solid rgba(0,210,255,0.25) !important; background:rgba(2,10,20,0.98) !important; box-shadow:0 12px 50px rgba(0,0,0,.8) !important; overflow:hidden !important; } /* ── Wrapper interno (lista de mensajes) ── */ .chatbot-container > div.wrapper { display:flex !important; flex-direction:column !important; gap:14px !important; padding:16px !important; } /* ── BASE de todos los mensajes ── NO poner display:block ni width fijos aquí */ .chatbot-container .message { max-width:82% !important; padding:12px 16px !important; font-size:14px !important; line-height:1.65 !important; position:relative !important; box-sizing:border-box !important; overflow-wrap:break-word !important; word-break:break-word !important; white-space:normal !important; hyphens:auto !important; } /* ── BURBUJA USUARIO (derecha) ── Modificar background/border para cambiar color del usuario */ .chatbot-container .message.user, .chatbot-container [data-testid="user"] { align-self:flex-end !important; margin-left:auto !important; margin-right:6px !important; width:fit-content !important; min-width:60px !important; max-width:75% !important; background:linear-gradient(145deg,rgba(0,80,160,0.82),rgba(0,40,95,0.96)) !important; border:1px solid rgba(0,180,255,0.45) !important; border-radius:18px 18px 4px 18px !important; color:#ffffff !important; box-shadow:0 4px 18px rgba(0,0,0,.55), 0 0 8px rgba(0,120,255,0.12) !important; text-align:left !important; display:block !important; } /* Párrafos dentro de burbuja usuario */ .chatbot-container .message.user p, .chatbot-container [data-testid="user"] p { margin:0 0 6px 0 !important; white-space:normal !important; word-break:break-word !important; overflow-wrap:break-word !important; color:#ffffff !important; } .chatbot-container .message.user p:last-child, .chatbot-container [data-testid="user"] p:last-child { margin-bottom:0 !important; } /* Todos los hijos del mensaje de usuario */ .chatbot-container .message.user *, .chatbot-container [data-testid="user"] * { color:#ffffff !important; overflow-wrap:break-word !important; word-break:break-word !important; white-space:normal !important; max-width:100% !important; } /* ── BURBUJA ASISTENTE (izquierda) ── Modificar background/border-left para cambiar color del bot */ .chatbot-container .message.bot, .chatbot-container [data-testid="bot"] { align-self:flex-start !important; margin-right:auto !important; margin-left:6px !important; width:fit-content !important; min-width:80px !important; max-width:82% !important; background:linear-gradient(145deg,rgba(10,25,45,0.92),rgba(5,15,30,0.96)) !important; border:1px solid rgba(0,212,255,0.15) !important; border-radius:18px 18px 18px 4px !important; border-left:3px solid #00d4ff !important; /* ← línea izquierda cian */ color:#c8e8f8 !important; box-shadow:0 4px 18px rgba(0,0,0,.6) !important; display:block !important; } /* Párrafos dentro de burbuja bot */ .chatbot-container .message.bot p, .chatbot-container [data-testid="bot"] p { margin-bottom:8px !important; white-space:pre-wrap !important; color:#c8e8f8 !important; } .chatbot-container .message.bot p:last-child, .chatbot-container [data-testid="bot"] p:last-child { margin-bottom:0 !important; } /* ── BLOQUES DE CÓDIGO dentro del chatbot ── */ .chatbot-container pre { background:rgba(0,5,15,0.85) !important; border:1px solid rgba(0,212,255,0.3) !important; border-radius:10px !important; padding:12px !important; margin:8px 0 !important; overflow-x:auto !important; white-space:pre !important; } .chatbot-container code { font-family:'Share Tech Mono',monospace !important; color:#00ffcc !important; background:rgba(0,255,200,0.1) !important; padding:2px 5px !important; border-radius:4px !important; white-space:pre-wrap !important; } /* ── SCROLLBAR del chatbot ── */ .chatbot-container > div::-webkit-scrollbar { width:5px !important; } .chatbot-container > div::-webkit-scrollbar-thumb { background:rgba(0,212,255,0.2) !important; border-radius:10px !important; } /* ══ AVATAR DEL ASISTENTE — Esfera con logo CVision ══ Modificar width/height para cambiar tamaño del avatar (actualmente 42x42) */ .chatbot-container .avatar-container { width:48px !important; height:48px !important; border-radius:50% !important; border:2px solid rgba(0,212,255,0.6) !important; box-shadow:0 0 10px rgba(0,212,255,0.35) !important; overflow:hidden !important; background:rgba(2,10,20,0.9) !important; display:flex !important; align-items:center !important; justify-content:center !important; flex-shrink:0 !important; } /* Tamaño del logo dentro del avatar — scale(1.18) para zoom */ .chatbot-container .avatar-container img { width:100% !important; height:100% !important; object-fit:cover !important; transform:scale(1.18) !important; /* ← aumentar para más zoom */ } /* Animación de pulso en el avatar del bot */ .chatbot-container [data-testid="bot"] .avatar-container { animation:avatar-pulse 3s infinite ease-in-out; } /* ══ TEXTBOX — Campo de entrada de texto ══ Modificar border-radius/font-size para cambiar estilo del input */ .gradio-textbox input, .gradio-textbox textarea { background:rgba(0,8,22,.92) !important; border:1px solid rgba(0,180,255,.22) !important; border-radius:22px !important; color:#e0f4ff !important; font-family:'Exo 2',sans-serif !important; font-size:13.5px !important; padding:9px 18px !important; transition:all .3s !important; } .gradio-textbox input:focus, .gradio-textbox textarea:focus { border-color:var(--cyan) !important; box-shadow:0 0 0 1px rgba(0,212,255,.15), 0 0 20px rgba(0,212,255,.12) !important; outline:none !important; } .gradio-textbox input::placeholder, .gradio-textbox textarea::placeholder { color:rgba(0,140,190,.35) !important; } /* ══ BOTÓN ENVIAR — Botón principal de envío de mensaje ══ Modificar background gradient para cambiar color del botón */ .btn-send-cv button, .btn-send-cv { background:linear-gradient(135deg, #001a3a 0%, #003670 40%, #0068b8 75%, #00aadd 100%) !important; border:1px solid rgba(0,212,255,0.5) !important; color:#e8f8ff !important; border-radius:20px !important; font-weight:700 !important; font-size:0.72rem !important; font-family:'Orbitron',monospace !important; letter-spacing:.08em !important; padding:8px 18px !important; height:38px !important; min-width:110px !important; max-width:160px !important; box-shadow:0 0 14px rgba(0,168,232,.35), 0 0 1px rgba(0,212,255,.6) inset !important; transition:all 0.2s ease !important; animation:btnGlow 3s ease-in-out infinite !important; position:relative !important; overflow:hidden !important; white-space:nowrap !important; text-overflow:ellipsis !important; } .btn-send-cv button::after, .btn-send-cv::after { content:''; position:absolute; top:0; left:-100%; width:100%; height:100%; background:linear-gradient(90deg,transparent,rgba(255,255,255,.12),transparent); transition:.4s; } .btn-send-cv button:hover, .btn-send-cv:hover { box-shadow:0 0 28px rgba(0,212,255,.65), 0 0 55px rgba(0,212,255,.2) !important; transform:translateY(-1px) !important; border-color:rgba(0,255,220,.7) !important; } .btn-send-cv button:hover::after, .btn-send-cv:hover::after { left:100%; } /* ══ BOTÓN VOZ — Botón de activar respuesta por voz del asistente ══ */ .btn-voice-cv button, .btn-voice-cv { background:rgba(0,20,50,.6) !important; border:1px solid rgba(0,212,255,.35) !important; color:#00d4ff !important; border-radius:20px !important; font-size:0.72rem !important; font-family:'Share Tech Mono',monospace !important; letter-spacing:.1em !important; padding:8px 14px !important; height:38px !important; min-width:70px !important; max-width:110px !important; transition:all 0.2s !important; white-space:nowrap !important; } .btn-voice-cv button:hover, .btn-voice-cv:hover { background:rgba(0,80,160,.3) !important; box-shadow:0 0 16px rgba(0,212,255,.4) !important; border-color:rgba(0,212,255,.7) !important; } /* ══ BOTÓN LIMPIAR — Botón para limpiar conversación ══ */ .btn-clear-cv button, .btn-clear-cv { background:rgba(30,0,0,.5) !important; border:1px solid rgba(255,50,80,.25) !important; color:rgba(255,100,120,.7) !important; border-radius:20px !important; font-size:0.68rem !important; font-family:'Share Tech Mono',monospace !important; letter-spacing:.08em !important; padding:8px 14px !important; height:38px !important; min-width:90px !important; max-width:160px !important; transition:all 0.2s !important; white-space:nowrap !important; } .btn-clear-cv button:hover, .btn-clear-cv:hover { background:rgba(60,0,0,.6) !important; border-color:rgba(255,80,80,.5) !important; box-shadow:0 0 12px rgba(255,50,50,.25) !important; } /* ══ BOTÓN EJECUTAR (PDF) — Botón de envío en el módulo PDF ══ */ .btn-exec-cv button, .btn-exec-cv { background:linear-gradient(135deg,#001a3a,#00306a,#0055a0) !important; border:1px solid rgba(0,200,255,.45) !important; color:#e0f8ff !important; border-radius:20px !important; font-family:'Orbitron',monospace !important; font-size:0.68rem !important; font-weight:700 !important; letter-spacing:.08em !important; padding:8px 18px !important; height:38px !important; box-shadow:0 0 12px rgba(0,150,255,.3) !important; transition:all .2s !important; white-space:nowrap !important; } .btn-exec-cv button:hover, .btn-exec-cv:hover { box-shadow:0 0 24px rgba(0,212,255,.55) !important; transform:translateY(-1px) !important; } /* ══ MIC / AUDIO COMPONENT — Componente de grabación de voz (voz→texto) ══ Modificar border/background para cambiar apariencia del micrófono */ .mic-tool-cv { background:rgba(0,6,18,.9) !important; border:1px solid rgba(0,180,255,.18) !important; border-radius:12px !important; padding:4px 8px !important; } .mic-tool-cv label { font-family:'Share Tech Mono',monospace !important; font-size:0.65rem !important; color:rgba(0,180,255,.5) !important; letter-spacing:.12em !important; } .mic-tool-cv button { background:rgba(0,50,120,.4) !important; border:1px solid rgba(0,212,255,.3) !important; border-radius:8px !important; color:#00d4ff !important; font-size:0.72rem !important; padding:4px 10px !important; transition:.2s !important; } .mic-tool-cv button:hover { background:rgba(0,80,180,.5) !important; box-shadow:0 0 10px rgba(0,212,255,.3) !important; } /* ══ FILAS DE INPUT — Espaciado de las filas de controles ══ */ .input-row-cv { gap:8px !important; align-items:center !important; } .mic-row-cv { gap:8px !important; align-items:center !important; margin-top:4px !important; } /* ══ FOOTER DE GRADIO — Ocultar footer por defecto ══ */ footer { display:none !important; } """ # ════════════════════════════════════════════════════════ # HEADER HTML — Barra superior: logo CV, título, imagen logo, badges # ════════════════════════════════════════════════════════ HEADER_HTML = """
Interfaz de inteligencia artificial multimodal. Combina procesamiento de lenguaje natural, análisis de documentos y búsqueda web en un solo núcleo cognitivo.