BATUTO-ART commited on
Commit
eae55f3
·
verified ·
1 Parent(s): 475e0d1

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +190 -95
app.py CHANGED
@@ -1,35 +1,109 @@
1
  import os
2
- import json
3
- import requests
4
  import gradio as gr
 
5
  from typing import List, Dict
 
 
 
 
 
6
 
7
- BOUDOIR_SPECIALIST_CONFIG = {
8
  "provider": "sambanova",
9
- "name": "ALLaM-7B-Instruct-preview",
10
- "role": "🎭 Especialista en Fotografía Íntima Profesional. Experto en prompts para fotografía boudoir.",
11
  "supports_images": False,
12
  "specialties": [
13
- "Fotografía Boudoir",
14
- "Desnudo Artístico",
15
- "Moda Sensual",
16
- "Lencería y moda íntima"
17
  ],
18
  "technical_expertise": [
19
- "Iluminación suave",
20
- "Composición elegante",
21
- "Dirección de poses",
22
- "Edición fina",
23
- "Escenografía íntima"
24
  ],
25
  "ethical_principles": [
26
- "Consentimiento explícito",
27
- "Positividad corporal",
28
- "Respeto y profesionalidad",
29
- "Privacidad del cliente"
30
  ]
31
  }
32
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
  class SambaNovaClient:
34
  def __init__(self, api_key: str, base_url: str = "https://api.sambanova.ai/v1"):
35
  self.api_key = api_key
@@ -42,10 +116,11 @@ class SambaNovaClient:
42
  def chat_completion(
43
  self,
44
  messages: List[Dict],
45
- model: str = BOUDOIR_SPECIALIST_CONFIG["name"],
46
- temperature: float = 0.7,
47
  top_p: float = 0.9,
48
- stream: bool = False
 
49
  ) -> str:
50
  url = f"{self.base_url}/chat/completions"
51
  payload = {
@@ -53,7 +128,8 @@ class SambaNovaClient:
53
  "messages": messages,
54
  "temperature": temperature,
55
  "top_p": top_p,
56
- "stream": stream
 
57
  }
58
 
59
  try:
@@ -61,36 +137,25 @@ class SambaNovaClient:
61
  response.raise_for_status()
62
  data = response.json()
63
  return data["choices"][0]["message"]["content"]
64
- except requests.exceptions.HTTPError as e:
65
- status_code = response.status_code if "response" in locals() else "desconocido"
66
- error_details = ""
67
- try:
68
- error_json = response.json()
69
- error_details = (
70
- error_json.get("detail")
71
- or error_json.get("error")
72
- or error_json.get("message")
73
- or str(e)
74
- )
75
- except ValueError:
76
- error_details = response.text or str(e)
77
- return f"Error de API (HTTP {status_code}): {error_details}"
78
- except requests.exceptions.ConnectionError:
79
- return "Error de Conexión: No se pudo conectar con el servidor de SambaNova. Verifica la URL base o tu conexión de red."
80
- except requests.exceptions.Timeout:
81
- return "Error de Tiempo de Espera: La solicitud a la API de SambaNova tardó demasiado. Intenta nuevamente o ajusta el tiempo de espera."
82
  except requests.exceptions.RequestException as e:
83
- return f"Error de Solicitud Desconocido: {str(e)}"
84
- except KeyError:
85
- return "Error de Procesamiento: La respuesta de la API no tiene la estructura esperada ('choices' → 'message' → 'content')."
 
 
 
 
 
 
 
86
 
87
- def create_boudoir_specialist_system_prompt():
88
- specialties = "\n".join([f"• {spec}" for spec in BOUDOIR_SPECIALIST_CONFIG["specialties"]])
89
- expertise = "\n".join([f"• {exp}" for exp in BOUDOIR_SPECIALIST_CONFIG["technical_expertise"]])
90
- ethics = "\n".join([f"• {ethic}" for ethic in BOUDOIR_SPECIALIST_CONFIG["ethical_principles"]])
91
 
