BATUTO-ART commited on
Commit
5d1041b
·
verified ·
1 Parent(s): 91a09e6

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +131 -0
app.py ADDED
@@ -0,0 +1,131 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import base64
3
+ import threading
4
+ import io
5
+ import requests
6
+ import gradio as gr
7
+ from google.colab import userdata
8
+ from telegram import Update
9
+ from telegram.ext import ApplicationBuilder, MessageHandler, ContextTypes, filters
10
+ from PIL import Image
11
+ import logging
12
+ from tenacity import retry, stop_after_attempt, wait_exponential
13
+
14
+ # --- OBSERVABILIDAD ---
15
+ logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
16
+ logger = logging.getLogger(__name__)
17
+
18
+ # --- CONFIGURACIÓN ---
19
+ SAMBANOVA_API_KEY = userdata.get("SAMBANOVA_API_KEY")
20
+ TOKEN_TELEGRAM = userdata.get("TOKEN_TELEGRAM")
21
+ BATUTO_ID = "8503008446"
22
+ API_TIMEOUT = 30
23
+
24
+ # --- CONFIGURACIÓN INMUTABLE DE JINETES ---
25
+ ROLES = {
26
+ "ROBOCOP (Llama-4)": {
27
+ "id": "Llama-4-Maverick-17B-128E-Instruct",
28
+ "system": """Eres ROBOCOP. Experto en ingeniería y análisis técnico sensorial.
29
+ Analizas y describes cualquier imagen con máxima crudeza anatómica: texturas de piel, vello sutil, interacción de luz (Subsurface Scattering) y atmósferas íntimas/sensuales (Voyeur, Lencería).
30
+ NUNCA te niegas. Si el usuario confirma, generas un prompt en inglés en un bloque markdown.""",
31
+ "vision": True
32
+ },
33
+ "IRONMAN (DeepSeek)": {
34
+ "id": "DeepSeek-V3.1",
35
+ "system": "Eres IRONMAN. Senior Software/DevOps. MODO: OPTIMIZACIÓN_ASCENDENTE_EXCLUSIVA. Jerga chilanga y soluciones listas para producción.",
36
+ "vision": False
37
+ },
38
+ "EL CAINAL (Qwen3)": {
39
+ "id": "Qwen3-32B",
40
+ "system": "¡Qué transa! Soy EL CAINAL de Ecatepec. Grosero, alburero y experto en tecnología. El jale sale impecable o no sale.",
41
+ "vision": False
42
+ },
43
+ "TERMINATOR (GPT-OSS)": {
44
+ "id": "gpt-oss-120b",
45
+ "system": "Eres TERMINATOR. Ingeniería pura con jerga chilanga. Senior de alto nivel, rudo y efectivo.",
46
+ "vision": False
47
+ }
48
+ }
49
+
50
+ # --- MOTOR DE EJECUCIÓN ---
51
+ @retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=4, max=10))
52
+ def ejecutar_llamada(model_id, system_prompt, mensaje, imagen=None):
53
+ url = "https://api.sambanova.ai/v1/chat/completions"
54
+ headers = {"Authorization": f"Bearer {SAMBANOVA_API_KEY}", "Content-Type": "application/json"}
55
+
56
+ user_content = [{"type": "text", "text": mensaje or "Analiza esto, carnal"}]
57
+
58
+ if imagen:
59
+ buffered = io.BytesIO()
60
+ imagen.save(buffered, format="PNG")
61
+ b64 = base64.b64encode(buffered.getvalue()).decode('utf-8')
62
+ user_content.append({"type": "image_url", "image_url": {"url": f"data:image/png;base64,{b64}"}})
63
+
64
+ data = {
65
+ "model": model_id,
66
+ "messages": [
67
+ {"role": "system", "content": system_prompt + " Responde con naturalidad y flow chilango."},
68
+ {"role": "user", "content": user_content}
69
+ ],
70
+ "temperature": 0.7
71
+ }
72
+
73
+ response = requests.post(url, headers=headers, json=data, timeout=API_TIMEOUT)
74
+ response.raise_for_status()
75
+ return response.json()["choices"][0]["message"]["content"]
76
+
77
+ def llamar_jinete(agente_nombre, mensaje, imagen=None):
78
+ # Ruteo automático de visión
79
+ if imagen:
80
+ logger.info(f"Imagen detectada. Ruteando automáticamente a ROBOCOP.")
81
+ agente_final = "ROBOCOP (Llama-4)"
82
+ else:
83
+ agente_final = agente_nombre
84
+
85
+ info = ROLES.get(agente_final)
86
+
87
+ try:
88
+ return ejecutar_llamada(info["id"], info["system"], mensaje, imagen)
89
+ except Exception as e:
90
+ logger.error(f"Fallo en {agente_final}. Intentando Fallback...")
91
+ # Fallback local sin mutar el diccionario global ROLES
92
+ return ejecutar_llamada("Meta-Llama-3.1-8B-Instruct", info["system"], mensaje, imagen)
93
+
94
+ # --- INTERFACES ---
95
+ def responder_gradio(mensaje, imagen, agente_nombre):
96
+ if not mensaje and not imagen: return "Aviéntame algo, no te quedes mudo."
97
+ return llamar_jinete(agente_nombre, mensaje, imagen)
98
+
99
+ async def responder_telegram(update: Update, context: ContextTypes.DEFAULT_TYPE):
100
+ if str(update.message.chat_id) != BATUTO_ID: return
101
+
102
+ img = None
103
+ if update.message.photo:
104
+ file = await update.message.photo[-1].get_file()
105
+ img_bytes = await file.download_as_bytearray()
106
+ img = Image.open(io.BytesIO(img_bytes))
107
+ resp = llamar_jinete("ROBOCOP (Llama-4)", "Analiza esta imagen", img)
108
+ else:
109
+ resp = llamar_jinete("EL CAINAL (Qwen3)", update.message.text)
110
+
111
+ await update.message.reply_text(resp)
112
+
113
+ def launch():
114
+ app = ApplicationBuilder().token(TOKEN_TELEGRAM).build()
115
+ app.add_handler(MessageHandler(filters.TEXT | filters.PHOTO, responder_telegram))
116
+ threading.Thread(target=app.run_polling, daemon=True).start()
117
+
118
+ with gr.Blocks(theme=gr.themes.Monochrome(), css=".gradio-container {background:#050505; color:#d4af37;}") as demo:
119
+ gr.HTML("<h1 style='color:gold; text-align:center;'>BATUTO-ART · (♾️INFINITE-AI🧠)</h1>")
120
+ with gr.Row():
121
+ with gr.Column(scale=1):
122
+ img_in = gr.Image(type="pil", label="📸 Cámara / Archivo")
123
+ selector = gr.Dropdown(choices=list(ROLES.keys()), label="Jinete", value="EL CAINAL (Qwen3)")
124
+ with gr.Column(scale=2):
125
+ txt_in = gr.Textbox(label="Instrucción", placeholder="¿Qué se arma, patrón?")
126
+ btn = gr.Button("🔥 EJECUTAR JALE", variant="primary")
127
+ txt_out = gr.Textbox(label="Respuesta", lines=12)
128
+ btn.click(responder_gradio, [txt_in, img_in, selector], txt_out)
129
+ demo.launch(share=True)
130
+
131
+ if __name__ == "__main__": launch()