BATUTO-ART commited on
Commit
a9b21d1
·
verified ·
1 Parent(s): 000b7bb

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +222 -174
app.py CHANGED
@@ -1,188 +1,236 @@
 
 
 
 
1
  import gradio as gr
2
- import torch
3
- from diffusers import FluxPipeline
4
- import random
5
- import json
6
  from PIL import Image
7
- import os
8
-
9
- HF_TOKEN = os.getenv("HF_TOKEN") # Toma tu token automáticamente
10
-
11
- # -------------------------------------------------------
12
- # Cargar modelo FLUX.2 (optimizado para CPU)
13
- # -------------------------------------------------------
14
- def load_flux_model():
15
- # Usar torch.float32 para CPU, ya que float16 puede no ser óptimo
16
- dtype = torch.float32
17
- pipe = FluxPipeline.from_pretrained(
18
- "black-forest-labs/FLUX.2-dev",
19
- torch_dtype=dtype,
20
- token=HF_TOKEN
21
- )
22
-
23
- # Forzar a CPU
24
- pipe.to("cpu")
25
- # Optimizar para CPU: habilitar offload secuencial para ahorrar memoria
26
- pipe.enable_sequential_cpu_offload()
27
- # Habilitar attention slicing para reducir uso de memoria
28
- pipe.enable_attention_slicing()
29
-
30
- return pipe
31
 
32
- pipe = load_flux_model()
33
-
34
- # -------------------------------------------------------
35
- # GENERACIÓN SIMPLE
36
- # -------------------------------------------------------
37
- def generate_flux_image(prompt, negative_prompt="", steps=28, guidance=7.0,
38
- width=576, height=1024, seed=None):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39
 
40
- if seed is None or seed == -1:
41
- seed = random.randint(0, 2**32 - 1)
42
 
43
- generator = torch.Generator("cpu").manual_seed(seed)
 
 
 
44
 
45
  try:
46
- image = pipe(
47
- prompt,
48
- num_inference_steps=steps,
49
- guidance_scale=guidance,
50
- height=height,
51
- width=width,
52
- generator=generator
53
- ).images[0]
54
-
55
- return image, seed
56
 
 
 
57
  except Exception as e:
58
- return None, f"Error: {str(e)}"
59
-
60
- # -------------------------------------------------------
61
- # GENERACIÓN JSON
62
- # -------------------------------------------------------
63
- def generate_json_image(scene, subjects, style, colors, lighting, mood,
64
- background, camera_angle, seed=None):
65
-
66
- json_prompt = {
67
- "scene": scene,
68
- "subjects": [{"description": subjects}],
69
- "style": style,
70
- "color_palette": [c.strip() for c in colors.split(",")] if colors else [],
71
- "lighting": lighting,
72
- "mood": mood,
73
- "background": background,
74
- "camera": {"angle": camera_angle}
75
- }
76
-
77
- prompt_text = json.dumps(json_prompt, ensure_ascii=False)
78
-
79
- return generate_flux_image(prompt_text, steps=25, seed=seed)
80
-
81
- def load_example(example_id):
82
- examples = {
83
- "producto": {
84
- "scene": "Fotografía profesional de producto en estudio",
85
- "subjects": "Taza de café minimalista con vapor ascendiendo",
86
- "style": "Fotografía de producto ultra realista",
87
- "colors": "#2C2C2C, #E8E8E8, #FF6B35",
88
- "lighting": "Iluminación suave de 3 puntos",
89
- "mood": "Limpio y profesional",
90
- "background": "Superficie de concreto pulido",
91
- "camera_angle": "ángulo alto"
92
- },
93
- "paisaje": {
94
- "scene": "Paisaje de montaña al atardecer",
95
- "subjects": "Lobo solitario en la cima de la montaña",
96
- "style": "Pintura digital épica",
97
- "colors": "#FF6B35, #1A535C, #4ECDC4",
98
- "lighting": "Luz dorada del atardecer",
99
- "mood": "Épico y sereno",
100
- "background": "Montañas y cielo naranja",
101
- "camera_angle": "vista panorámica"
102
- },
103
- "retrato": {
104
- "scene": "Estudio de retrato profesional",
105
- "subjects": "Persona con sonrisa genuina, iluminación dramática",
106
- "style": "Retrato fotográfico profesional",
107
- "colors": "#2C2C2C, #F5F5F5, #8B4513",
108
- "lighting": "Iluminación Rembrandt",
109
- "mood": "Elegante y confiado",
110
- "background": "Fondo negro mate",
111
- "camera_angle": "primer plano"
112
- }
113
- }
114
-
115
- return [examples[example_id][field] for field in [
116
- "scene", "subjects", "style", "colors", "lighting",
117
- "mood", "background", "camera_angle"
118
- ]]
119
-
120
- # -------------------------------------------------------
121
- # INTERFAZ GRADIO
122
- # -------------------------------------------------------
123
- with gr.Blocks(title="FLUX.2 - Generador de Imágenes") as demo:
124
 