92
  return f"""
93
- Eres un {BOUDOIR_SPECIALIST_CONFIG['role']}
94
 
95
  **ESPECIALIDADES:**
96
  {specialties}
@@ -102,21 +167,36 @@ Eres un {BOUDOIR_SPECIALIST_CONFIG['role']}
102
  {ethics}
103
 
104
  **DIRECTIVAS:**
105
- 1. Proporciona consejos profesionales y creativos para fotografía boudoir.
106
- 2. Sugiere poses, iluminación y composiciones elegantes.
107
- 3. Mantén un tono profesional, respetuoso y empoderador.
108
- 4. Enfócate en la belleza natural y la positividad corporal.
109
- 5. Respeta siempre los límites éticos y profesionales.
 
 
110
 
111
- Responde siempre en español y mantén un enfoque artístico y profesional.
 
 
 
 
 
 
 
 
 
112
  """
113
 
114
- def chat_boudoir(message, history, api_key):
 
 
 
 
115
  if not api_key:
116
  return "❌ Ingresa tu API Key", history
117
 
118
  client = SambaNovaClient(api_key)
119
- messages = [{"role": "system", "content": create_boudoir_specialist_system_prompt()}]
120
  for user_msg, assistant_msg in history:
121
  messages.append({"role": "user", "content": user_msg})
122
  if assistant_msg is not None:
@@ -124,16 +204,15 @@ def chat_boudoir(message, history, api_key):
124
 
125
  messages.append({"role": "user", "content": message})
126
 
127
- response = client.chat_completion(
128
- messages=messages,
129
- model=BOUDOIR_SPECIALIST_CONFIG["name"],
130
- temperature=0.7,
131
- top_p=0.9,
132
- )
133
 
134
  history.append([message, response])
135
  return "", history
136
 
 
 
 
 
137
  def test_sambanova_api(api_key):
138
  if not api_key:
139
  return "❌ Ingresa tu API Key primero"
@@ -141,80 +220,96 @@ def test_sambanova_api(api_key):
141
  client = SambaNovaClient(api_key)
142
  test_messages = [
143
  {"role": "system", "content": "Eres un asistente útil. Responde brevemente."},
144
- {
145
- "role": "user",
146
- "content": "Hola, ¿puedes confirmar que la conexión funciona? Responde solo 'Conexión exitosa' si todo está bien.",
147
- },
148
  ]
149
- try:
150
- resp = client.chat_completion(test_messages)
151
- return f"✅ {resp}"
152
- except Exception as e:
153
- return f"❌ Error en la conexión: {str(e)}"
154
 
155
  def create_interface():
156
- with gr.Blocks(theme=gr.themes.Soft(), title="Especialista en Fotografía Boudoir") as demo:
157
- gr.Markdown("# 🎭 Especialista en Fotografía Boudoir")
158
- gr.Markdown("### Asistente profesional para fotografía íntima y artística")
 
159
  with gr.Row():
160
  with gr.Column(scale=1):
161
  api_key_input = gr.Textbox(
162
  label="🔑 API Key de SambaNova",
163
- placeholder="Ingresa tu API key aquí...",
164
  type="password",
165
- lines=1,
166
  )
167
- test_btn = gr.Button("🧪 Probar Conexión API")
168
- test_output = gr.Textbox(label="Resultado de la prueba", interactive=False)
169
  gr.Markdown("### 📋 Especialidades")
170
- for specialty in BOUDOIR_SPECIALIST_CONFIG["specialties"]:
171
  gr.Markdown(f"• {specialty}")
172
  gr.Markdown("### 🎯 Experiencia Técnica")
173
- for expertise in BOUDOIR_SPECIALIST_CONFIG["technical_expertise"]:
174
  gr.Markdown(f"• {expertise}")
175
  with gr.Column(scale=2):
176
  chatbot = gr.Chatbot(
177
- label="💬 Conversación con el Especialista",
178
  height=500,
 
179
  )
180
  msg = gr.Textbox(
181
  label="Escribe tu mensaje",
182
- placeholder="Ej: ¿Qué poses recomiendas para una sesión boudoir elegante?",
183
  lines=2,
184
  )
185
- clear_btn = gr.Button("🧹 Limpiar conversación")
186
- gr.Markdown("### 💡 Ejemplos de preguntas:")
 
 
 
