import gradio as gr import google.generativeai as genai import os import re from typing import List, Tuple # --- Configuración Mejorada con Manejo de Errores --- def configurar_gemini(): try: # Opción 1: Variable de entorno en Hugging Face api_key = os.environ.get("GOOGLE_API_KEY") # Opción 2: Fallback para desarrollo local if not api_key: from dotenv import load_dotenv load_dotenv() api_key = os.environ.get("GOOGLE_API_KEY") if not api_key: return None, "❌ **ERROR**: GOOGLE_API_KEY no encontrada. Configúrala en los Secrets de Hugging Face." # Configurar con manejo de versión genai.configure(api_key=api_key) # Verificar modelos disponibles try: models = genai.list_models() model_names = [model.name for model in models] # Priorizar modelos disponibles preferred_models = [ 'models/gemini-1.5-pro', 'models/gemini-1.5-flash', 'models/gemini-pro', 'models/gemini-pro-vision' ] for model_name in preferred_models: if model_name in model_names: model = genai.GenerativeModel(model_name.split('/')[-1]) return model, f"✅ **Conectado**: Usando {model_name}" # --- Modificación en tu función configurar_gemini (Alrededor de la línea 42) --- # 💡 Nueva Lógica: Priorizar los modelos 2.5 más recientes # Lista de modelos a buscar, en orden de preferencia (más potente/nuevo primero) preferred_prefixes = [ 'gemini-2.5-pro', 'gemini-2.5-flash', 'gemini-pro', # Fallback a nombres base 'gemini-flash' ] selected_model_name = None # Buscar el modelo más potente disponible for prefix in preferred_prefixes: # Buscamos la versión más reciente del modelo con ese prefijo matching_models = [name for name in model_names if prefix in name] if matching_models: # Seleccionar el modelo que contenga el prefijo y que esté disponible # A menudo, los más largos son las versiones preview selected_model_name = sorted(matching_models, key=len, reverse=True)[0] break # Encontramos el mejor, salimos del bucle if selected_model_name: # Obtener solo el nombre corto del modelo (lo que va después de 'models/') model_alias = selected_model_name.split('/')[-1] model = genai.GenerativeModel(model_alias) return model, f"✅ **Conectado**: Usando {selected_model_name}" # Si no se encontró ninguno de los preferidos return None, f"❌ **ERROR**: No se encontraron modelos Gemini compatibles. Disponibles: {', '.join(model_names[:5])}..." # Mostrar solo los primeros 5 except Exception as e: return None, f"❌ **ERROR**: No se encontraron modelos Gemini. Disponibles: {', '.join(model_names)}" except Exception as e: return None, f"❌ **ERROR al conectar con Gemini**: {str(e)}" except Exception as e: return None, f"❌ **ERROR de configuración**: {str(e)}" # Inicializar modelo model, status_message = configurar_gemini() # --- CSS Futurista Mejorado --- futurista_css = """ :root { --primary: #00ff88; --secondary: #0088ff; --accent: #ff0088; --dark-bg: #0a0a0f; --darker-bg: #050508; --card-bg: #111122; --text: #e0e0ff; --text-secondary: #a0a0c0; --border: #222244; } .gradio-container { background: linear-gradient(135deg, var(--dark-bg) 0%, var(--darker-bg) 100%) !important; min-height: 100vh !important; font-family: 'Segoe UI', system-ui, sans-serif; } .dark-bg { background: var(--darker-bg) !important; } .contain { background: var(--card-bg) !important; border: 1px solid var(--border) !important; border-radius: 15px !important; box-shadow: 0 0 20px rgba(0, 255, 136, 0.1) !important; } .chatbot { background: var(--darker-bg) !important; border: none !important; min-height: 400px !important; } .message { margin: 8px 0 !important; border: none !important; padding: 12px 16px !important; } .message.user { background: linear-gradient(135deg, #0088ff, #00ff88) !important; color: white !important; border-radius: 18px 18px 4px 18px !important; margin-left: 20% !important; } .message.bot { background: var(--card-bg) !important; color: var(--text) !important; border: 1px solid var(--border) !important; border-radius: 18px 18px 18px 4px !important; margin-right: 20% !important; } .textbox { background: var(--card-bg) !important; border: 1px solid var(--border) !important; border-radius: 25px !important; color: var(--text) !important; padding: 12px 20px !important; } .textbox:focus { border-color: var(--primary) !important; box-shadow: 0 0 10px var(--primary) !important; } .button { background: linear-gradient(135deg, var(--primary), var(--secondary)) !important; border: none !important; border-radius: 20px !important; color: white !important; font-weight: 600 !important; transition: all 0.3s ease !important; } .button:hover { transform: translateY(-2px) !important; box-shadow: 0 5px 15px rgba(0, 255, 136, 0.3) !important; } .button-secondary { background: var(--card-bg) !important; border: 1px solid var(--border) !important; color: var(--text) !important; } .header-gradient { background: linear-gradient(135deg, var(--primary), var(--secondary), var(--accent)) !important; -webkit-background-clip: text !important; -webkit-text-fill-color: transparent !important; text-align: center !important; font-weight: 800 !important; font-size: 2.2em !important; margin-bottom: 10px !important; text-shadow: 0 0 20px rgba(0, 255, 136, 0.3); } .status-banner { background: linear-gradient(135deg, #ff0088, #ff4444) !important; color: white !important; padding: 10px 15px !important; border-radius: 10px !important; margin: 10px 0 !important; text-align: center !important; font-weight: bold !important; } .status-success { background: linear-gradient(135deg, #00ff88, #00aaff) !important; } .code-block { background: var(--darker-bg) !important; border: 1px solid var(--border) !important; border-radius: 10px !important; padding: 15px !important; margin: 10px 0 !important; font-family: 'Courier New', monospace !important; color: var(--primary) !important; border-left: 4px solid var(--primary) !important; overflow-x: auto !important; } .examples-box { background: var(--card-bg) !important; border: 1px solid var(--border) !important; border-radius: 10px !important; padding: 15px !important; margin: 10px 0 !important; } """ # --- Funciones del Asistente --- def detectar_lenguaje(codigo: str) -> str: """Detecta el lenguaje de programación del código.""" patrones = { 'python': [r'^def\s+', r'^class\s+', r'^import\s+', r'^from\s+', r'print\('], 'javascript': [r'function\s*\w*\s*\(', r'const\s+|let\s+|var\s+', r'=>', r'console\.log'], 'java': [r'public\s+class', r'private\s+', r'protected\s+', r'System\.out\.print'], 'cpp': [r'#include\s+<', r'using\s+namespace', r'std::', r'cout\s*<<'], 'html': [r'', r'', r'', r''], } for lenguaje, patrones_list in patrones.items(): for patron in patrones_list: if re.search(patron, codigo, re.MULTILINE | re.IGNORECASE): return lenguaje return 'desconocido' def analizar_codigo_basico(codigo: str) -> dict: """Análisis básico del código.""" problemas = [] sugerencias = [] lenguaje = detectar_lenguaje(codigo) # Análisis básico lineas = codigo.split('\n') if len(lineas) > 50: problemas.append("Código potencialmente muy largo") sugerencias.append("Considera dividir en funciones más pequeñas") for i, linea in enumerate(lineas): if len(linea.strip()) > 0 and len(linea) > 100: problemas.append(f"Línea {i+1} muy larga ({len(linea)} caracteres)") sugerencias.append("Divide la línea para mejorar legibilidad") return { 'lenguaje': lenguaje, 'problemas': problemas, 'sugerencias': sugerencias, 'lineas': len(lineas), 'caracteres': len(codigo) } def formatear_respuesta(respuesta: str) -> str: """Formatea la respuesta con estilos HTML.""" # Detectar bloques de código respuesta = re.sub( r'```(\w+)?\n(.*?)```', r'
Código \1:
\2
', respuesta, flags=re.DOTALL ) # Formatear listas respuesta = re.sub(r'^- (.*?)$', r'• \1', respuesta, flags=re.MULTILINE) return respuesta def generar_respuesta_fallback(mensaje: str) -> str: """Genera una respuesta cuando Gemini no está disponible.""" mensaje_lower = mensaje.lower() if "analiza" in mensaje_lower and "código" in mensaje_lower: codigo_match = re.search(r'```(?:\w+)?\n(.*?)```', mensaje, re.DOTALL) if codigo_match: codigo = codigo_match.group(1) analisis = analizar_codigo_basico(codigo) respuesta = f""" 🔍 **Análisis Básico del Código** **Lenguaje detectado:** {analisis['lenguaje']} **Líneas de código:** {analisis['lineas']} **Caracteres:** {analisis['caracteres']} """ if analisis['problemas']: respuesta += "**⚠️ Problemas detectados:**\n" + "\n".join([f"• {p}" for p in analisis['problemas']]) respuesta += "\n\n**💡 Sugerencias:**\n" + "\n".join([f"• {s}" for s in analisis['sugerencias']]) else: respuesta += "✅ **No se detectaron problemas evidentes en el análisis básico.**" return respuesta # Respuestas predefinidas para casos comunes respuestas_predefinidas = { "hola": "¡Hola! Soy Cyber-Coder 9000. Actualmente estoy en modo básico. ¿En qué puedo ayudarte con tu código?", "help": """ 🤖 **Cyber-Coder 9000 - Modo Básico** **Funciones disponibles:** • 🔍 Análisis básico de código • 📝 Revisión de sintaxis simple • 💡 Sugerencias de estructura **Cómo usar:** 1. Para analizar código: `analiza este código: [tu código]` 2. Envía el código entre triple backticks: \\`\\`\\`python # tu código aquí \\`\\`\\` **Nota:** La funcionalidad completa estará disponible cuando se configure la API de Gemini. """, "python": "**💡 Tip Python:** Recuerda usar type hints, docstrings y manejar excepciones adecuadamente.", "javascript": "**💡 Tip JavaScript:** Usa 'const' y 'let' en lugar de 'var', y considera async/await para operaciones asíncronas.", "error": "**🔧 Para debugging:** Revisa la sintaxis, verifica los tipos de datos y usa print statements para seguimiento." } for clave, valor in respuestas_predefinidas.items(): if clave in mensaje_lower: return valor return """ 🤖 **Cyber-Coder 9000 - Modo Básico** Actualmente estoy operando en modo limitado. Para funcionalidad completa: 1. **Configura tu API Key de Gemini** en los Secrets de Hugging Face 2. **Asegúrate** de que la variable se llame `GOOGLE_API_KEY` 3. **Verifica** que la API key sea válida **Mientras tanto, puedo ayudarte con:** • Análisis básico de estructura de código • Sugerencias generales de programación • Revisión de sintaxis simple ¿En qué aspecto de tu código necesitas ayuda? """ def asistente_codificacion(mensaje: str, historial: List[Tuple[str, str]]) -> str: """Función principal del asistente.""" if not mensaje.strip(): return "Por favor, escribe un mensaje." # Si Gemini está disponible, usarlo if model: try: contexto = "Eres un asistente especializado en análisis, corrección y generación de código. Responde de manera técnica pero clara." if "analiza" in mensaje.lower() and "código" in mensaje.lower(): codigo_match = re.search(r'```(?:\w+)?\n(.*?)```', mensaje, re.DOTALL) if codigo_match: codigo = codigo_match.group(1) lenguaje = detectar_lenguaje(codigo) contexto += f" Analiza este código en {lenguaje} y proporciona sugerencias de mejora." response = model.generate_content(contexto + "\n\n" + mensaje) return formatear_respuesta(response.text) except Exception as e: return f"❌ **Error de Gemini**: {str(e)}\n\n" + generar_respuesta_fallback(mensaje) else: # Modo fallback return generar_respuesta_fallback(mensaje) # --- Interfaz de Gradio --- def crear_interfaz(): with gr.Blocks(css=futurista_css, theme=gr.themes.Soft()) as demo: gr.Markdown( """
🤖 CYBER-CODER 9000
""", elem_id="header" ) # Banner de estado status_class = "status-success" if model else "status-banner" gr.Markdown( f"""
{status_message}
""", elem_id="status" ) gr.Markdown( """ **Asistente Especializado en Codificación Avanzada** *Auto-completa • Analiza • Corrige • Perfecciona • Genera* """, elem_classes="description" ) with gr.Row(): with gr.Column(scale=3): chatbot = gr.Chatbot( label="🤖 Sesión de Codificación", height=400, show_copy_button=True, show_share_button=True, placeholder="Escribe tu consulta de código aquí..." ) with gr.Row(): msg = gr.Textbox( label="💻 Instrucción de Código", placeholder="Ej: 'analiza este código', 'genera una función Python para...', 'corrige este error'", scale=4, lines=2, max_lines=5 ) btn = gr.Button("🚀 Ejecutar", scale=1) with gr.Row(): clear_btn = gr.Button("🔄 Limpiar Chat", variant="secondary") with gr.Column(scale=1): gr.Markdown("### 🛠️ Acciones Rápidas") with gr.Column(): accion_analizar = gr.Button("🔍 Análisis de Código") accion_corregir = gr.Button("🔧 Corrección") accion_generar = gr.Button("⚡ Generar Código") accion_optimizar = gr.Button("🎯 Optimizar") accion_help = gr.Button("❓ Ayuda") gr.Markdown("---") gr.Markdown("### 📊 Estado") gr.Markdown(f"**Conexión Gemini:** {'✅ Conectado' if model else '❌ Sin conexión'}") gr.Markdown("**Modo actual:** Básico" if not model else "**Modo actual:** Completo") # Ejemplos de uso with gr.Accordion("📚 Ejemplos Rápidos", open=False): gr.Markdown(""" **Haz clic en cualquier ejemplo para probar:** """) ejemplos = gr.Examples( examples=[ ["Analiza este código Python:\n```python\ndef factorial(n):\n if n == 0:\n return 1\n else:\n return n * factorial(n-1)\n```"], ["Genera una función en Python que valide emails usando expresiones regulares"], ["¿Cuáles son las mejores prácticas para manejar errores en JavaScript?"], ["Corrige este código:\n```python\ndef sum_list(numbers):\n total = 0\n for i in range(len(numbers)):\n total += numbers[i]\n return total\n```"] ], inputs=msg, label="" ) # Event handlers def enviar_mensaje(mensaje, historial): if not mensaje.strip(): return "", historial respuesta = asistente_codificacion(mensaje, historial) historial.append((mensaje, respuesta)) return "", historial def accion_rapida(accion): mensajes = { "🔍 Análisis de Código": "Por favor, pega el código que quieres analizar entre triple backticks (```). Ejemplo: \\`\\`\\`python\n# tu código aquí\n\\`\\`\\`", "🔧 Corrección": "Pega el código con errores entre triple backticks para que pueda ayudarte a corregirlo.", "⚡ Generar Código": "Describe qué código necesitas generar. Sé específico sobre el lenguaje y funcionalidad.", "🎯 Optimizar": "Pega el código que quieres optimizar entre triple backticks para mejorar su rendimiento.", "❓ Ayuda": "help" } return mensajes.get(accion, "Selecciona una acción válida") # Conectar eventos btn.click(enviar_mensaje, [msg, chatbot], [msg, chatbot]) msg.submit(enviar_mensaje, [msg, chatbot], [msg, chatbot]) clear_btn.click(lambda: None, None, chatbot, queue=False) # Conectar acciones rápidas acciones_rapidas = [accion_analizar, accion_corregir, accion_generar, accion_optimizar, accion_help] for accion in acciones_rapidas: accion.click( accion_rapida, inputs=[accion], outputs=[msg] ) return demo # --- Inicialización --- demo = crear_interfaz() if __name__ == "__main__": demo.launch( server_name="0.0.0.0", server_port=7860, share=True )