125
- gr.Markdown("# 🎨 FLUX.2 - Generador de Imágenes (Optimizado para CPU, Formato 9:16)")
126
-
127
- # ----------------- TAB SIMPLE -----------------
128
- with gr.Tab("🎯 Prompt Simple"):
129
  with gr.Row():
130
- with gr.Column():
131
- prompt_simple = gr.Textbox(label="Prompt")
132
- negative_prompt = gr.Textbox(label="Negative Prompt (FLUX no lo usa)")
133
- steps = gr.Slider(10, 50, value=28, step=1, label="Steps")
134
- guidance = gr.Slider(1, 20, value=7, step=0.5, label="Guidance")
135
- width = gr.Slider(256, 1024, value=576, step=64, label="Ancho (9:16 por defecto)")
136
- height = gr.Slider(256, 1024, value=1024, step=64, label="Alto (9:16 por defecto)")
137
- seed = gr.Number(value=-1, label="Semilla")
138
- btn_simple = gr.Button("Generar Imagen")
139
 
140
- with gr.Column():
141
- out_img_simple = gr.Image(label="Imagen Generada")
142
- out_seed_simple = gr.Number(label="Semilla Usada")
143
-
144
- # ----------------- TAB JSON -----------------
145
- with gr.Tab("📝 JSON Prompt"):
146
- scene = gr.Textbox(label="Escena")
147
- subjects = gr.Textbox(label="Sujetos")
148
- style = gr.Textbox(label="Estilo")
149
- colors = gr.Textbox(label="Colores (coma)")
150
- lighting = gr.Textbox(label="Iluminación")
151
- mood = gr.Textbox(label="Estado de Ánimo")
152
- background = gr.Textbox(label="Fondo")
153
- camera_angle = gr.Textbox(label="Ángulo de cámara")
154
- json_seed = gr.Number(value=-1, label="Semilla")
155
- btn_json = gr.Button("Generar con JSON")
156
-
157
- out_img_json = gr.Image()
158
- out_seed_json = gr.Number()
159
 
160
  with gr.Row():
161
- gr.Button("Ej Producto").click(
162
- fn=lambda: load_example("producto"),
163
- outputs=[scene, subjects, style, colors, lighting, mood, background, camera_angle]
164
- )
165
- gr.Button("Ej Paisaje").click(
166
- fn=lambda: load_example("paisaje"),
167
- outputs=[scene, subjects, style, colors, lighting, mood, background, camera_angle]
168
- )
169
- gr.Button("Ej Retrato").click(
170
- fn=lambda: load_example("retrato"),
171
- outputs=[scene, subjects, style, colors, lighting, mood, background, camera_angle]
172
- )
173
-
174
- # Eventos
175
- btn_simple.click(
176
- fn=generate_flux_image,
177
- inputs=[prompt_simple, negative_prompt, steps, guidance, width, height, seed],
178
- outputs=[out_img_simple, out_seed_simple]
179
- )
180
-
181
- btn_json.click(
182
- fn=generate_json_image,
183
- inputs=[scene, subjects, style, colors, lighting, mood, background, camera_angle, json_seed],
184
- outputs=[out_img_json, out_seed_json]
185
- )
186
-
187
- # Lanzar la demo (para ejecución local o en CPU)
188
- demo.launch()
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import base64
3
+ import requests
4
+ import io
5
  import gradio as gr
 
 
 
 
6
  from PIL import Image
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
 
