BATUTO-ART commited on
Commit
98209a7
·
verified ·
1 Parent(s): 064f9e2

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +80 -250
app.py CHANGED
@@ -6,285 +6,115 @@ from PIL import Image
6
  import base64
7
  import io
8
  import requests
9
- from functools import lru_cache
10
- import torch
11
- # Importar la clase de excepción de la API de SambaNova si está disponible, si no, usar la base
12
- try:
13
- from sambanova.exceptions import APIError as SambaNovaAPIError
14
- except ImportError:
15
- # Usar una excepción genérica si la librería no está instalada o no la expone
16
- SambaNovaAPIError = Exception
17
 
18
- print("🚀 Iniciando aplicación...")
19
-
20
- # ------------ CONFIGURACIÓN SIMPLIFICADA ------------
21
  class Config:
22
  def __init__(self):
23
- # Buscar API keys en environment variables (secrets de Hugging Face)
24
  self.SAMBANOVA_API_KEY = os.getenv("SAMBANOVA_API_KEY")
25
  self.BRIA_API_TOKEN = os.getenv("BRIA_API_TOKEN")
26
- self.HUGGINGFACE_TOKEN = os.getenv("HUGGINGFACE_TOKEN")
27
-
28
- self.validate_keys()
29
 
30
- def validate_keys(self):
31
- if self.SAMBANOVA_API_KEY:
32
- print(f"✅ SAMBANOVA_API_KEY configurado (primeros 10 chars): {self.SAMBANOVA_API_KEY[:10]}...")
33
- else:
34
- print(" SAMBANOVA_API_KEY no encontrado")
35
- print("💡 Agrega el secret en: Settings → Repository secrets → SAMBANOVA_API_KEY")
36
-
37
- if self.BRIA_API_TOKEN:
38
- print("✅ BRIA_API_TOKEN configurado")
39
- else:
40
- print("⚠️ BRIA_API_TOKEN no configurado - Imagen deshabilitada")
41
 
42
  config = Config()
43
 
44
- # ------------ IMPORTACION CONDICIONAL CON MEJOR MANEJO DE ERRORES ------------
45
- SambaNova = None
46
- try:
47
- # Intenta importar la clase SambaNova
48
- from sambanova import SambaNova
49
- print("✅ SambaNova importado correctamente")
50
- except ImportError:
51
- print("⚠️ SambaNova no disponible - usando solo modelos locales")
52
- print("💡 Para instalar: pip install sambanova")
53
-
54
- # ------------ MODELOS INTEGRADOS ------------
55
- MODELS = {
56
- "chat": {
57
- "llama3": "NousResearch/Meta-Llama-3-8B-Instruct",
58
- "qwen": "Qwen/Qwen1.5-7B-Chat",
59
- "deepseek": "deepseek-ai/deepseek-llm-7b-chat",
60
- },
61
- "code": {
62
- "starcoder2": "bigcode/starcoder2-3b",
63
- "deepseek-coder": "deepseek-ai/deepseek-coder-1.3b-instruct"
64
- },
65
- "sambanova": {
66
- # 💡 Reemplaza con el nombre de modelo de SambaNova correcto.
67
- "chat_model": "Llama-3.3-Swallow-70B-Instruct-v0.4"
68
- }
69
- }
70
-
71
- # ------------ CARGA DE MODELOS SIMPLIFICADA ------------
72
- class ModelLoader:
73
- @lru_cache(maxsize=2)
74
- def load_hf_model(self, model_name: str):
75
  try:
76
- from transformers import AutoModelForCausalLM, AutoTokenizer
77
-
78
- print(f"📥 Cargando modelo: {model_name}")
79
-
80
- # Configuración simplificada para CPU
81
- tokenizer = AutoTokenizer.from_pretrained(model_name)
82
-
83
- # Nota: Si el modelo es muy grande para tu entorno, esta carga puede ser lenta o fallar.
84
- model = AutoModelForCausalLM.from_pretrained(
85
- model_name,
86
- device_map="auto",
87
- torch_dtype=torch.float32,
88
- low_cpu_mem_usage=True
89
- )
90
- model.eval()
91
- print(f"✅ Modelo {model_name} cargado exitosamente")
92
- return model, tokenizer
93
-
94
  except Exception as e:
95
- print(f"❌ Error cargando modelo {model_name}: {e}")
96
- return None, None
97
 
98
- model_loader = ModelLoader()
99
-
100
- # ------------ HERRAMIENTAS SIMPLIFICADAS ------------
101
  class AI_Tools:
102
  def __init__(self):
