BATUTO-ART commited on
Commit
bea6ca0
·
verified ·
1 Parent(s): 3f5d61a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +576 -107
app.py CHANGED
@@ -1,119 +1,588 @@
1
- import os
2
  import gradio as gr
3
- import asyncio
4
- import torch
 
 
5
  from PIL import Image
6
- from transformers import AutoTokenizer, AutoModelForCausalLM, pipeline
7
- from sambanova import SambaNova
8
- from functools import lru_cache
9
- from dotenv import load_dotenv
 
 
10
 
11
- # Cargar variables de entorno
12
- load_dotenv()
 
 
 
13
  SAMBANOVA_API_KEY = os.getenv("SAMBANOVA_API_KEY")
14
- HUGGINGFACE_TOKEN = os.getenv("HUGGINGFACE_TOKEN")
15
-
16
- if not SAMBANOVA_API_KEY:
17
- raise ValueError("❌ Falta SAMBANOVA_API_KEY en entorno")
18
-
19
- # Inicializar SambaNova client
20
- sn_client = SambaNova(api_key=SAMBANOVA_API_KEY, base_url="https://api.sambanova.ai/v1")
21
-
22
- # Modelos disponibles:
23
- MODELS = {
24
- "llama": "Llama-3.3-Swallow-70B-Instruct-v0.4",
25
- "qwen": "Qwen3-32B",
26
- "code": "bigcode/starcoder2-7b",
27
- "image": "runwayml/stable-diffusion-v1-5"
28
- }
29
-
30
- # Carga modelos Hugging Face para código y texto local con cache
31
- class ModelLoader:
32
- @lru_cache(maxsize=2)
33
- def load_hf_model(self, model_name):
34
- tokenizer = AutoTokenizer.from_pretrained(model_name, use_auth_token=HUGGINGFACE_TOKEN)
35
- model = AutoModelForCausalLM.from_pretrained(
36
- model_name,
37
- use_auth_token=HUGGINGFACE_TOKEN,
38
- device_map="auto" if torch.cuda.is_available() else {"": "cpu"},
39
- torch_dtype=torch.float16 if torch.cuda.is_available() else torch.float32,
40
- )
41
- model.eval()
42
- return model, tokenizer
43
-
44
- model_loader = ModelLoader()
45
-
46
- # Pipeline HF para generar imágenes
47
- image_pipe = pipeline(
48
- "text-to-image",
49
- model=MODELS["image"],
50
- use_auth_token=HUGGINGFACE_TOKEN,
51
- device=0 if torch.cuda.is_available() else -1
52
- )
53
-
54
- # Funciones async para generación
55
- async def generate_sambanova_text(prompt: str, model_name: str):
56
- loop = asyncio.get_event_loop()
57
- return await loop.run_in_executor(
58
- None,
59
- lambda: sn_client.chat.completions.create(
60
- model=model_name,
61
- messages=[{"role": "user", "content": prompt}],
62
- temperature=0.6,
63
- top_p=0.9,
64
- max_tokens=500
65
- ).choices[0].message.content
66
- )
67
-
68
- async def generate_code(prompt: str):
69
- model, tokenizer = model_loader.load_hf_model(MODELS["code"])
70
- inputs = tokenizer(prompt, return_tensors="pt").to(model.device)
71
- with torch.no_grad():
72
- outputs = model.generate(
73
- **inputs,
74
- max_new_tokens=256,
75
- temperature=0.4,
76
- pad_token_id=tokenizer.eos_token_id
77
- )
78
- return tokenizer.decode(outputs[0], skip_special_tokens=True)
79
 
80
- async def generate_image(prompt: str):
81
- loop = asyncio.get_event_loop()
82
- return await loop.run_in_executor(None, lambda: image_pipe(prompt)["images"][0])
 
 
 
 
 
 
 
 
 
83
 
84
- # Procesar entrada usuario
85
- async def process_input(message, image, history):
86
- text_lower = message.lower()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
87
  try:
88
- # Detectar petición de generación de imagen
89
- if any(w in text_lower for w in ["imagen", "foto", "pintura", "genera imagen"]):
90
- img = await generate_image(message)
91
- return history + [(message, ("🖼️ Imagen generada:", img))]
92
- # Detectar petición de código
93
- elif any(w in text_lower for w in ["código", "script", "programa", "python"]):
94
- code = await generate_code(message)
95
- return history + [(message, f"``````")]
96
- # Detectar modelo por keywords y generar texto en SambaNova
97
- elif "qwen" in text_lower:
98
- response = await generate_sambanova_text(message, MODELS["qwen"])
99
- return history + [(message, response)]
100
- else:
101
- response = await generate_sambanova_text(message, MODELS["llama"])
102
- return history + [(message, response)]
 
 
 
 
103
  except Exception as e:
104
- return history + [(message, f" Error: {str(e)}")]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
105
 
106
- # Interfaz Gradio
107
- with gr.Blocks(title="MultiModel AI Assistant") as app:
108
- chatbot = gr.Chatbot(height=500)
109
- msg = gr.Textbox(label="Mensaje")
110
- img_input = gr.Image(type="pil", label="Subir imagen (opcional)")
111
- submit_btn = gr.Button("Enviar")
112
 
113
- submit_btn.click(process_input, inputs=[msg, img_input, chatbot], outputs=chatbot)
114
- msg.submit(process_input, inputs=[msg, img_input, chatbot], outputs=chatbot)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
115
 
116
  if __name__ == "__main__":
117
- print("🚀 Aplicación iniciada en http://localhost:7860")
118
- app.launch(server_port=7860)
119
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import gradio as gr
2
+ import random
3
+ import os
4
+ import requests
5
+ import pyperclip
6
  from PIL import Image
7
+ from io import BytesIO
8
+ import concurrent.futures
9
+ import threading
10
+ from openai import OpenAI
11
+ import subprocess # Added this line
12
+ import sys # Added this line for sys.executable
13
 
14
+ # ============================================
15
+ # CONFIGURACIONES Y DATOS
16
+ # ============================================
17
+
18
+ # Configuración de APIs
19
  SAMBANOVA_API_KEY = os.getenv("SAMBANOVA_API_KEY")