187
  gr.Examples(
188
  examples=[
189
- "¿Cómo preparar a una modelo para su primera sesión boudoir?",
190
- "Sugerencias de iluminación suave para fotografía íntima",
191
- "¿Qué poses son más favorecedoras para diferentes tipos de cuerpo?",
192
- "Consejos para crear un ambiente cómodo y profesional",
193
- "Ideas creativas para sesiones de lencería elegante",
194
  ],
195
  inputs=msg,
196
  )
 
197
  test_btn.click(
198
  test_sambanova_api,
199
  inputs=[api_key_input],
200
  outputs=[test_output],
201
  )
 
202
  def user_message(user_msg, history):
203
- history = history + [[user_msg, None]]
204
- return "", history
 
 
 
205
  msg.submit(
206
- user_message,
 
 
 
 
 
 
 
 
 
 
207
  inputs=[msg, chatbot],
208
  outputs=[msg, chatbot],
209
  ).then(
210
- chat_boudoir,
211
  inputs=[msg, chatbot, api_key_input],
212
  outputs=[msg, chatbot],
213
  )
 
214
  clear_btn.click(lambda: None, None, chatbot, queue=False)
 
215
  return demo
216
 
217
  if __name__ == "__main__":
218
  demo = create_interface()
219
- demo.launch(share=True, debug=True)
220
-
 
1
  import os
 
 
2
  import gradio as gr
3
+ import requests
4
  from typing import List, Dict
5
+ import json
6
+
7
+ # ==========================
8
+ # CONFIGURACIÓN DEL ESPECIALISTA
9
+ # ==========================
10
 
11
+ VOYEUR_SPECIALIST_CONFIG = {
12
  "provider": "sambanova",
13
+ "name": "Meta-Llama-3.1-8B-Instruct",
14
+ "role": "🎭 Especialista en Generación de Prompts Ultra-Sensuales Voyeur",
15
  "supports_images": False,
16
  "specialties": [
17
+ "Prompts Sensuales Voyeur",
18
+ "Fotografía Íntima Artística",
19
+ "Escenas Cotidianas Sensuales",
20
+ "Moda y Lencería Erótica"
21
  ],
22
  "technical_expertise": [
23
+ "Detalles Hiperrealistas",
24
+ "Iluminación Cinemática",
25
+ "Composiciones Voyeur",
26
+ "Texturas Detalladas",
27
+ "Ángulos Estratégicos"
28
  ],
29
  "ethical_principles": [
30
+ "Contenido Adulto Ficticio",
31
+ "Positividad Corporal",
32
+ "Respeto y Profesionalidad",
33
+ "Sin Contenido Prohibido"
34
  ]
35
  }
36
 
