Agen_Kode_Bat / app.py
ivanoctaviogaitansantos's picture
Update app.py
bc0b670 verified
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'<!DOCTYPE html>', r'<html>', r'<head>', r'<body>'],
}
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'<div class="code-block"><strong>Código \1:</strong><br><pre>\2</pre></div>',
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(
"""
<div class="header-gradient">
🤖 CYBER-CODER 9000
</div>
""",
elem_id="header"
)
# Banner de estado
status_class = "status-success" if model else "status-banner"
gr.Markdown(
f"""<div class="{status_class}">{status_message}</div>""",
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
)