20
+ client_sambanova = OpenAI(
21
+ api_key=SAMBANOVA_API_KEY,
22
+ base_url="https://api.sambanova.ai/v1"
23
+ ) if SAMBANOVA_API_KEY else None
24
+
25
+ # Datos para el generador automático de prompts
26
+ LINGERIE_VARIATIONS = [
27
+ "matching fiery red lace bra and thong set with thigh-high stockings",
28
+ "sheer midnight black lingerie ensemble with transparent lace bra",
29
+ "pure white silk babydoll with light transparent lace bra",
30
+ "vibrant emerald green lace corset top with plunging neckline",
31
+ "soft blush pink satin teddy with lace accents",
32
+ "deep navy blue mesh bodysuit with integrated lace thong and bra",
33
+ "delicate lavender floral lace set with balconette bra",
34
+ "bold scarlet red push-up bra with intricate lace overlays",
35
+ "opulent ivory sheer chemise with lace bra insert",
36
+ "sultry midnight black bustier cinched at the waist"
37
+ ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
 
39
+ BOUDOIR_POSES = [
40
+ "Standing with legs slightly apart, hand on hip, intense gaze directed at viewer",
41
+ "Sitting on edge of bed, one leg bent, leaning forward slightly",
42
+ "Leaning against wall, back arched, looking over shoulder",
43
+ "Kneeling on floor, hands on thighs, seductive smile",
44
+ "Lying on side, propped on elbow, legs slightly bent",
45
+ "Standing with one foot on chair, revealing lace thong",
46
+ "Bending forward slightly, hands on knees, playful expression",
47
+ "Twisting torso, looking back at camera with sultry look",
48
+ "Arms raised, stretching, showing silhouette",
49
+ "Sitting cross-legged, leaning back on hands"
50
+ ]
51
 
52
+ ANGLES = [
53
+ "Low-angle shot from floor looking up",
54
+ "Eye-level straight-on portrait",
55
+ "Three-quarter angle highlighting curves",
56
+ "Close-up from waist up with intimate framing",
57
+ "Full-body shot with slight downward tilt"
58
+ ]
59
+
60
+ ENVIRONMENTS = [
61
+ "luxury bedroom with soft ambient lighting",
62
+ "modern office after hours",
63
+ "chic hotel suite at golden hour",
64
+ "dimly lit lounge with moody atmosphere",
65
+ "elegant dressing room with vanity mirror"
66
+ ]
67
+
68
+ PROFESSIONAL_ROLES = [
69
+ "Executive Secretary", "Luxury Hotel Manager", "Fashion Boutique Owner",
70
+ "Corporate Lawyer", "Private Jet Attendant", "Art Gallery Curator",
71
+ "University Professor", "Ballet Instructor", "Yacht Stewardess",
72
+ "Professional Maid", "CEO", "Doctor", "Pilot", "Police Officer"
73
+ ]
74
+
75
+ # ============================================
76
+ # FUNCIONES REVE CREATE
77
+ # ============================================
78
+
79
+ def generate_single_reve_image(prompt, key, model, index, results_list, lock):
80
+ """Genera una sola imagen usando REVE API"""
81
  try:
82
+ url = "https://api.reveai.xyz/v1/images"
83
+ headers = {"Authorization": f"Bearer {key}"}
84
+ data = {"prompt": prompt, "model": model}
85
+ resp = requests.post(url, json=data, headers=headers, timeout=30)
86
+
87
+ if resp.status_code != 200:
88
+ return
89
+
90
+ img_url = resp.json().get("image")
91
+ if not img_url:
92
+ return
93
+
94
+ img_data = requests.get(img_url, timeout=30).content
95
+ img = Image.open(BytesIO(img_data))
96
+ out = f"/tmp/reve_{index}_{threading.current_thread().ident}.png"
97
+ img.save(out)
98
+
99
+ with lock:
100
+ results_list.append(out)
101
  except Exception as e:
102
+ print(f"Error generando imagen: {e}")
103
+
104
+ def reve_generate_multiple(prompt, key, model, num_images):
105
+ """Genera múltiples imágenes en paralelo"""
106
+ if not key:
107
+ return None
108
+
109
+ num_images = min(int(num_images), 8)
110
+ results = []
111
+ lock = threading.Lock()
112
+
113
+ with concurrent.futures.ThreadPoolExecutor(max_workers=min(num_images, 4)) as executor:
114
+ futures = [
115
+ executor.submit(
116
+ generate_single_reve_image,
117
+ prompt, key, model, i, results, lock
118
+ ) for i in range(num_images)
119
+ ]
120
+ concurrent.futures.wait(futures)
121
+
122
+ return results if results else None
123
+
124
+ # ============================================
125
+ # CHATBOT PARA PROMPTS SENSUALES
126
+ # ============================================
127
+
128
+ def create_sensual_prompt_system():
129
+ """Sistema para el chatbot especializado en prompts sensuales"""
130
+ return """Eres un chatbot especialista en generar prompts super sensuales en inglés.
131
+
132
+ INSTRUCCIONES:
133
+ 1. Responde naturalmente en español a conversaciones generales
134
+ 2. Cuando el usuario pida prompts sensuales, genera SOLO el prompt en inglés dentro de ``` ```
135
+ 3. Los prompts deben ser super sensuales, fotorealistas, insinuando una braguita de encaje bajo la vestimenta
136
+ 4. Estructura: Full-body 9:16, ángulos bajos, poses sugerentes, lencería de encaje, mirada intensa
137
+ 5. Ejemplo: "Photorealistic sensual portrait of [name], hinting at lace thong under dress..."
138
+ 6. Incluye siempre el formato: --ar 9:16 --v 6 --style raw
139
+ 7. Mantén respuestas concisas y enfocadas
140
+ """
141
+
142
+ def chat_sensual(message, chat_history):
143
+ """Función principal del chatbot"""
144
+ if not client_sambanova:
145
+ chat_history.append((message, "⚠️ API no configurada. Configura SAMBANOVA_API_KEY."))
146
+ return "", chat_history
147
+
148
+ messages = [{"role": "system", "content": create_sensual_prompt_system()}]
149
+
150
+ for user_msg, bot_msg in chat_history:
151
+ messages.append({"role": "user", "content": user_msg})
152
+ messages.append({"role": "assistant", "content": bot_msg})
153
+
154
+ messages.append({"role": "user", "content": message})
155
+
156
+ try:
157
+ response = client_sambanova.chat.completions.create(
158
+ model="Meta-Llama-3.1-8B-Instruct",
159
+ messages=messages,
160
+ temperature=0.8,
161
+ max_tokens=500
162
+ )
163
+ bot_response = response.choices[0].message.content.strip()
164
+ chat_history.append((message, bot_response))
165
+ except Exception as e:
166
+ chat_history.append((message, f"Error: {str(e)}"))
167
+
168
+ return "", chat_history
169
+
170
+ # ============================================
171
+ # GENERADOR AUTOMÁTICO DE PROMPTS
172
+ # ============================================
173
+
174
+ def generate_random_prompt(name, role, intensity, scene_type):
175
+ """Genera un prompt aleatorio completo"""
176
+
177
+ # Elementos aleatorios
178
+ lingerie = random.choice(LINGERIE_VARIATIONS)
179
+ pose = random.choice(BOUDOIR_POSES)
180
+ angle = random.choice(ANGLES)
181
+ environment = random.choice(ENVIRONMENTS)
182
+
183
+ # Determinar nivel de sensualidad
184
+ intensity_levels = [
185
+ ("subtly hinting at", "delicate", "gentle"),
186
+ ("suggestively revealing", "sensual", "provocative"),
187
+ ("boldly displaying", "explicit", "intense")
188
+ ]
189
+
190
+ level_idx = min(2, int(intensity / 33))
191
+ hint_word, style_word, lighting_word = intensity_levels[level_idx]
192
+
193
+ # Determinar si es celebridad o rol
194
+ if role == "Celebrity":
195
+ subject = name if name else "a beautiful woman"
196
+ outfit = f"elegant dress or professional attire"
197
+ else:
198
+ subject = f"{name} as a {random.choice(PROFESSIONAL_ROLES)}"
199
+ outfit = f"professional {random.choice(['dress', 'suit', 'uniform'])}"
200
+
201
+ # Determinar escena
202
+ if scene_type == "Professional":
203
+ environment = random.choice(["modern office", "boardroom", "luxury hotel", "art gallery"])
204
+ action = "working late or attending a high-profile meeting"
205
+ elif scene_type == "Casual":
206
+ environment = random.choice(["her apartment", "cafe", "park", "shopping mall"])
207
+ action = "relaxing or going about her day"
208
+ else: # Intimate
209
+ environment = random.choice(["bedroom", "dressing room", "bathroom", "private lounge"])
210
+ action = "getting ready or enjoying private moments"
211
+
212
+ # Construir el prompt
213
+ prompt = f"""Photorealistic 8K {style_word} portrait of {subject}, {hint_word} her {lingerie} under her {outfit}.
214
+
215
+ 📸 **Scene & Setting:**
216
+ - Location: {environment} with {lighting_word} lighting
217
+ - Action: {action}
218
+ - Pose: {pose}
219
+ - Camera Angle: {angle}
220
+ - Expression: Seductive gaze directly at viewer with slightly parted lips
221
+
222
+ 🎨 **Visual Details:**
223
+ - Lighting: Cinematic {lighting_word} lighting with soft shadows
224
+ - Focus: Hint of lace thong visible through or under clothing
225
+ - Textures: Hyper-detailed skin, fabric wrinkles, lace patterns
226
+ - Atmosphere: Intimate, sensual, photorealistic
227
+ - Style: Editorial fashion photography, intimate boudoir aesthetic
228
+
229
+ 🛠️ **Technical Specifications:**
230
+ - Aspect Ratio: 9:16 vertical
231
+ - Quality: 8K ultra-detailed, photorealistic
232
+ - Parameters: --ar 9:16 --v 6 --style raw --stylize 200
233
+ - Signature: Professional photography, no artifacts, perfect lighting"""
234
+
235
+ return prompt.strip()
236
+
237
+ def generate_auto_prompts(name, role_type, num_prompts, intensity, scene_type):
238
+ """Genera múltiples prompts automáticos"""
239
+ if not name.strip() and role_type == "Celebrity":
240
+ return ["⚠️ Por favor ingresa un nombre de celebridad"] * 5
241
+
242
+ prompts = []
243
+
244
+ for i in range(min(num_prompts, 5)):
245
+ prompt = generate_random_prompt(name, role_type, intensity, scene_type)
246
+ prompts.append(prompt)
247
+
248
+ # Rellenar si se piden menos de 5
249
+ while len(prompts) < 5:
250
+ prompts.append("")
251
+
252
+ return prompts
253
+
254
+ def copy_to_clipboard(text):
255
+ """Copia texto al portapapeles"""
256
+ if text and text.strip():
257
+ pyperclip.copy(text.strip())
258
+ return "✅ ¡Copiado al portapapeles!"
259
+ return ""
260
+
261
+ # ============================================
262
+ # INTERFAZ PRINCIPAL
263
+ # ============================================
264
+
265
+ with gr.Blocks(title="Sensual AI Studio", theme=gr.themes.Soft()) as demo:
266
+ gr.Markdown("# 🌹 Sensual AI Studio")
267
+ gr.Markdown("Generador especializado de prompts sensuales e imágenes")
268
+
269
+ # ========== TAB 1: Chatbot ==========
270
+ with gr.Tab("💬 Chatbot Sensual"):
271
+ gr.Markdown("### Chatbot especialista en prompts sensuales")
272
+ gr.Markdown("Habla naturalmente en español. Pide prompts y los recibirás en inglés listos para copiar.")
273
+
274
+ chatbot = gr.Chatbot(height=400, bubble_full_width=False)
275
+
276
+ with gr.Row():
277
+ msg = gr.Textbox(
278
+ placeholder="Ej: 'Genera un prompt super sensual para Emma Watson donde se insinúe su braguita de encaje'",
279
+ label="Tu mensaje",
280
+ scale=4
281
+ )
282
+ send_btn = gr.Button("Enviar", variant="primary", scale=1)
283
 
284
+ with gr.Row():
285
+ clear_btn = gr.Button("🧹 Limpiar chat")
286
+ example_btn = gr.Button("💡 Ver ejemplos")
 
 
 
287
 
288
+ # Ejemplos de prompts
289
+ examples = gr.Examples(
290
+ examples=[
291
+ ["Genera un prompt sensual para Scarlett Johansson como secretaria"],
292
+ ["Crea 3 prompts para una modelo con vestido negro y braguita de encaje"],
293
+ ["Prompt para Ana de Armas en escena íntima"],
294
+ ["Genera un prompt super sensual con ángulo bajo y mirada intensa"]
295
+ ],
296
+ inputs=[msg],
297
+ label="Ejemplos de solicitudes"
298
+ )
299
+
300
+ send_btn.click(chat_sensual, [msg, chatbot], [msg, chatbot])
301
+ clear_btn.click(lambda: [], None, chatbot)
302
+ example_btn.click(lambda: gr.Info("Revisa los ejemplos abajo 👇"), None, None)
303
+
304
+ # ========== TAB 2: Generador Automático ==========
305
+ with gr.Tab("🎲 Generador Automático"):
306
+ gr.Markdown("### Genera prompts sensuales automáticamente")
307
+ gr.Markdown("Configura los parámetros y genera prompts con randomización.")
308
+
309
+ with gr.Row():
310
+ with gr.Column(scale=1):
311
+ # Controles
312
+ name_input = gr.Textbox(
313
+ label="Nombre / Personaje",
314
+ placeholder="Ej: Emma Watson, Jennifer, Sophia",
315
+ max_lines=1
316
+ )
317
+
318
+ role_type = gr.Radio(
319
+ choices=["Celebrity", "Professional Role"],
320
+ value="Celebrity",
321
+ label="Tipo de personaje"
322
+ )
323
+
324
+ scene_type = gr.Dropdown(
325
+ choices=["Professional", "Casual", "Intimate"],
326
+ value="Intimate",
327
+ label="Tipo de escena"
328
+ )
329
+
330
+ num_prompts = gr.Slider(
331
+ minimum=1, maximum=5, step=1, value=3,
332
+ label="Número de Prompts"
333
+ )
334
+
335
+ intensity = gr.Slider(
336
+ minimum=1, maximum=100, value=65,
337
+ label="Intensidad Sensual",
338
+ info="1 = Sutil, 100 = Explícito"
339
+ )
340
+
341
+ generate_auto = gr.Button("✨ Generar Prompts", variant="primary")
342
+
343
+ # Información
344
+ gr.Markdown("""
345
+ ### 💡 Consejos:
346
+ - Usa nombres de celebridades reales
347
+ - Ajusta la intensidad según necesites
348
+ - Combina diferentes tipos de escena
349
+ - Los prompts están listos para Midjourney/DALL-E
350
+ """)
351
+
352
+ with gr.Column(scale=2):
353
+ # Output de prompts
354
+ prompt_outputs = []
355
+ copy_buttons = []
356
+
357
+ for i in range(5):
358
+ with gr.Group(visible=i < 3) as group:
359
+ gr.Markdown(f"### Prompt {i+1}")
360
+ prompt_box = gr.Textbox(
361
+ label="",
362
+ lines=8,
363
+ interactive=False,
364
+ show_copy_button=True
365
+ )
366
+ with gr.Row():
367
+ copy_btn = gr.Button(f"📋 Copiar Prompt {i+1}", size="sm", variant="secondary")
368
+ copy_status = gr.Textbox(
369
+ label="", interactive=False, visible=False, scale=4
370
+ )
371
+
372
+ prompt_outputs.append(prompt_box)
373
+ copy_buttons.append((copy_btn, copy_status))
374
+
375
+ # Funciones para mostrar/ocultar prompts
376
+ def update_visibility(num):
377
+ return [gr.update(visible=i < num) for i in range(5)]
378
+
379
+ # Generar prompts
380
+ generate_auto.click(
381
+ fn=generate_auto_prompts,
382
+ inputs=[name_input, role_type, num_prompts, intensity, scene_type],
383
+ outputs=prompt_outputs
384
+ ).then(
385
+ fn=update_visibility,
386
+ inputs=num_prompts,
387
+ outputs=[group for group in demo.children if isinstance(group, gr.Group)]
388
+ )
389
+
390
+ # Botones de copiar
391
+ for i, (copy_btn, copy_status) in enumerate(copy_buttons):
392
+ copy_btn.click(
393
+ fn=copy_to_clipboard,
394
+ inputs=prompt_outputs[i],
395
+ outputs=copy_status
396
+ ).then(
397
+ lambda i=i: gr.Info(f"Prompt {i+1} copiado al portapapeles!"),
398
+ outputs=None
399
+ )
400
+
401
+ # ========== TAB 3: Generador de Imágenes ==========
402
+ with gr.Tab("🖼️ REVE CREATE Images"):
403
+ gr.Markdown("### Generador de imágenes con REVE CREATE")
404
+ gr.Markdown("Pega cualquier prompt y genera imágenes sensuales de alta calidad.")
405
+
406
+ with gr.Row():
407
+ with gr.Column(scale=1):
408
+ reve_key = gr.Textbox(
409
+ label="🔑 REVE API Key",
410
+ type="password",
411
+ placeholder="Ingresa tu API key de REVE CREATE",
412
+ info="[Obtén tu key aquí](https://reveai.xyz)"
413
+ )
414
+
415
+ gr.Markdown("### Configuración")
416
+ reve_model = gr.Dropdown(
417
+ choices=["reve-1", "reve-2", "reve-fast"],
418
+ value="reve-fast",
419
+ label="Modelo"
420
+ )
421
+ num_imgs = gr.Slider(1, 8, value=4, step=1, label="Número de imágenes")
422
+
423
+ with gr.Column(scale=2):
424
+ reve_prompt = gr.Textbox(
425
+ label="📝 Prompt (en inglés)",
426
+ placeholder="Pega aquí tu prompt sensual...\n\nEj: Photorealistic sensual portrait of a woman, hinting at lace thong under her dress, looking directly at viewer with seductive gaze, low-angle shot, 8K ultra-detailed --ar 9:16",
427
+ lines=8
428
+ )
429
+
430
+ generate_reve = gr.Button("🎨 Generar Imágenes", variant="primary", size="lg")
431
+
432
+ gallery = gr.Gallery(
433
+ label="🖼️ Imágenes Generadas",
434
+ columns=4,
435
+ height="auto",
436
+ object_fit="contain"
437
+ )
438
+
439
+ # Función de generación
440
+ generate_reve.click(
441
+ fn=reve_generate_multiple,
442
+ inputs=[reve_prompt, reve_key, reve_model, num_imgs],
443
+ outputs=gallery
444
+ )
445
+
446
+ # ========== TAB 4: Instrucciones ==========
447
+ with gr.Tab("📚 Guía y Configuración"):
448
+ gr.Markdown("""
449
+ # 📖 Guía Completa de Uso
450
+
451
+ ## 🌟 Características Principales
452
+
453
+ ### 1. 💬 **Chatbot Sensual**
454
+ - Habla en español natural
455
+ - Genera prompts en inglés dentro de ``` ```
456
+ - Especializado en insinuar braguitas de encaje
457
+ - Prompts listos para Midjourney, DALL-E, Stable Diffusion
458
+
459
+ ### 2. 🎲 **Generador Automático**
460
+ - Randomización inteligente
461
+ - Control preciso de intensidad
462
+ - 3 tipos de escenas: Profesional, Casual, Íntima
463
+ - Botones de copiar con un clic
464
+
465
+ ### 3. 🖼️ **REVE CREATE Images**
466
+ - Generación de imágenes realistas
467
+ - Hasta 8 imágenes simultáneas
468
+ - Modelos optimizados para contenido sensual
469
+ - Galería integrada
470
+
471
+ ## 🔧 Configuración Necesaria
472
+
473
+ ### API Keys:
474
+ 1. **SAMBANOVA_API_KEY** (Opcional)
475
+ - Para el chatbot inteligente
476
+ - Si no la configuras, el chatbot mostrará mensaje de error
477
+
478
+ 2. **REVE API Key** (Requerido para imágenes)
479
+ - Regístrate en: https://reveai.xyz
480
+ - Obtén tu key gratuita
481
+ - Pégala en el campo correspondiente
482
+
483
+ ## 📝 Ejemplos de Prompts Efectivos
484
+
485
+ ### Para el Chatbot:
486
+ ```
487
+ "Genera un prompt super sensual para Margot Robbie donde se vea su braguita de encaje bajo el vestido"
488
+ "Crea 2 prompts para una secretaria con falda ajustada"
489
+ "Prompt íntimo para Gal Gadot en su habitación"
490
+ ```
491
+
492
+ ### Para el Generador Automático:
493
+ - Nombre: `Emma Watson`
494
+ - Tipo: `Celebrity`
495
+ - Escena: `Professional`
496
+ - Intensidad: `75`
497
+
498
+ ### Para REVE CREATE:
499
+ ```
500
+ Photorealistic sensual portrait of Emma Watson as a corporate lawyer,
501
+ hinting at black lace thong under her pencil skirt, looking directly
502
+ at viewer with confident seductive gaze, low-angle shot from floor,
503
+ professional office setting, cinematic lighting, 8K ultra-detailed --ar 9:16
504
+ ```
505
+
506
+ ## 🚀 Consejos Pro
507
+
508
+ 1. **Para mejores resultados:**
509
+ - Sé específico con poses y ángulos
510
+ - Usa adjetivos sensuales: "seductive", "alluring", "provocative"
511
+ - Incluye detalles de textura: "lace patterns", "silk fabric", "skin texture"
512
+
513
+ 2. **Optimización:**
514
+ - Siempre incluye `--ar 9:16` para formato vertical
515
+ - Usa `--v 6` para la última versión de Midjourney
516
+ - Agrega `--style raw` para mayor realismo
517
+
518
+ 3. **Ética y uso:**
519
+ - Usa para contenido artístico y creativo
520
+ - Respeta los derechos de imagen
521
+ - Genera contenido para adultos con responsabilidad
522
+
523
+ ## 🆘 Solución de Problemas
524
+
525
+ ### Chatbot no responde:
526
+ - Verifica tu SAMBANOVA_API_KEY
527
+ - Asegúrate de pedir prompts explícitamente
528
+
529
+ ### Imágenes no se generan:
530
+ - Revisa tu REVE API Key
531
+ - Verifica que el prompt esté en inglés
532
+ - Prueba con el modelo "reve-fast"
533
+
534
+ ### Prompts muy repetitivos:
535
+ - Cambia el tipo de escena
536
+ - Ajusta la intensidad
537
+ - Usa diferentes nombres/roles
538
+
539
+ ## 📞 Soporte
540
+ - Para problemas técnicos: Revisa la consola del navegador
541
+ - Para sugerencias: Modifica los arrays de datos en el código
542
+ - Para personalización: Edita las funciones de generación
543
+
544
+ ¡Disfruta creando contenido artístico y sensual! 🌹
545
+ """)
546
+
547
+ # ============================================
548
+ # EJECUCIÓN
549
+ # ============================================
550
 
551
  if __name__ == "__main__":
552
+ # Instalar dependencias si faltan
553
+ try:
554
+ import pyperclip
555
+ except ImportError:
556
+ import subprocess
557
+ import sys
558
+ subprocess.check_call([sys.executable, "-m", "pip", "install", "pyperclip"])
559
+
560
+ try:
561
+ from openai import OpenAI
562
+ except ImportError:
563
+ import subprocess
564
+ import sys
565
+ subprocess.check_call([sys.executable, "-m", "pip", "install", "openai"])
566
+
567
+ # Verificar dependencias
568
+ required = ["gradio", "requests", "PIL"]
569
+ for package in required:
570
+ try:
571
+ __import__(package.lower() if package == "PIL" else package)
572
+ except ImportError:
573
+ print(f"Instalando {package}...")
574
+ subprocess.check_call([sys.executable, "-m", "pip", "install",
575
+ "pillow" if package == "PIL" else package])
576
+
577
+ # Lanzar la aplicación
578
+ print("🚀 Iniciando Sensual AI Studio...")
579
+ print("🌐 URL: http://localhost:7860")
580
+ print("🔑 Recuerda configurar tus API keys si es necesario")
581
+
582
+ demo.launch(
583
+ server_name="0.0.0.0",
584
+ server_port=7860,
585
+ share=True,
586
+ debug=False,
587
+ show_error=True
588
+ )