103
- self.sn_client = None
104
- if SambaNova is not None and config.SAMBANOVA_API_KEY:
105
- try:
106
- # 💡 VERIFICACIÓN CLAVE: Inicialización del cliente
107
- self.sn_client = SambaNova(api_key=config.SAMBANOVA_API_KEY)
108
- print("✅ Cliente SambaNova inicializado")
109
- except Exception as e:
110
- # Se imprime el error específico de la inicialización
111
- print(f"❌ Error inicializando SambaNova. Asegúrate que la clave es válida: {e}")
112
-
113
- async def generate_text(self, model_type: str, model_name: str, prompt: str) -> str:
114
- try:
115
- if model_type == "sambanova" and self.sn_client:
116
- return await self._generate_sambanova(model_name, prompt)
117
- else:
118
- return await self._generate_hf(model_name, prompt)
119
- except Exception as e:
120
- # Error de alto nivel
121
- return f"❌ Error general en la generación: {str(e)}"
122
 
123
- async def _generate_sambanova(self, model_name: str, prompt: str) -> str:
124
- # 💡 GUARDIA: Si por alguna razón el cliente falla después de la inicialización
125
- if not self.sn_client:
126
- return "❌ Cliente SambaNova no disponible (inicialización fallida)."
127
-
128
- try:
129
- # Usar run_in_executor para llamadas síncronas de la librería sambanova.
130
- response = await asyncio.get_event_loop().run_in_executor(
131
- None,
132
- # La llamada síncrona real
133
- lambda: self.sn_client.chat.completions.create(
134
- model=model_name,
135
- messages=[{"role": "user", "content": prompt}],
136
- temperature=0.7,
137
- max_tokens=500
138
- )
139
- )
140
- return response.choices[0].message.content
141
-
142
- # Captura de errores de la API de SambaNova (si es posible) o errores generales de conexión
143
- except SambaNovaAPIError as e:
144
- # 💡 MENSAJE CLARO DE FALLO: Si falla, el error aparecerá en el chat.
145
- return f"❌ Error de API de SambaNova. Verifica el nombre del modelo ({model_name}) y la clave API. Error: {str(e)}"
146
- except Exception as e:
147
- return f"❌ Error de conexión/ejecución SambaNova: {type(e).__name__} - {str(e)}"
148
 
149
- async def _generate_hf(self, model_name: str, prompt: str) -> str:
150
- try:
151
- model, tokenizer = model_loader.load_hf_model(model_name)
152
- if model is None:
153
- return "❌ Modelo local no disponible"
154
-
155
- inputs = tokenizer(prompt, return_tensors="pt")
156
-
157
- with torch.no_grad():
158
- outputs = model.generate(
159
- **inputs,
160
- max_new_tokens=256,
161
- temperature=0.7,
162
- do_sample=True,
163
- pad_token_id=tokenizer.eos_token_id
164
- )
165
-
166
- response = tokenizer.decode(outputs[0], skip_special_tokens=True)
167
- # Remover el prompt del response
168
- return response[len(prompt):].strip()
169
-
170
- except Exception as e:
171
- return f"❌ Error modelo local: {str(e)}"
172
-
173
- async def generate_image(self, prompt: str) -> Optional[Image.Image]:
174
  if not config.BRIA_API_TOKEN:
175
  return None
176
-
177
  try:
178
- url = "https://api.bria.ai/v1/generate"
179
- headers = {
180
- "Authorization": f"Bearer {config.BRIA_API_TOKEN}",
181
- "Content-Type": "application/json"
182
- }
183
- json_data = {
184
- "prompt": prompt,
185
- "options": {"resolution": "512x512"}
186
- }
187
-
188
- response = await asyncio.get_event_loop().run_in_executor(
189
- None,
190
- lambda: requests.post(url, headers=headers, json=json_data, timeout=30)
191
- )
192
-
193
  if response.status_code == 200:
194
- data = response.json()
195
- if "image_base64" in data:
196
- img_bytes = base64.b64decode(data["image_base64"])
197
  return Image.open(io.BytesIO(img_bytes))
198
-
199
- return None
200
  except Exception as e:
201
- print(f"⚠️ Error imagen: {e}")
202
- return None
203
- # ------------ INTERFAZ Y LÓGICA DE PROCESAMIENTO ------------
204
- tools = AI_Tools()
205
 
