BATUTO-ART's picture
Upload app.py
5552b00 verified
# =========================================================
# BATUTOrquestaIA V3 — Compatible con Gradio 6.0
# =========================================================
import os
import gradio as gr
import requests
import base64
import json
from io import BytesIO
from PIL import Image
from openai import OpenAI
from dotenv import load_dotenv
import huggingface_hub
load_dotenv()
# --- CONFIGURACIÓN COMPLETA DE APIS ---
OPENAI_CLIENT = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
SAMBA_API_KEY = os.getenv("SAMBANOVA_API_KEY")
REVE_API_KEY = os.getenv("REVE_API_KEY")
HUGGINGFACE_TOKEN = os.getenv("HF_TOKEN")
# --- DICCIONARIO MAESTRO DE MODELOS ACTUALIZADO ---
MODELOS_UNIFICADOS = {
# === MODELOS LOCALES (Hugging Face) ===
"🧠 Mellum 4B Local": {"engine": "huggingface", "id": "JetBrains/Mellum-4b-sft-python"},
"🧠 Codestral 22B": {"engine": "huggingface", "id": "mistralai/Codestral-22B-v0.1"},
"💻 WizardCoder-V2": {"engine": "huggingface", "id": "WizardLM/WizardCoder-Python-34B-V1.0"},
"🐍 Llama 3.x Code": {"engine": "huggingface", "id": "meta-llama/Llama-3.2-11B-Vision-Instruct"},
# === SAMBANOVA (Alta Velocidad) ===
"💡 DeepSeek R1": {"engine": "samba", "id": "DeepSeek-R1"},
"💡 DeepSeek V3": {"engine": "samba", "id": "DeepSeek-V3"},
"💡 DeepSeek V3.1": {"engine": "samba", "id": "DeepSeek-V3.1"},
"💡 DeepSeek V3-0324": {"engine": "samba", "id": "DeepSeek-V3-0324"},
"🦙 Llama 3.1 8B": {"engine": "samba", "id": "Meta-Llama-3.1-8B-Instruct"},
"🦙 Llama 3.3 70B": {"engine": "samba", "id": "Meta-Llama-3.3-70B-Instruct"},
"🦙 Llama-4 Maverick 17B": {"engine": "samba", "id": "Llama-4-Maverick-17B-128E-Instruct"},
"🧩 Qwen3-32B": {"engine": "samba", "id": "Qwen3-32B"},
"🌀 GPT-OSS 120B": {"engine": "samba", "id": "gpt-oss-120b"},
"🌍 ALLaM-7B": {"engine": "samba", "id": "ALLaM-7B-Instruct-preview"},
# === GITHUB AI / PREMIUM ===
"🧬 GPT-5 Mini": {"engine": "github", "id": "openai/gpt-5-mini"},
"🧬 Grok 3": {"engine": "github", "id": "xai/grok-3"},
"🧬 Mistral Code": {"engine": "github", "id": "mistral-ai/Codestral-2501"},
# === OPENAI DIRECTO ===
"🪩 GPT-4.5 Omni": {"engine": "openai", "id": "gpt-4.5-turbo"},
"🪩 GPT-o1": {"engine": "openai", "id": "gpt-o1"},
"🪩 GPT-4o": {"engine": "openai", "id": "gpt-4o"},
"🪩 GPT-4o-mini": {"engine": "openai", "id": "gpt-4o-mini"},
# === GENERACIÓN VISUAL ===
"🎨 REVE CREATE (Imagen)": {"engine": "reve", "id": "reve-v1"},
"🎨 Stable Diffusion 3": {"engine": "huggingface", "id": "stabilityai/stable-diffusion-3.5-medium"},
# === MODELOS DE CÓDIGO ESPECIALIZADOS ===
"💾 CodeLlama 70B": {"engine": "samba", "id": "CodeLlama-70b"},
"🔧 DeepSeek Coder": {"engine": "samba", "id": "DeepSeek-Coder-V2"}
}
# --- LÓGICA MEJORADA PARA HUGGING FACE ---
def query_huggingface(model_id, prompt, max_length=500):
"""Consulta modelos de Hugging Face"""
API_URL = f"https://api-inference.huggingface.co/models/{model_id}"
headers = {"Authorization": f"Bearer {HUGGINGFACE_TOKEN}"}
payload = {
"inputs": prompt,
"parameters": {
"max_length": max_length,
"temperature": 0.7,
"top_p": 0.9
}
}
try:
response = requests.post(API_URL, headers=headers, json=payload, timeout=30)
response.raise_for_status()
result = response.json()
if isinstance(result, list) and len(result) > 0:
return result[0].get('generated_text', str(result))
return str(result)
except Exception as e:
return f"❌ Error con Hugging Face: {str(e)}"
# --- LÓGICA DE GENERACIÓN DE IMAGEN MEJORADA ---
def gen_image(prompt, engine="reve"):
"""Genera imágenes con múltiples proveedores"""
if engine == "reve":
url = "https://api.reve.com/v1/image/create"
headers = {"Authorization": f"Bearer {REVE_API_KEY}", "Content-Type": "application/json"}
payload = {
"prompt": prompt,
"aspect_ratio": "1:1",
"version": "latest",
"quality": "standard",
"guidance_scale": 7.5
}
try:
response = requests.post(url, headers=headers, json=payload, timeout=60)
if response.status_code == 200:
data = response.json()
if "image" in data:
img_data = base64.b64decode(data["image"])
return Image.open(BytesIO(img_data))
elif "image_url" in data:
img_resp = requests.get(data["image_url"])
return Image.open(BytesIO(img_resp.content))
except Exception as e:
print(f"Error REVE: {e}")
# Fallback a Hugging Face para generación de imágenes
elif engine == "huggingface":
API_URL = "https://api-inference.huggingface.co/models/stabilityai/stable-diffusion-3.5-medium"
headers = {"Authorization": f"Bearer {HUGGINGFACE_TOKEN}"}
try:
response = requests.post(API_URL, headers=headers, json={"inputs": prompt}, timeout=60)
if response.status_code == 200:
return Image.open(BytesIO(response.content))
except:
pass
return None
# --- LÓGICA DE CHAT UNIFICADA MEJORADA ---
def responder_orquesta(mensaje, historial, modelo_nombre):
"""Procesa mensajes con el modelo seleccionado"""
cfg = MODELOS_UNIFICADOS.get(modelo_nombre)
if not cfg:
return historial + [(mensaje, "❌ Modelo no encontrado")], None
# Caso 1: Generación de Imagen
if cfg["engine"] in ["reve", "huggingface"] and cfg["id"] in ["reve-v1", "stabilityai/stable-diffusion-3.5-medium"]:
img = gen_image(mensaje, engine=cfg["engine"])
if img:
historial.append((mensaje, f"🎨 **¡Aquí tienes tu creación, mi BATUTO!**\n\nModelo: {modelo_nombre}\n\n*Firma: BATUTO-ART*"))
return historial, img
return historial + [(mensaje, "❌ No se pudo generar la imagen. Intenta con otro prompt.")], None
# Prompt del sistema mejorado
prompt_sistema = """Eres BATUTO-ART, una IA con flow chilango de barrio pero conocimiento de nivel mundial.
Instrucciones:
1. Responde en español a menos que se pida otro idioma
2. Sé creativo pero preciso
3. Firma siempre como "BATUTO-ART" al final
4. Si es código, explica brevemente
5. Mantén un tono profesional pero con personalidad
Modelo actual: """ + modelo_nombre
try:
respuesta = ""
# SAMBANOVA
if cfg["engine"] == "samba":
client_samba = OpenAI(
api_key=SAMBA_API_KEY,
base_url="https://api.sambanova.ai/v1"
)
res = client_samba.chat.completions.create(
model=cfg["id"],
messages=[
{"role": "system", "content": prompt_sistema},
{"role": "user", "content": mensaje}
],
temperature=0.7,
max_tokens=1000
)
respuesta = res.choices[0].message.content
# HUGGING FACE (texto)
elif cfg["engine"] == "huggingface":
respuesta = query_huggingface(cfg["id"], f"{prompt_sistema}\n\nUsuario: {mensaje}")
# OPENAI / GITHUB AI
elif cfg["engine"] in ["openai", "github"]:
model_to_use = cfg["id"] if cfg["engine"] == "openai" else "gpt-4o"
res = OPENAI_CLIENT.chat.completions.create(
model=model_to_use,
messages=[
{"role": "system", "content": prompt_sistema},
{"role": "user", "content": mensaje}
],
temperature=0.7
)
respuesta = res.choices[0].message.content
else:
respuesta = "❌ Motor no soportado aún"
historial.append((mensaje, respuesta))
return historial, None
except Exception as e:
error_msg = f"❌ **Error de conexión con {modelo_nombre}:**\n\n`{str(e)}`\n\nIntenta con otro modelo o revisa tu conexión."
return historial + [(mensaje, error_msg)], None
# --- CSS ACTUALIZADO ---
CSS = """
.gradio-container {
background: linear-gradient(135deg, #0a0a0a 0%, #1a1a2e 50%, #16213e 100%) !important;
color: #e2e8f0 !important;
font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
}
.floating-eleven {
position: fixed;
bottom: 100px;
right: 20px;
z-index: 1000;
border: 2px solid #6366f1;
border-radius: 16px;
background: rgba(15, 23, 42, 0.9);
backdrop-filter: blur(10px);
padding: 10px;
box-shadow: 0 10px 30px rgba(99, 102, 241, 0.3);
}
.batuto-sig {
position: fixed;
top: 15px;
left: 15px;
font-family: 'Courier New', monospace;
color: #6366f1;
font-weight: 800;
font-size: 18px;
z-index: 1001;
background: linear-gradient(45deg, #6366f1, #8b5cf6, #ec4899);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.model-selector {
background: rgba(30, 41, 59, 0.8) !important;
border: 1px solid rgba(99, 102, 241, 0.3) !important;
color: #e2e8f0 !important;
border-radius: 12px !important;
}
.chatbot {
background: rgba(15, 23, 42, 0.7) !important;
border: 1px solid rgba(99, 102, 241, 0.2) !important;
border-radius: 16px !important;
backdrop-filter: blur(10px);
}
.textbox {
background: rgba(30, 41, 59, 0.8) !important;
border: 1px solid rgba(99, 102, 241, 0.3) !important;
border-radius: 12px !important;
color: #e2e8f0 !important;
}
.button {
background: linear-gradient(45deg, #6366f1, #8b5cf6) !important;
color: white !important;
border: none !important;
border-radius: 12px !important;
font-weight: 600 !important;
}
.button:hover {
background: linear-gradient(45deg, #8b5cf6, #ec4899) !important;
transform: translateY(-2px);
box-shadow: 0 5px 20px rgba(99, 102, 241, 0.4);
}
.image-display {
border-radius: 16px !important;
border: 2px solid rgba(99, 102, 241, 0.2) !important;
}
.markdown-text {
color: #e2e8f0 !important;
}
.contain {
contain: layout style paint;
}
"""
# --- CREACIÓN DE LA INTERFAZ ---
with gr.Blocks(title="BATUTOrquestaIA V3 - Multi-Model Fusion") as demo:
# Header fijo
gr.HTML("""
<div class='batuto-sig'>
<span style="font-size: 24px;">🎨</span> BATUTO-ART
<span style="font-size: 12px; opacity: 0.7; margin-left: 10px;">Multi-Model AI Platform</span>
</div>
""")
# Título principal
gr.Markdown("""
# 🎼 BATUTOrquestaIA V3
### *El Mero Mero Multimodal - Todos los modelos sin exclusión*
⚡ **Selecciona cualquier modelo de la lista y comienza a crear**
""")
with gr.Row():
# Panel de chat principal
with gr.Column(scale=3):
chatbot = gr.Chatbot(
label="Chat Multimodal",
height=650,
show_label=False,
container=True
)
with gr.Row():
user_input = gr.Textbox(
show_label=False,
placeholder="¿Qué vamos a crear hoy, mi BATUTO? Escribe tu prompt aquí...",
scale=8,
lines=2,
max_lines=5
)
submit_btn = gr.Button("🚀 Enviar", scale=1, variant="primary")
clear_btn = gr.Button("🧹 Limpiar", scale=1, variant="secondary")
# Panel lateral de control
with gr.Column(scale=1):
# Selector de modelos con grupos
gr.Markdown("### 🎯 **Selector de Modelos**")
with gr.Tabs():
with gr.TabItem("🤖 Todos"):
model_dropdown = gr.Dropdown(
choices=list(MODELOS_UNIFICADOS.keys()),
value="💡 DeepSeek R1",
label="",
interactive=True,
elem_classes="model-selector"
)
with gr.TabItem("🎨 Imagen"):
image_models = [k for k, v in MODELOS_UNIFICADOS.items()
if v["engine"] in ["reve", "huggingface"] and "reve-v1" in str(v.get("id", ""))]
gr.Dropdown(
choices=image_models,
value="🎨 REVE CREATE (Imagen)" if image_models else None,
label="Modelos de Imagen"
)
with gr.TabItem("💻 Código"):
code_models = [k for k, v in MODELOS_UNIFICADOS.items()
if "codestral" in k.lower() or "coder" in k.lower() or "wizard" in k.lower()]
gr.Dropdown(
choices=code_models,
label="Modelos de Programación"
)
# Estadísticas rápidas
gr.Markdown("---")
gr.Markdown("### 📊 **Estadísticas**")
model_count = gr.Markdown(f"**Modelos disponibles:** {len(MODELOS_UNIFICADOS)}")
last_update = gr.Markdown("**Última actualización:** Ahora mismo")
# Panel de imagen generada
gr.Markdown("---")
gr.Markdown("### 🖼️ **Galería de Imágenes**")
image_output = gr.Image(
label="Imagen Generada",
type="pil",
height=300,
show_label=False
)
# Botones de acción rápida
gr.Markdown("---")
gr.Markdown("### ⚡ **Acciones Rápidas**")
with gr.Row():
save_btn = gr.Button("💾 Guardar", scale=1)
share_btn = gr.Button("📤 Compartir", scale=1)
# Widget de ElevenLabs (solo uno)
gr.HTML("""
<div class="floating-eleven">
<h4 style="margin: 0 0 10px 0; color: #8b5cf6;">🎙️ Voice Assistant</h4>
<elevenlabs-convai agent-id="agent_1301kdwts7v9eszss3tkzm87kra3"></elevenlabs-convai>
<script src="https://unpkg.com/@elevenlabs/convai-widget-embed@beta" async></script>
<p style="font-size: 11px; margin: 5px 0 0 0; opacity: 0.7;">Powered by ElevenLabs</p>
</div>
""")
# Pie de página
gr.Markdown("---")
gr.Markdown("""
<div style="text-align: center; color: #94a3b8; font-size: 0.9em;">
<p>🎨 <strong>BATUTO-ART Platform</strong> | Multi-Model AI Fusion System</p>
<p>⚠️ <em>Algunos modelos pueden requerir API keys adicionales</em></p>
<p style="font-size: 0.8em;">v3.0 | Compatible con Gradio 6.0</p>
</div>
""")
# Funcionalidad
def process_input(mensaje, historial, modelo):
"""Envuelve la función principal para manejo de estado"""
return responder_orquesta(mensaje, historial, modelo)
submit_btn.click(
process_input,
inputs=[user_input, chatbot, model_dropdown],
outputs=[chatbot, image_output]
).then(lambda: "", outputs=[user_input])
user_input.submit(
process_input,
inputs=[user_input, chatbot, model_dropdown],
outputs=[chatbot, image_output]
).then(lambda: "", outputs=[user_input])
clear_btn.click(lambda: [], outputs=[chatbot])
# Función para guardar imagen
def save_image(image):
if image is not None:
import datetime
timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
filename = f"batuto_art_{timestamp}.png"
image.save(filename)
return f"💾 Imagen guardada como: {filename}"
return "❌ No hay imagen para guardar"
save_btn.click(save_image, inputs=[image_output], outputs=[gr.Textbox(visible=False)])
# --- EJECUCIÓN ---
if __name__ == "__main__":
print("🚀 Iniciando BATUTOrquestaIA V3...")
print(f"📊 Modelos cargados: {len(MODELOS_UNIFICADOS)}")
# Configuración para Hugging Face Spaces
demo.launch(
server_name="0.0.0.0",
server_port=7860,
share=False,
debug=True,
css=CSS,
theme=gr.themes.Soft()
)