File size: 10,447 Bytes
23a1d38
 
6615bd0
 
eae55f3
475e0d1
eae55f3
 
aff1151
 
eae55f3
 
 
6615bd0
eae55f3
6615bd0
eae55f3
 
6615bd0
 
eae55f3
 
 
 
6615bd0
 
eae55f3
 
 
 
 
6615bd0
 
eae55f3
 
 
 
475e0d1
6615bd0
 
eae55f3
 
 
 
475e0d1
 
 
 
 
 
 
 
 
 
 
 
eae55f3
 
475e0d1
eae55f3
 
475e0d1
 
 
 
 
 
 
eae55f3
 
475e0d1
 
 
 
 
 
 
 
eae55f3
 
 
 
 
 
 
 
 
 
475e0d1
eae55f3
 
 
 
66f02ff
475e0d1
eae55f3
6615bd0
475e0d1
 
28ab90e
475e0d1
 
6615bd0
12aa591
475e0d1
6615bd0
475e0d1
eae55f3
 
 
 
 
 
 
6615bd0
eae55f3
 
 
 
 
 
 
 
 
 
475e0d1
6615bd0
23a1d38
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
eae55f3
12aa591
eae55f3
 
23a1d38
 
 
475e0d1
12aa591
 
23a1d38
42a1e8b
6615bd0
475e0d1
dcbcd2e
7ebd92a
eae55f3
dcbcd2e
12aa591
 
 
dcbcd2e
 
475e0d1
cd7ff6f
7ebd92a
eae55f3
cd7ff6f
12aa591
 
 
 
42a1e8b
475e0d1
eae55f3
 
 
 
23a1d38
 
 
475e0d1
23a1d38
475e0d1
 
 
 
eae55f3
475e0d1
eae55f3
 
 
12aa591
eae55f3
475e0d1
 
6f2a908
eae55f3
 
 
475e0d1
 
23a1d38
eae55f3
475e0d1
eae55f3
475e0d1
 
eae55f3
475e0d1
23a1d38
 
 
 
 
475e0d1
12aa591
475e0d1
eae55f3
42a1e8b
475e0d1
 
 
eae55f3
475e0d1
 
eae55f3
 
 
 
 
475e0d1
 
eae55f3
 
 
 
c4e5996
475e0d1
c4e5996
eae55f3
475e0d1
 
23a1d38
475e0d1
 
eae55f3
12aa591
dcbcd2e
12aa591
 
7ebd92a
eae55f3
dcbcd2e
475e0d1
dcbcd2e
eae55f3
 
 
7ebd92a
23a1d38
eae55f3
 
 
dcbcd2e
eae55f3
dcbcd2e
475e0d1
 
 
7ebd92a
23a1d38
475e0d1
 
eae55f3
42a1e8b
dcbcd2e
eae55f3
475e0d1
cd7ff6f
 
475e0d1
23a1d38
12aa591
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
# app.py

import os
import gradio as gr
import requests
from typing import List, Dict
import json

from data import PROFESSIONS, EVERYDAY_MOMENTS, LACE_THONG_STYLES, HOSIERY_STYLES

# ==========================
# CONFIGURACIÓN DEL ESPECIALISTA
# ==========================

VOYEUR_SPECIALIST_CONFIG = {
    "provider": "sambanova",
    "name": "Meta-Llama-3.1-8B-Instruct",
    "role": "🎭 Especialista en Generación de Prompts Ultra-Sensuales Voyeur",
    "supports_images": False,
    "specialties": [
        "Prompts Sensuales Voyeur",
        "Fotografía Íntima Artística",
        "Escenas Cotidianas Sensuales",
        "Moda y Lencería Erótica"
    ],
    "technical_expertise": [
        "Detalles Hiperrealistas",
        "Iluminación Cinemática",
        "Composiciones Voyeur",
        "Texturas Detalladas",
        "Ángulos Estratégicos"
    ],
    "ethical_principles": [
        "Contenido Adulto Ficticio",
        "Positividad Corporal",
        "Respeto y Profesionalidad",
        "Sin Contenido Prohibido"
    ]
}

# ==========================
# CLIENTE SAMBANOVA
# ==========================