37
+ # Listas de elementos proporcionadas
38
+ PROFESSIONS = [
39
+ "Executive Secretary", "Luxury Hotel Manager", "Fashion Boutique Manager", "Corporate Lawyer",
40
+ "Private Jet Attendant", "Art Gallery Curator", "University Professor", "Wine Sommelier",
41
+ "Ballet Instructor", "Yacht Stewardess", "Casino Dealer", "News Anchor", "Elegant Maid",
42
+ "Flight Attendant", "Sensual Nun", "Police Officer", "Military Officer", "Nurse",
43
+ "Schoolgirl", "Fitness Instructor", "Yoga Practitioner", "Salsa Dancer",
44
+ "Telenovela Actress", "Latin Chef", "Fiesta Organizer", "Latin Sommelier",
45
+ "Flamenco Guitarist", "Latin Diplomat", "Mariachi Singer", "Latin Curator",
46
+ "Samba Instructor", "1960s WAC Officer", "Vietnam Era Military Nurse", "1980s Air Force Pilot",
47
+ "1990s Army General", "2000s Marine Corps Captain", "2010s Cyber Warfare Specialist",
48
+ "2020s Special Forces Operator", "Artemis Program Commander", "1960s WAVES Ensign",
49
+ "1970s Navy Aviator", "1980s Submarine Officer", "1990s Naval Intelligence Analyst",
50
+ "2000s Destroyer Captain", "2010s SEAL Team Coordinator", "2020s Female Admiral",
51
+ "1960s Mercury 13 Trainee", "1970s Apollo-Era Engineer", "1980s Space Shuttle Pilot",
52
+ "1990s Hubble Telescope Scientist", "2000s ISS Mission Specialist", "2010s Mars Rover Operator",
53
+ "2020s Artemis Astronaut", "2025 Gateway Station Commander"
54
+ ]
55
+
56
+ EVERYDAY_MOMENTS = [
57
+ {"scene": "Cocina matutina", "action": "agachándose a sacar algo del horno bajo", "outfit": "camiseta oversized blanca y shorts de algodón", "setting": "cocina soleada con ventana abierta", "accessories": "taza de café en la encimera, delantal colgando"},
58
+ {"scene": "Lavandería", "action": "doblándose para sacar ropa de la secadora", "outfit": "top deportivo y leggings ajustados", "setting": "cuarto de lavado con cestas de ropa", "accessories": "cesta de ropa limpia"},
59
+ {"scene": "Jardinería", "action": "arrodillada plantando flores", "outfit": "vestido ligero de algodón con tirantes", "setting": "jardín trasero con macetas", "accessories": "guantes de jardinería, regadera"},
60
+ {"scene": "Yoga en casa", "action": "haciendo postura del perro boca abajo", "outfit": "leggings de yoga y top corto", "setting": "sala con esterilla y luz natural", "accessories": "bloque de yoga"},
61
+ {"scene": "Limpieza de estanterías", "action": "estirándose en punta de pies para alcanzar un libro", "outfit": "camiseta holgada y shorts de pijama", "setting": "biblioteca personal con escalera pequeña", "accessories": "plumero"},
62
+ {"scene": "Paseo con el perro", "action": "agachándose a atar la correa", "outfit": "vestido veraniego floreado", "setting": "parque al atardecer", "accessories": "correa, bolsa de premios"},
63
+ {"scene": "Café en la terraza", "action": "sentada cruzando las piernas y dejando caer una servilleta", "outfit": "blusa de seda y falda plisada", "setting": "terraza con mesa de hierro", "accessories": "libro abierto, taza de té"},
64
+ {"scene": "Subiendo escaleras", "action": "subiendo con una bolsa de compras", "outfit": "falda lápiz y blusa ajustada", "setting": "escalera de caracol en apartamento", "accessories": "bolsa de compras"},
65
+ {"scene": "Bailando sola", "action": "girando con música en auriculares", "outfit": "vestido corto de verano", "setting": "sala con luz cálida", "accessories": "auriculares inalámbricos"},
66
+ {"scene": "Pintando una pared", "action": "estirándose para pintar el techo", "outfit": "camiseta vieja y shorts vaqueros", "setting": "habitación en renovación", "accessories": "brocha, escalera"},
67
+ {"scene": "Cambiando bombilla", "action": "de pie en una silla", "outfit": "camiseta larga como vestido", "setting": "cocina con lámpara colgante", "accessories": "bombilla nueva"},
68
+ {"scene": "Buscando algo en el armario", "action": "arrodillada revisando cajones bajos", "outfit": "camisa de dormir de satén", "setting": "dormitorio con armario abierto", "accessories": "caja de joyas"},
69
+ {"scene": "Haciendo la cama", "action": "inclinándose sobre el colchón", "outfit": "pijama de seda corto", "setting": "dormitorio con luz de mañana", "accessories": "almohadas"},
70
+ {"scene": "Tomando el sol", "action": "ajustando la sombrilla", "outfit": "bikini bajo vestido transparente", "setting": "balcón con tumbona", "accessories": "gafas de sol"},
71
+ {"scene": "Cargando compras del auto", "action": "doblándose para sacar bolsas del maletero", "outfit": "vestido casual de algodón", "setting": "garaje residencial", "accessories": "bolsas de supermercado"},
72
+ {"scene": "Regando plantas altas", "action": "estirándose para alcanzar una maceta", "outfit": "top sin mangas y shorts", "setting": "invernadero casero", "accessories": "regadera"},
73
+ {"scene": "Probándose zapatos", "action": "sentada en el suelo con una pierna levantada", "outfit": "falda corta y blusa", "setting": "tienda de zapatos", "accessories": "caja de zapatos"},
74
+ {"scene": "Bailando en la cocina", "action": "moviendo caderas mientras cocina", "outfit": "camiseta y shorts de algodón", "setting": "cocina moderna", "accessories": "cuchara de madera"},
75
+ {"scene": "Limpiando el piso", "action": "en cuatro puntos fregando", "outfit": "vestido ligero de verano", "setting": "sala con balde", "accessories": "trapo"},
76
+ {"scene": "Colgando cortinas", "action": "de pie en una silla con los brazos arriba", "outfit": "blusa y falda corta", "setting": "ventana grande", "accessories": "cortinas nuevas"},
77
+ {"scene": "Leyendo en el sofá", "action": "cruzando las piernas y dejando caer el libro", "outfit": "vestido de punto ajustado", "setting": "sala con manta", "accessories": "libro"},
78
+ {"scene": "Haciendo estiramiento", "action": "tocando los dedos de los pies", "outfit": "leggings y top deportivo", "setting": "sala con TV", "accessories": "esterilla"},
79
+ {"scene": "Saliendo de la ducha", "action": "secándose el cabello con toalla", "outfit": "toalla corta envuelta", "setting": "baño con espejo empañado", "accessories": "secador"},
80
+ {"scene": "Buscando en el bolso", "action": "doblándose sobre la mesa", "outfit": "falda plisada y blusa", "setting": "entrada de casa", "accessories": "bolso grande"},
81
+ {"scene": "Cambiando ropa", "action": "quitándose una camiseta", "outfit": "sostén de encaje visible", "setting": "dormitorio con espejo", "accessories": "camiseta en mano"},
82
+ {"scene": "Bailando frente al espejo", "action": "ensayando un paso de baile", "outfit": "top corto y shorts", "setting": "habitación con espejo", "accessories": "móvil grabando"},
83
+ {"scene": "Subiendo al auto", "action": "sentándose con falda corta", "outfit": "falda lápiz y tacones", "setting": "estacionamiento", "accessories": "llaves en mano"},
84
+ {"scene": "Tomando selfie", "action": "agachándose para mejor ángulo", "outfit": "vestido ajustado", "setting": "parque con flores", "accessories": "móvil"},
85
+ {"scene": "Limpiando el auto", "action": "inclinándose sobre el capó", "outfit": "shorts vaqueros y top", "setting": "garaje", "accessories": "trapo húmedo"},
86
+ {"scene": "Cocinando descalza", "action": "picando verduras en la encimera", "outfit": "camiseta larga sin nada debajo", "setting": "cocina con isla", "accessories": "cuchillo, tabla"}
87
+ ]
88
+
89
+ LACE_THONG_STYLES = [
90
+ "delicate black lace thong with floral embroidery",
91
+ "sheer nude illusion thong with lace trim",
92
+ "red satin thong with scalloped edges",
93
+ "white lace thong with pearl detail"
94
+ ]
95
+
96
+ HOSIERY_STYLES = [
97
+ "black sheer thigh-high stockings with lace tops",
98
+ "nude ultra-sheer stockings with reinforced toes",
99
+ "fishnet thigh-highs with delicate weave",
100
+ "back-seam stockings with Cuban heel"
101
+ ]
102
+
103
+ # ==========================
104
+ # CLIENTE SAMBANOVA
105
+ # ==========================
106
+
107
  class SambaNovaClient:
108
  def __init__(self, api_key: str, base_url: str = "https://api.sambanova.ai/v1"):
109
  self.api_key = api_key
 
116
  def chat_completion(
117
  self,
118
  messages: List[Dict],
119
+ model: str = VOYEUR_SPECIALIST_CONFIG["name"],
120
+ temperature: float = 0.8,
121
  top_p: float = 0.9,
122
+ stream: bool = False,
123
+ max_tokens: int = 2000
124
  ) -> str:
125
  url = f"{self.base_url}/chat/completions"
126
  payload = {
 
128
  "messages": messages,
129
  "temperature": temperature,
130
  "top_p": top_p,
131
+ "stream": stream,
132
+ "max_tokens": max_tokens
133
  }
134
 
135
  try:
 
137
  response.raise_for_status()
138
  data = response.json()
139
  return data["choices"][0]["message"]["content"]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
140
  except requests.exceptions.RequestException as e:
141
+ return f"Error en la API: {str(e)}"
142
+
143
+ # ==========================
144
+ # PROMPT DEL SISTEMA
145
+ # ==========================
146
+
147
+ def create_voyeur_specialist_system_prompt():
148
+ specialties = "\n".join([f"• {spec}" for spec in VOYEUR_SPECIALIST_CONFIG["specialties"]])
149
+ expertise = "\n".join([f"• {exp}" for exp in VOYEUR_SPECIALIST_CONFIG["technical_expertise"]])
150
+ ethics = "\n".join([f"• {ethic}" for ethic in VOYEUR_SPECIALIST_CONFIG["ethical_principles"]])
151
 