8
+ # ===========================
9
+ # CLAVES PARA SAMBANOVA
10
+ # ===========================
11
+ API_KEY = os.getenv("REVE_API_KEY")
12
+ BASE_URL = "https://api.sambanova.ai/v1"
13
+
14
+ if not API_KEY:
15
+ raise ValueError("⚠️ ERROR: No se encontró la variable REVE_API_KEY en HuggingFace Spaces.")
16
+
17
+ # ===========================
18
+ # DICCIONARIO DE MODELOS
19
+ # ===========================
20
+ MODELS = {
21
+ "general_fast": {
22
+ "name": "Meta-Llama-3.1-8B-Instruct",
23
+ "role": "🔄 Respuestas rápidas y generales",
24
+ "description": "Conversación ligera y eficiente."
25
+ },
26
+ "general_smart": {
27
+ "name": "Meta-Llama-3.3-70B-Instruct",
28
+ "role": "🧠 Razonamiento profundo",
29
+ "description": "Análisis detallado y avanzado."
30
+ },
31
+ "coding_expert": {
32
+ "name": "DeepSeek-V3.1",
33
+ "role": "💻 Programación y debugging",
34
+ "description": "Ideal para desarrollo."
35
+ },
36
+ "coding_alt": {
37
+ "name": "DeepSeek-V3-0324",
38
+ "role": "⚡ Código rápido",
39
+ "description": "Alternativa veloz."
40
+ },
41
+ "massive_brain": {
42
+ "name": "gpt-oss-120b",
43
+ "role": "🏛️ Sabiduría masiva",
44
+ "description": "Problemas pesados y complejos."
45
+ },
46
+ "specialized_1": {
47
+ "name": "DeepSeek-V3.1-Terminus",
48
+ "role": "🎯 Especialista técnico",
49
+ "description": "Tareas científicas y avanzadas."
50
+ },
51
+ "specialized_2": {
52
+ "name": "Llama-3.3-Swallow-70B-Instruct-v0.4",
53
+ "role": "🔥 Sin censura",
54
+ "description": "Modelo sin restricciones."
55
+ },
56
+ "multilingual": {
57
+ "name": "Qwen3-32B",
58
+ "role": "🌍 Multilingüe",
59
+ "description": "Múltiples idiomas."
60
+ },
61
+ "arabic_special": {
62
+ "name": "ALLaM-7B-Instruct-preview",
63
+ "role": "🕌 Estilo árabe y moda íntima",
64
+ "description": "Experto en estilos del Medio Oriente."
65
+ },
66
+ "vision_expert": {
67
+ "name": "Llama-4-Maverick-17B-128E-Instruct",
68
+ "role": "👁️ Visión avanzada",
69
+ "description": "Análisis de imágenes."
70
+ }
71
+ }
72
+
73
+ # ===========================
74
+ # CSS PERSONALIZADO
75
+ # ===========================
76
+ custom_css = """
77
+ .gradio-container {max-width: 900px !important; margin: auto;}
78
+ .model-box {padding: 10px; border-radius: 10px; background: #1d1d1d; margin-bottom: 6px;}
79
+ """
80
+
81
+ # ===========================
82
+ # CODIFICAR IMAGEN EN BASE64
83
+ # ===========================
84
+ def encode_image(img):
85
+ if img is None:
86
+ return None
87
+ buffer = io.BytesIO()
88
+ img.save(buffer, format="PNG")
89
+ return base64.b64encode(buffer.getvalue()).decode()
90
+
91
+ # ===========================
92
+ # API CALL SAMBANOVA
93
+ # ===========================
94
+ def call_sambanova(model, messages, images=None):
95
+ url = f"{BASE_URL}/chat/completions"
96
+
97
+ payload = {
98
+ "model": model,
99
+ "messages": messages,
100
+ "stream": False
101
+ }
102
 
103
+ if images:
104
+ payload["images"] = images
105
 
106
+ headers = {
107
+ "Authorization": f"Bearer {API_KEY}",
108
+ "Content-Type": "application/json"
109
+ }
110
 
111
  try:
112
+ response = requests.post(url, json=payload, headers=headers, timeout=30)
113
+
114
+ if response.status_code != 200:
115
+ return f"⚠️ Error en API: {response.status_code}\n{response.text}"
 
 
 
 
 
 
116
 