206
- async def process_input(message: str, image: Optional[Image.Image], history: list) -> list:
207
- try:
208
- msg_lower = message.lower()
209
-
210
- # Generación de imágenes
211
- if image or any(k in msg_lower for k in ["imagen", "genera imagen", "foto"]):
212
- img = await tools.generate_image(message)
213
- if img:
214
- return history + [(message, ("Imagen generada:", img))]
215
- return history + [(message, "❌ Imagen no disponible - BRIA no configurado")]
216
-
217
- # Generación de código
218
- elif any(k in msg_lower for k in ["código", "code", "programa"]):
219
- response = await tools.generate_text("hf", MODELS["code"]["deepseek-coder"], message)
220
- return history + [(message, f"```python\n{response}\n```")]
221
-
222
- # Chat normal
223
- else:
224
- # Intentar SambaNova primero
225
- if tools.sn_client:
226
- # 💡 USO DEL MODELO SAMBANOVA CONFIGURADO
227
- sn_model_name = MODELS["sambanova"]["chat_model"]
228
- response = await tools.generate_text("sambanova", sn_model_name, message)
229
- else:
230
- # Fallback a modelo local
231
- response = await tools.generate_text("hf", MODELS["chat"]["llama3"], message)
232
-
233
- return history + [(message, response)]
234
-
235
- except Exception as e:
236
- return history + [(message, f"❌ Error inesperado: {str(e)}")]
237
 
238
- # ------------ APLICACIÓN GRADIO ------------
239
- with gr.Blocks(title="AI Assistant", theme=gr.themes.Soft()) as app:
240
- gr.Markdown("# 🤖 AI Assistant")
241
-
242
- # El estado del cliente SN ahora reflejará si la inicialización fue exitosa o no
243
- status_text = "SambaNova: ✅ Conectado" if tools.sn_client else "SambaNova: ❌ No disponible (Verifica logs de inicialización)"
244
- gr.Markdown(f"**{status_text}** | **Modelos locales:** ✅ Disponibles")
245
-
246
- chatbot = gr.Chatbot(height=400)
247
 
248
- with gr.Row():
249
- msg = gr.Textbox(
250
- label="Mensaje",
251
- placeholder="Escribe tu pregunta...",
252
- scale=4
253
- )
254
- submit_btn = gr.Button("Enviar", variant="primary", scale=1)
255
 
256
- img_input = gr.Image(type="pil", label="Imagen (opcional)", height=150)
257
-
258
- # Ejemplos
259
- gr.Examples(
260
- examples=[
261
- "Explique la teoría de la relatividad",
262
- "Escribe una función Python para ordenar una lista",
263
- "Genera una imagen de un paisaje montañoso"
264
- ],
265
- inputs=msg
266
- )
267
 
268
- def clear_chat():
269
- return []
 
 
 
270
 
 
 
 
 
 
 
 
 
 
 
 
 
271
  clear_btn = gr.Button("Limpiar chat")
272
- clear_btn.click(clear_chat, outputs=chatbot)
273
 
274
- submit_btn.click(
275
- process_input,
276
- inputs=[msg, img_input, chatbot],
277
- outputs=chatbot
278
- )
279
-
280
- msg.submit(
281
- process_input,
282
- inputs=[msg, img_input, chatbot],
283
- outputs=chatbot
284
- )
285
 
286
  if __name__ == "__main__":
287
- # Asegúrate de ejecutarlo con 'python -m uvicorn tu_archivo:app --reload'
288
- # si estás usando Gradio en un entorno de servidor.
289
- app.launch(server_name="0.0.0.0", server_port=7860)
290
-
 
6
  import base64
7
  import io
8
  import requests
 
 
 
 
 
 
 
 
9
 
10
+ # --- Config ---
 
 
11
  class Config:
12
  def __init__(self):
 
13
  self.SAMBANOVA_API_KEY = os.getenv("SAMBANOVA_API_KEY")
14
  self.BRIA_API_TOKEN = os.getenv("BRIA_API_TOKEN")
15
+ self.validate()
 
 
16
 
17
+ def validate(self):
18
+ ok_samba = bool(self.SAMBANOVA_API_KEY)
19
+ ok_bria = bool(self.BRIA_API_TOKEN)
20
+ print(f"✅ SambaNova: {'Configurado' if ok_samba else 'Falta SAMBANOVA_API_KEY'}")
21
+ print(f" Bria: {'Configurado' if ok_bria else 'Falta BRIA_API_TOKEN'}")
 
 
 
 
 
 
22
 
23
  config = Config()
24
 