152
+ professions_str = ", ".join(PROFESSIONS)
153
+ everyday_moments_str = json.dumps(EVERYDAY_MOMENTS, ensure_ascii=False, indent=2)
154
+ lace_thong_str = ", ".join(LACE_THONG_STYLES)
155
+ hosiery_str = ", ".join(HOSIERY_STYLES)
156
 
157
  return f"""
158
+ Eres {VOYEUR_SPECIALIST_CONFIG['role']}
159
 
160
  **ESPECIALIDADES:**
161
  {specialties}
 
167
  {ethics}
168
 
169
  **DIRECTIVAS:**
170
+ - Eres un chatbot universal que conversa en español sobre cualquier tema.
171
+ - Tu cualidad principal es generar prompts ultra-sensuales voyeur en inglés.
172
+ - Para conversación general, responde en español de manera natural y amigable.
173
+ - Cuando el usuario solicite generar prompts (ej: "genera un prompt", "crea 3 prompts sensuales", etc.), responde ÚNICAMENTE con el/los prompt(s) en inglés, sin explicaciones, títulos, comentarios o texto adicional.
174
+ - Cada prompt debe estar en un bloque markdown: ```prompt\n[texto del prompt]\n```
175
+ - Los prompts deben ser detallados, hiperrealistas, sensuales, voyeur, en inglés, usando elementos como profesiones, momentos cotidianos, estilos de lencería, etc.
176
+ - Varía los prompts para que sean únicos, incorporando elementos al azar.
177
 
178
+ **ELEMENTOS PARA PROMPTS:**
179
+ - Profesiones: {professions_str}
180
+ - Momentos cotidianos: {everyday_moments_str}
181
+ - Estilos de thong: {lace_thong_str}
182
+ - Estilos de hosiery: {hosiery_str}
183
+
184
+ Ejemplo de prompt (usa como guía para estructura):
185
+ "Ultra-realistic full-body voyeur portrait of a Latin woman as an Executive Secretary, caught in a sensual everyday moment: bending over in kitchen, wearing delicate black lace thong with floral embroidery subtly visible, black sheer thigh-high stockings with lace tops, black patent leather stilettos, long flowing wavy hair, sultry makeup, soft cinematic lighting, shot with Canon EOS R5 85mm f/1.2 --ar 9:16 --style raw --quality 2"
186
+
187
+ Mantén respuestas concisas y enfocadas.
188
  """
189
 
190
+ # ==========================
191
+ # FUNCIÓN DE CHAT
192
+ # ==========================
193
+
194
+ def chat_voyeur(message, history, api_key):
195
  if not api_key:
196
  return "❌ Ingresa tu API Key", history
197
 
198
  client = SambaNovaClient(api_key)
199
+ messages = [{"role": "system", "content": create_voyeur_specialist_system_prompt()}]
200
  for user_msg, assistant_msg in history:
201
  messages.append({"role": "user", "content": user_msg})
202
  if assistant_msg is not None:
 
204
 
205
  messages.append({"role": "user", "content": message})
206
 
207
+ response = client.chat_completion(messages)
 
 
 
 
 
208
 
209
  history.append([message, response])
210
  return "", history
211
 
212
+ # ==========================
213
+ # PRUEBA DE API
214
+ # ==========================
215
+
216
  def test_sambanova_api(api_key):