117
+ data = response.json()
118
+ return data["choices"][0]["message"]["content"]
119
  except Exception as e:
120
+ return f"⚠️ Error de conexión: {str(e)}"
121
+
122
+ # ===========================
123
+ # CHAT PRINCIPAL
124
+ # ===========================
125
+ def chat_logic(user_text, user_image, model_selection, history):
126
+ if not user_text and not user_image:
127
+ return history, "⚠️ Por favor, escribe un mensaje o sube una imagen."
128
+
129
+ selected = MODELS[model_selection]
130
+ model_name = selected["name"]
131
+
132
+ messages = []
133
+ images_encoded = None
134
+
135
+ # Add system message
136
+ messages.append({"role": "system", "content": selected["role"]})
137
+
138
+ # Handle image and text
139
+ if user_image:
140
+ images_encoded = [encode_image(user_image)]
141
+ messages.append({
142
+ "role": "user",
143
+ "content": [
144
+ {"type": "text", "text": user_text or "Describe esta imagen."},
145
+ {"type": "image", "image": images_encoded[0]}
146
+ ]
147
+ })
148
+ else:
149
+ messages.append({"role": "user", "content": user_text})
150
+
151
+ # Call API
152
+ reply = call_sambanova(model_name, messages, images_encoded)
153
+
154
+ # Update history
155
+ user_display = f"🧍 Usuario: {user_text}" if user_text else "🧍 Usuario: [Imagen]"
156
+ history.append((user_display, f"🤖 {selected['role']}: {reply}"))
157
+
158
+ return history, reply
159
+
160
+ # ===========================
161
+ # INTERFAZ MEJORADA
162
+ # ===========================
163
+ def create_ui():
164
+ with gr.Blocks(css=custom_css, title="Chat Multimodal SambaNova") as demo:
165
+ gr.Markdown("""
166
+ # 🤖 Chat avanzado con modelos SambaNova
167
+ *Soporte multimodal (texto + imágenes) con 10+ modelos especializados*
168
+ """)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
169
 
 
 
 
 
170
  with gr.Row():
171
+ with gr.Column(scale=1):
172
+ model_selection = gr.Dropdown(
173
+ choices=list(MODELS.keys()),
174
+ value="general_fast",
175
+ label="🎛️ Selecciona modelo",
176
+ info="Elige el modelo según tus necesidades"
177
+ )
178
+ # Display model description
179
+ model_info = gr.Markdown("**Meta-Llama-3.1-8B-Instruct**: Conversación ligera y eficiente.")
180
 
181
+ with gr.Row():
182
+ with gr.Column(scale=1):
183
+ user_image = gr.Image(
184
+ label="🖼️ Sube una imagen (opcional)",
185
+ type="pil",
186
+ height=200
187
+ )
188
+ with gr.Column(scale=2):
189
+ user_text = gr.Textbox(
190
+ label="💬 Escribe tu mensaje",
191
+ placeholder="Escribe tu pregunta aquí...",
192
+ lines=3
193
+ )
194
+
195
+ send_btn = gr.Button("🚀 Enviar mensaje", variant="primary")
 
 
 
 
196
 
197
  with gr.Row():
198
+ with gr.Column():
199
+ chatbox = gr.Chatbot(
200
+ label="💭 Conversación",
201
+ height=400
202
+ )
203
+
204
+ # Update model info when selection changes
205
+ def update_model_info(selection):
206
+ selected = MODELS[selection]
207
+ return f"**{selected['name']}**: {selected['description']}"
208
+
209
+ model_selection.change(
210
+ update_model_info,
211
+ inputs=[model_selection],
212
+ outputs=[model_info]
213
+ )
214
+
215
+ # Clear inputs after sending
216
+ def clear_inputs():
217
+ return None, ""
218
+
219
+ send_btn.click(
220
+ chat_logic,
221
+ inputs=[user_text, user_image, model_selection, chatbox],
222
+ outputs=[chatbox, user_text]
223
+ ).then(
224
+ clear_inputs,
225
+ outputs=[user_image, user_text]
226
+ )
227
+
228
+ return demo
229
+
230
+ # ===========================
231
+ # LANZAR INTERFAZ
232
+ # ===========================
233
+ demo = create_ui()
234
+
235
+ if __name__ == "__main__":
236
+ demo.launch(share=False)