class SambaNovaClient:
    def __init__(self, api_key: str, base_url: str = "https://api.sambanova.ai/v1"):
        self.api_key = api_key
        self.base_url = base_url
        self.headers = {
            "Authorization": f"Bearer {api_key}",
            "Content-Type": "application/json"
        }

    def chat_completion(
        self,
        messages: List[Dict],
        model: str = VOYEUR_SPECIALIST_CONFIG["name"],
        temperature: float = 0.8,
        top_p: float = 0.9,
        stream: bool = False,
        max_tokens: int = 2000
    ) -> str:
        url = f"{self.base_url}/chat/completions"
        payload = {
            "model": model,
            "messages": messages,
            "temperature": temperature,
            "top_p": top_p,
            "stream": stream,
            "max_tokens": max_tokens
        }

        try:
            response = requests.post(url, headers=self.headers, json=payload)
            response.raise_for_status()
            data = response.json()
            return data["choices"][0]["message"]["content"]
        except requests.exceptions.RequestException as e:
            return f"Error en la API: {str(e)}"

# ==========================
# PROMPT DEL SISTEMA
# ==========================

def create_voyeur_specialist_system_prompt():
    specialties = "\n".join([f"• {spec}" for spec in VOYEUR_SPECIALIST_CONFIG["specialties"]])
    expertise = "\n".join([f"• {exp}" for exp in VOYEUR_SPECIALIST_CONFIG["technical_expertise"]])
    ethics = "\n".join([f"• {ethic}" for ethic in VOYEUR_SPECIALIST_CONFIG["ethical_principles"]])

    professions_str = ", ".join(PROFESSIONS)
    everyday_moments_str = json.dumps(EVERYDAY_MOMENTS, ensure_ascii=False, indent=2)
    lace_thong_str = ", ".join(LACE_THONG_STYLES)
    hosiery_str = ", ".join(HOSIERY_STYLES)

    return f"""
Eres {VOYEUR_SPECIALIST_CONFIG['role']}

**ESPECIALIDADES:**
{specialties}

**EXPERIENCIA TÉCNICA:**
{expertise}

**PRINCIPLOS ÉTICOS:**
{ethics}

**DIRECTIVAS:**
- Eres un chatbot universal que conversa en español sobre cualquier tema.
- Tu cualidad principal es generar prompts ultra-sensuales voyeur en inglés.
- Para conversación general, responde en español de manera natural y amigable.
- 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.
- Cada prompt debe estar en un bloque markdown: ```prompt\n[texto del prompt]\n```
- Los prompts deben ser detallados, hiperrealistas, sensuales, voyeur, en inglés, usando elementos como profesiones, momentos cotidianos, estilos de lencería, etc.
- Varía los prompts para que sean únicos, incorporando elementos al azar.

**ELEMENTOS PARA PROMPTS:**
- Profesiones: {professions_str}
- Momentos cotidianos: {everyday_moments_str}
- Estilos de thong: {lace_thong_str}
- Estilos de hosiery: {hosiery_str}

Ejemplo de prompt (usa como guía para estructura):
"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"

Mantén respuestas concisas y enfocadas.
"""

# ==========================
# FUNCIÓN PARA OBTENER API KEY
# ==========================

def get_api_key():
    """Obtiene la API key desde variables de entorno/secretos"""
    # Para Hugging Face Spaces
    api_key = os.environ.get("SAMBANOVA_API_KEY")
    
    # Si no está en variables de entorno, intenta desde secrets (para Gradio)
    if not api_key:
        try:
            # Esto funciona en Hugging Face Spaces con secretos
            api_key = os.environ.get("SAMBANOVA_API_KEY")
        except:
            pass
            
    return api_key

# ==========================
# FUNCIÓN DE CHAT COMPATIBLE CON HF SPACES
# ==========================

def chat_voyeur(message, chat_history):
    api_key = get_api_key()
    
    if not api_key:
        # Formato de diccionario para HF Spaces
        chat_history.append({"role": "user", "content": message})
        chat_history.append({"role": "assistant", "content": "❌ API Key no configurada. Por favor, configura la variable de entorno SAMBANOVA_API_KEY."})
        return "", chat_history

    client = SambaNovaClient(api_key)
    
    # Construir mensajes para la API
    messages = [{"role": "system", "content": create_voyeur_specialist_system_prompt()}]
    
    # Procesar historial en formato diccionario
    for msg in chat_history:
        messages.append({"role": msg["role"], "content": msg["content"]})
    
    # Agregar el mensaje actual
    messages.append({"role": "user", "content": message})

    # Obtener respuesta
    response = client.chat_completion(messages)

    # Agregar al historial en formato diccionario
    chat_history.append({"role": "user", "content": message})
    chat_history.append({"role": "assistant", "content": response})
    
    return "", chat_history