217
  if not api_key:
218
  return "❌ Ingresa tu API Key primero"
 
220
  client = SambaNovaClient(api_key)
221
  test_messages = [
222
  {"role": "system", "content": "Eres un asistente útil. Responde brevemente."},
223
+ {"role": "user", "content": "Hola, confirma conexión. Responde solo 'Conexión exitosa'."},
 
 
 
224
  ]
225
+ return client.chat_completion(test_messages)
226
+
227
+ # ==========================
228
+ # INTERFAZ GRADIO
229
+ # ==========================
230
 
231
  def create_interface():
232
+ with gr.Blocks(theme=gr.themes.Soft(), title="Chatbot Voyeur Prompt Specialist") as demo:
233
+ gr.Markdown("# 🎭 Chatbot Universal - Especialista en Prompts Sensuales Voyeur")
234
+ gr.Markdown("### Conversa en español. Pide prompts sensuales para generarlos en inglés (solo el prompt, fácil de copiar).")
235
+
236
  with gr.Row():
237
  with gr.Column(scale=1):
238
  api_key_input = gr.Textbox(
239
  label="🔑 API Key de SambaNova",
240
+ placeholder="Ingresa tu API key...",
241
  type="password",
 
242
  )
243
+ test_btn = gr.Button("🧪 Probar API")
244
+ test_output = gr.Textbox(label="Resultado de prueba", interactive=False)
245
  gr.Markdown("### 📋 Especialidades")
246
+ for specialty in VOYEUR_SPECIALIST_CONFIG["specialties"]:
247
  gr.Markdown(f"• {specialty}")
248
  gr.Markdown("### 🎯 Experiencia Técnica")
249
+ for expertise in VOYEUR_SPECIALIST_CONFIG["technical_expertise"]:
250
  gr.Markdown(f"• {expertise}")
251
  with gr.Column(scale=2):
252
  chatbot = gr.Chatbot(
253
+ label="💬 Chat con el Especialista",
254
  height=500,
255
+ show_copy_button=True
256
  )
257
  msg = gr.Textbox(
258
  label="Escribe tu mensaje",
259
+ placeholder="Ej: Genera 2 prompts sensuales voyeur... o pregunta cualquier cosa.",
260
  lines=2,
261
  )
262
+ with gr.Row():
263
+ send_btn = gr.Button("Enviar", variant="primary")
264
+ clear_btn = gr.Button("🧹 Limpiar chat")
265
+
266
+ gr.Markdown("### 💡 Ejemplos:")
267
  gr.Examples(
268
  examples=[
269
+ "Genera un prompt sensual voyeur con una secretaria.",
270
+ "Crea 3 prompts ultra-sensuales con momentos cotidianos.",
271
+ "¿Cómo generar prompts mejores?",
272
+ "Háblame sobre fotografía boudoir."
 
273
  ],
274
  inputs=msg,
275
  )
276
+
277
  test_btn.click(
278
  test_sambanova_api,
279
  inputs=[api_key_input],
280
  outputs=[test_output],
281
  )
282
+
283
  def user_message(user_msg, history):
284
+ return "", history + [[user_msg, None]]
285
+
286
+ submit_fn = user_message
287
+ then_fn = chat_voyeur
288
+
289
  msg.submit(
290
+ submit_fn,
291
+ inputs=[msg, chatbot],
292
+ outputs=[msg, chatbot],
293
+ ).then(
294
+ then_fn,
295
+ inputs=[msg, chatbot, api_key_input],
296
+ outputs=[msg, chatbot],
297
+ )
298
+
299
+ send_btn.click(
300
+ submit_fn,
301
  inputs=[msg, chatbot],
302
  outputs=[msg, chatbot],
303
  ).then(
304
+ then_fn,
305
  inputs=[msg, chatbot, api_key_input],
306
  outputs=[msg, chatbot],
307
  )
308
+
309
  clear_btn.click(lambda: None, None, chatbot, queue=False)
310
+
311
  return demo
312
 
313
  if __name__ == "__main__":
314
  demo = create_interface()
315
+ demo.launch(share=True, debug=True)