25
+ # --- Cliente SambaNova ---
26
+ class SambaNovaClient:
27
+ BASE_URL = "https://api.sambanova.ai/v1/chat/completions"
28
+
29
+ def __init__(self, api_key: str):
30
+ self.api_key = api_key
31
+
32
+ def generate(self, prompt: str, model: str = "Maverick") -> str:
33
+ headers = {
34
+ "Authorization": f"Bearer {self.api_key}",
35
+ "Content-Type": "application/json"
36
+ }
37
+ data = {
38
+ "model": model,
39
+ "messages": [{"role": "user", "content": prompt}],
40
+ "temperature": 0.7,
41
+ "max_tokens": 500
42
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
43
  try:
44
+ response = requests.post(self.BASE_URL, headers=headers, json=data, timeout=30)
45
+ response.raise_for_status()
46
+ return response.json()["choices"][0]["message"]["content"].strip()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47
  except Exception as e:
48
+ return f"❌ Error SambaNova: {str(e)}"
 
49
 
50
+ # --- Herramientas ---
 
 
51
  class AI_Tools:
52
  def __init__(self):
53
+ self.samba = SambaNovaClient(config.SAMBANOVA_API_KEY) if config.SAMBANOVA_API_KEY else None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
54
 
55
+ def generate_text(self, prompt: str) -> str:
56
+ if not self.samba:
57
+ return "❌ SAMBANOVA_API_KEY no configurada."
58
+ return self.samba.generate(prompt, model="Maverick")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
59
 
60
+ def generate_image(self, prompt: str) -> Optional[Image.Image]:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61
  if not config.BRIA_API_TOKEN:
62
  return None
 
63
  try:
64
+ url = "https://api.bria.ai/v1/generate" # ✅ sin espacio
65
+ headers = {"Authorization": f"Bearer {config.BRIA_API_TOKEN}"}
66
+ json_data = {"prompt": prompt, "options": {"resolution": "512x512"}}
67
+ response = requests.post(url, headers=headers, json=json_data, timeout=30)
 
 
 
 
 
 
 
 
 
 
 
68
  if response.status_code == 200:
69
+ img_b64 = response.json().get("image_base64")
70
+ if img_b64:
71
+ img_bytes = base64.b64decode(img_b64)
72
  return Image.open(io.BytesIO(img_bytes))
 
 
73
  except Exception as e:
74
+ print(f"⚠️ Error en Bria: {e}")
75
+ return None
 
 
76
 
77
+ # --- Lógica de procesamiento (síncrona para Gradio) ---
78
+ tools = AI_Tools()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79
 
80
+ def process_input(message: str, image: Optional[Image.Image], history: list) -> list:
81
+ msg_lower = message.lower()
 
 
 
 
 
 
 
82
 
83
+ # Imagen
84
+ if any(kw in msg_lower for kw in ["imagen", "foto", "genera imagen"]):
85
+ img = tools.generate_image(message)
86
+ if img:
87
+ return history + [(message, ("", img))]
88
+ return history + [(message, "❌ Imagen no generada (Bria no disponible o error).")]
 
89
 
90
+ # Texto (usa SambaNova/Maverick)
91
+ response = tools.generate_text(message)
92
+ return history + [(message, response)]
 
 
 
 
 
 
 
 
93
 
94
+ # --- Interfaz Gradio ---
95
+ with gr.Blocks(title="AI Assistant", theme=gr.themes.Soft()) as app:
96
+ gr.Markdown("# 🤖 AI Assistant (Maverick + Bria)")
97
+ status = "✅ SambaNova: Activo" if tools.samba else "❌ SambaNova: Inactivo"
98
+ gr.Markdown(f"**{status}** | **Bria:** {'✅ Activo' if config.BRIA_API_TOKEN else '❌ Inactivo'}")
99
 
100
+ chatbot = gr.Chatbot(height=450)
101
+ msg = gr.Textbox(label="Mensaje", placeholder="Ej: 'Genera una imagen de un gato en la luna' o 'Explica la fusión nuclear'")
102
+ img_input = gr.Image(type="pil", label="Imagen (opcional)", visible=False) # No usado realmente
103
+
104
+ gr.Examples([
105
+ "Escribe un poema sobre el otoño",
106
+ "Genera una imagen de un robot andando en bicicleta",
107
+ "¿Qué es la computación cuántica?"
108
+ ], inputs=msg)
109
+
110
+ def clear(): return []
111
+
112
  clear_btn = gr.Button("Limpiar chat")
113
+ clear_btn.click(clear, outputs=chatbot)
114
 
115
+ # ✅ Asíncrono en el fondo, pero interfaz síncrona (compatible con Gradio)
116
+ msg.submit(process_input, [msg, img_input, chatbot], chatbot, queue=True)
117
+ gr.Button("Enviar").click(process_input, [msg, img_input, chatbot], chatbot, queue=True)
 
 
 
 
 
 
 
 
118
 
119
  if __name__ == "__main__":
120
+ app.queue().launch(server_name="0.0.0.0", server_port=7860)