# ==========================
# PRUEBA DE API
# ==========================

def test_sambanova_api():
    api_key = get_api_key()
    
    if not api_key:
        return "❌ API Key no configurada. Configura la variable de entorno SAMBANOVA_API_KEY."

    client = SambaNovaClient(api_key)
    test_messages = [
        {"role": "system", "content": "Eres un asistente útil. Responde brevemente."},
        {"role": "user", "content": "Hola, confirma conexión. Responde solo 'Conexión exitosa'."},
    ]
    return client.chat_completion(test_messages)

# ==========================
# INTERFAZ GRADIO COMPATIBLE CON HF SPACES
# ==========================

def create_interface():
    with gr.Blocks(title="Chatbot Voyeur Prompt Specialist") as demo:
        gr.Markdown("# 🎭 Chatbot Universal - Especialista en Prompts Sensuales Voyeur")
        gr.Markdown("### Conversa en español. Pide prompts sensuales para generarlos en inglés (solo el prompt, fácil de copiar).")

        with gr.Row():
            with gr.Column(scale=1):
                test_btn = gr.Button("🧪 Probar Conexión API")
                test_output = gr.Textbox(label="Resultado de prueba", interactive=False)
                gr.Markdown("### 📋 Especialidades")
                for specialty in VOYEUR_SPECIALIST_CONFIG["specialties"]:
                    gr.Markdown(f"• {specialty}")
                gr.Markdown("### 🎯 Experiencia Técnica")
                for expertise in VOYEUR_SPECIALIST_CONFIG["technical_expertise"]:
                    gr.Markdown(f"• {expertise}")
                
                # Información sobre configuración
                gr.Markdown("### ⚙️ Configuración")
                gr.Markdown("API Key configurada desde variables de entorno")
                
            with gr.Column(scale=2):
                # Chatbot sin parámetros problemáticos
                chatbot = gr.Chatbot(
                    label="💬 Chat con el Especialista",
                    height=500
                )
                msg = gr.Textbox(
                    label="Escribe tu mensaje",
                    placeholder="Ej: Genera 2 prompts sensuales voyeur... o pregunta cualquier cosa.",
                    lines=2,
                )
                with gr.Row():
                    send_btn = gr.Button("Enviar", variant="primary")
                    clear_btn = gr.Button("🧹 Limpiar chat")

        gr.Markdown("### 💡 Ejemplos:")
        gr.Examples(
            examples=[
                "Genera un prompt sensual voyeur con una secretaria.",
                "Crea 3 prompts ultra-sensuales con momentos cotidianos.",
                "¿Cómo generar prompts mejores?",
                "Háblame sobre fotografía boudoir."
            ],
            inputs=msg,
        )

        test_btn.click(
            test_sambanova_api,
            inputs=[],
            outputs=[test_output],
        )

        # Función para manejar mensajes en formato diccionario
        def user_message(user_msg, chat_history):
            # Agregar mensaje de usuario al historial
            new_history = chat_history + [{"role": "user", "content": user_msg}]
            return "", new_history

        # Configurar el envío con Enter
        msg.submit(
            user_message,
            inputs=[msg, chatbot],
            outputs=[msg, chatbot],
        ).then(
            chat_voyeur,
            inputs=[msg, chatbot],
            outputs=[msg, chatbot],
        )

        # Configurar el botón Enviar
        send_btn.click(
            user_message,
            inputs=[msg, chatbot],
            outputs=[msg, chatbot],
        ).then(
            chat_voyeur,
            inputs=[msg, chatbot],
            outputs=[msg, chatbot],
        )

        # Configurar el botón Limpiar
        clear_btn.click(lambda: [], None, chatbot, queue=False)

    return demo

if __name__ == "__main__":
    demo = create_interface()
    # Para Hugging Face Spaces, no usar share=True
    demo.launch(debug=True)