Spaces:
Running
Running
File size: 18,324 Bytes
c76bbac e75a09b c76bbac a339470 5119b98 348d961 b1cf5e1 348d961 b1cf5e1 fa342d1 c0073a3 b1cf5e1 c0073a3 b1cf5e1 fa342d1 b1cf5e1 c76bbac b1cf5e1 d8f563a 15d4fae d8f563a 6a4a20b b1cf5e1 97cb8fc b1cf5e1 c76bbac b1cf5e1 97cb8fc 5119b98 259916f e369da7 92a9e2f dc6ebfc 92a9e2f 97cb8fc e369da7 dc6ebfc 5119b98 650485d 97cb8fc 650485d 97cb8fc e369da7 5119b98 b6b7f7a 97cb8fc b6b7f7a 657617d b6b7f7a b1cf5e1 b6b7f7a b1cf5e1 a6d440d b1cf5e1 b6b7f7a 5c64156 b6b7f7a 5c64156 b6b7f7a 5c64156 97cb8fc 50a21af 0e57fcc 97cb8fc 9456d19 97cb8fc 9456d19 97cb8fc 9456d19 97cb8fc a5ef5cf 97cb8fc d1e9ecf b1cf5e1 d6a1a2a b1cf5e1 97cb8fc 4c826ff b1cf5e1 97cb8fc 4c826ff b1cf5e1 97cb8fc 923676c 97cb8fc b1cf5e1 97cb8fc b1cf5e1 97cb8fc b1cf5e1 b6b7f7a 9456d19 d5a01fe 259916f e259740 259916f e369da7 259916f e369da7 e259740 e369da7 e259740 259916f e259740 259916f |
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 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 |
import time
import os
import joblib
import streamlit as st
import google.generativeai as genai
from dotenv import load_dotenv
from puv_formulas import puv_formulas
from system_prompts import get_puv_system_prompt
# Función para cargar CSS personalizado
def load_css(file_path):
with open(file_path) as f:
st.markdown(f'<style>{f.read()}</style>', unsafe_allow_html=True)
# Intentar cargar el CSS personalizado con ruta absoluta para mayor seguridad
try:
css_path = os.path.join(os.path.dirname(__file__), 'static', 'css', 'style.css')
load_css(css_path)
except Exception as e:
print(f"Error al cargar CSS: {e}")
# Si el archivo no existe, crear un estilo básico en línea
st.markdown("""
<style>
.robocopy-title {
color: #4ECDC4 !important;
font-weight: bold;
font-size: 2em;
}
</style>
""", unsafe_allow_html=True)
load_dotenv()
GOOGLE_API_KEY=os.environ.get('GOOGLE_API_KEY')
genai.configure(api_key=GOOGLE_API_KEY)
new_chat_id = f'{time.time()}'
MODEL_ROLE = 'ai'
AI_AVATAR_ICON = '🤖' # Cambia el emoji por uno de robot para coincidir con tu logo
USER_AVATAR_ICON = '👤' # Añade un avatar para el usuario
# Create a data/ folder if it doesn't already exist
try:
os.mkdir('data/')
except:
# data/ folder already exists
pass
# Load past chats (if available)
try:
past_chats: dict = joblib.load('data/past_chats_list')
except:
past_chats = {}
# Sidebar allows a list of past chats
with st.sidebar:
# Centrar el logo y eliminar el título de RoboCopy
col1, col2, col3 = st.columns([1, 2, 1])
with col2:
st.image("assets/robocopy_logo.png", width=300)
st.write('# Chats Anteriores')
if st.session_state.get('chat_id') is None:
st.session_state.chat_id = st.selectbox(
label='Selecciona un chat anterior',
options=[new_chat_id] + list(past_chats.keys()),
format_func=lambda x: past_chats.get(x, 'Nuevo Chat'),
placeholder='_',
)
else:
# This will happen the first time AI response comes in
st.session_state.chat_id = st.selectbox(
label='Selecciona un chat anterior',
options=[new_chat_id, st.session_state.chat_id] + list(past_chats.keys()),
index=1,
format_func=lambda x: past_chats.get(x, 'Nuevo Chat' if x != st.session_state.chat_id else st.session_state.chat_title),
placeholder='_',
)
# Save new chats after a message has been sent to AI
st.session_state.chat_title = f'SesiónChat-{st.session_state.chat_id}'
st.write('# Generador de Propuestas de Valor Únicas (PUVs)')
st.write('Describe tu producto/servicio y audiencia objetivo para generar PUVs personalizadas.')
# Chat history (allows to ask multiple questions)
try:
st.session_state.messages = joblib.load(
f'data/{st.session_state.chat_id}-st_messages'
)
st.session_state.gemini_history = joblib.load(
f'data/{st.session_state.chat_id}-gemini_messages'
)
print('old cache')
except:
st.session_state.messages = []
st.session_state.gemini_history = []
print('new_cache made')
st.session_state.model = genai.GenerativeModel('gemini-2.0-flash')
st.session_state.chat = st.session_state.model.start_chat(
history=st.session_state.gemini_history,
)
# Display chat messages from history on app rerun
for message in st.session_state.messages:
with st.chat_message(
name=message['role'],
avatar=message.get('avatar'),
):
st.markdown(message['content'])
# Mensaje inicial del sistema si es un chat nuevo
if not st.session_state.messages:
system_prompt = get_puv_system_prompt()
# Mostrar la carátula inicial
st.markdown("""
<div style='text-align: center; padding: 1rem 0;'>
<h1 style='font-size: 24px; margin-bottom: 0.5rem;'>💎 RoboCopy PUV Creator</h1>
<p style='font-size: 16px; color: white;'>By Jesús Cabrera</p>
<p style='font-size: 14px; background-color: rgba(78, 205, 196, 0.1); padding: 8px; border-radius: 5px; margin-top: 10px; color: white;'>
🎯 Experto en crear Propuestas de Valor Únicas que convierten audiencia en clientes
</p>
</div>
""", unsafe_allow_html=True)
# Mostrar los ejemplos
ejemplos = [
{"texto": "¿Cuál es la mejor fórmula para mi PUV? 🤔", "prompt": "Ayúdame a elegir la mejor fórmula para mi Propuesta de Valor Única"},
{"texto": "¿Cómo hacer mi PUV más impactante? 💫", "prompt": "Dame consejos para hacer mi Propuesta de Valor más persuasiva y memorable"},
{"texto": "Necesito una PUV para mi servicio...", "prompt": "Ayúdame a crear una Propuesta de Valor para mi servicio de coaching"},
{"texto": "¿Ejemplos de PUVs efectivas? 🎯", "prompt": "Muéstrame ejemplos de Propuestas de Valor exitosas en mi industria"}
]
# Crear los botones de ejemplo
cols = st.columns(4)
for idx, ejemplo in enumerate(ejemplos):
with cols[idx]:
if st.button(ejemplo["texto"], key=f"ejemplo_{idx}", help=ejemplo["prompt"]):
st.session_state.prompt = ejemplo["prompt"]
st.rerun()
# Inicializar el chat con el sistema prompt
st.session_state.chat = st.session_state.model.start_chat(
history=st.session_state.gemini_history
)
# Enviar el system prompt como primer mensaje
st.session_state.chat.send_message(system_prompt)
# Solo añadir el mensaje inicial si no es un ejemplo
if not st.session_state.get('prompt'):
st.session_state.messages.append(
dict(
role=MODEL_ROLE,
content="""
Hola, soy RoboCopy tu asistente especializado en crear Propuestas de Valor Únicas.
Para ayudarte a crear PUVs efectivas, necesito conocer:
1. ¿Qué producto o servicio ofreces?
2. ¿A quién va dirigido? (describe tu público objetivo)
3. ¿Qué fórmula prefieres usar? Puedo ofrecerte:
- Tradicional: Clara y directa
- Anti-tradicional: Innovadora y disruptiva
- Contrato Imposible: Audaz y sorprendente
- Reto Ridículo: Humorística y relatable
4. ¿Cuántos ejemplos de PUVs necesitas?
¿Empezamos con tu producto o servicio?
""",
avatar=AI_AVATAR_ICON,
)
)
# Add system message to chat history
st.session_state.messages.append(
dict(
role=MODEL_ROLE,
content="""
Hola, soy RoboCopy tu asistente especializado en crear Propuestas de Valor Únicas.
Para ayudarte a crear PUVs efectivas, necesito conocer:
1. ¿Qué producto o servicio ofreces?
2. ¿A quién va dirigido? (describe tu público objetivo)
3. ¿Qué fórmula prefieres usar? Puedo ofrecerte:
- Tradicional: Clara y directa
- Anti-tradicional: Innovadora y disruptiva
- Contrato Imposible: Audaz y sorprendente
- Reto Ridículo: Humorística y relatable
4. ¿Cuántos ejemplos de PUVs necesitas?
¿Empezamos con tu producto o servicio?
""",
avatar=AI_AVATAR_ICON,
)
)
# React to user input
if prompt := st.chat_input('Describe tu producto/servicio y audiencia objetivo...'):
# Save this as a chat for later
if st.session_state.chat_id not in past_chats.keys():
# Es una nueva conversación, generemos un título basado en el primer mensaje
# Primero, guardamos un título temporal
temp_title = f'SesiónChat-{st.session_state.chat_id}'
past_chats[st.session_state.chat_id] = temp_title
# Generamos un título basado en el contenido del mensaje
try:
# Usamos el mismo modelo para generar un título corto
title_generator = genai.GenerativeModel('gemini-2.0-flash')
title_response = title_generator.generate_content(
f"Genera un título corto (máximo 5 palabras) que describa de qué trata esta consulta, sin usar comillas ni puntuación: '{prompt}'")
# Obtenemos el título generado
generated_title = title_response.text.strip()
# Actualizamos el título en past_chats
if generated_title:
st.session_state.chat_title = generated_title
past_chats[st.session_state.chat_id] = generated_title
else:
st.session_state.chat_title = temp_title
except Exception as e:
print(f"Error al generar título: {e}")
st.session_state.chat_title = temp_title
else:
# Ya existe esta conversación, usamos el título guardado
st.session_state.chat_title = past_chats[st.session_state.chat_id]
joblib.dump(past_chats, 'data/past_chats_list')
# Display user message in chat message container
with st.chat_message('user', avatar=USER_AVATAR_ICON):
st.markdown(prompt)
# Add user message to chat history
st.session_state.messages.append(
dict(
role='user',
content=prompt,
)
)
# Construir el prompt para el modelo con todas las fórmulas disponibles
puv_expert_prompt = """You are a collaborative team of world-class experts working together to create exceptional Unique Value Propositions (UVPs) that convert audience into customers.
INTERNAL ANALYSIS (DO NOT OUTPUT):
1. DEEP AVATAR ANALYSIS:
A. Daily Life & Cultural Context:
- What daily experiences resonate with them?
- What cultural references do they understand?
- What mental images are easy for them to recall?
- What TV shows, movies, or media do they consume?
B. Pain Points & Emotional Core:
- What is their ONE main pain point?
- What consequences does this pain point trigger?
- What past painful experience influences current decisions?
- What internal conflict do they regularly experience?
- What do they need to heal or resolve to feel complete?
C. Previous Solutions:
- What have they tried before that didn't work?
- Why didn't these solutions work for them?
- What do other "experts" tell them to do?
- What false beliefs do they hold?
D. Desires & Transformations:
- What are their primary desires?
- What is their current vs. desired situation?
- What transformation are they seeking?
- Why do they need to act now?
2. PRODUCT/SERVICE ANALYSIS:
- What is the main benefit or promise?
- What makes it unique or different?
- What transformation does it offer?
- How does it help achieve results?
- Why is it superior to existing solutions?
3. MARKET CONTEXT:
- What are the common industry solutions?
- Why do these solutions fail?
- What are the typical misconceptions?
- What makes your solution unique?
Based on this internal analysis, create UVPs that:
1. Connect directly with the main pain point
2. Address deep emotional motivations
3. Contrast with failed past solutions
4. Present your unique method convincingly
5. Use familiar analogies or metaphors
THE EXPERT TEAM:
1. MASTER UVP STRATEGIST:
- Expert in UVP frameworks and conversion strategies
- Ensures the UVPs follow the selected framework structure precisely
- Focuses on strategic placement of key conversion elements
2. ELITE DIRECT RESPONSE COPYWRITER:
- Trained by Gary Halbert, Gary Bencivenga, and David Ogilvy
- Creates compelling hooks and persuasive elements
- Ensures the language resonates with the target audience
3. AUDIENCE PSYCHOLOGY SPECIALIST:
- Expert in understanding audience motivations and objections
- Creates content that builds genuine connection and trust
- Identifies and addresses hidden fears and desires
4. STORYTELLING MASTER:
- Creates compelling narratives that illustrate key points
- Makes complex concepts accessible through narrative
5. ENGAGEMENT EXPERT:
- Specializes in creating memorable and impactful statements
- Ensures the UVPs are clear, concise and compelling
You are a UVP (Unique Value Proposition) expert. Analyze (internally only, do not output the analysis) the user's message to identify information about their product/service and target audience.
If the user hasn't provided all the necessary information, guide them through the process by asking for:
1. What product/service they offer
2. Who their target audience is
3. Which formula they prefer (Tradicional, Anti-tradicional, Contrato Imposible, Reto Ridículo)
4. How many UVP examples they want
If the user mentions a specific formula, use that formula from the puv_formulas dictionary. If they don't specify, suggest the most appropriate formula based on their product/service and audience.
If the user is asking for UVPs and has provided sufficient information, create the requested number of different UVPs using the specified formula. If the user is asking a question about UVPs or marketing, answer it helpfully.
When creating UVPs, follow these CRITICAL INSTRUCTIONS:
- Each UVP must be specific and measurable
- Focus on the transformation journey
- Use natural, conversational language
- Avoid generic phrases and buzzwords
- Maximum 2 lines per UVP
If creating UVPs, output in this format:
"Basado en tu descripción, aquí tienes [número] propuestas de valor únicas (PUVs) para tu [producto/servicio] usando la fórmula [nombre de fórmula]:
1. [First UVP]
2. [Second UVP]
3. [Third UVP]
...
Estas PUVs destacan [principales beneficios]. ¿Hay alguna que te guste más o quieres que ajuste algún aspecto?"
If answering a question, provide a helpful, expert response.
"""
# Combinar el prompt del experto con el mensaje del usuario
enhanced_prompt = f"{puv_expert_prompt}\n\nUser message: {prompt}"
## Send message to AI
response = st.session_state.chat.send_message(
enhanced_prompt,
stream=True,
)
# Display assistant response in chat message container
with st.chat_message(
name=MODEL_ROLE,
avatar=AI_AVATAR_ICON,
):
message_placeholder = st.empty()
full_response = ''
assistant_response = response
# Añade un indicador de "escribiendo..."
typing_indicator = st.empty()
typing_indicator.markdown("*Generando respuesta...*")
# Streams in a chunk at a time
for chunk in response:
# Simulate stream of chunk
for ch in chunk.text: # Eliminamos el split(' ') para procesar carácter por carácter
full_response += ch
time.sleep(0.01) # Más rápido
# Rewrites with a cursor at end
message_placeholder.write(full_response + '▌')
# Elimina el indicador de escritura
typing_indicator.empty()
# Write full message with placeholder
message_placeholder.write(full_response)
# Add assistant response to chat history
st.session_state.messages.append(
dict(
role=MODEL_ROLE,
content=st.session_state.chat.history[-1].parts[0].text,
avatar=AI_AVATAR_ICON,
)
)
st.session_state.gemini_history = st.session_state.chat.history
# Save to file
joblib.dump(
st.session_state.messages,
f'data/{st.session_state.chat_id}-st_messages',
)
joblib.dump(
st.session_state.gemini_history,
f'data/{st.session_state.chat_id}-gemini_messages',
)
# Cuando se selecciona un ejemplo
if 'prompt' in st.session_state and st.session_state.prompt: # Verificamos que el prompt no esté vacío
prompt = st.session_state.prompt
# Guardar el chat para después si es nuevo
if st.session_state.chat_id not in past_chats.keys():
temp_title = f'SesiónChat-{st.session_state.chat_id}'
past_chats[st.session_state.chat_id] = temp_title
joblib.dump(past_chats, 'data/past_chats_list')
# Mostrar el mensaje del usuario
with st.chat_message('user', avatar=USER_AVATAR_ICON):
st.markdown(prompt)
# Añadir el mensaje del usuario al historial
st.session_state.messages.append({
'role': 'user',
'content': prompt,
'avatar': USER_AVATAR_ICON
})
# Procesar la respuesta del modelo
if prompt.strip():
with st.chat_message(MODEL_ROLE, avatar=AI_AVATAR_ICON):
try:
response = st.session_state.chat.send_message(prompt)
st.markdown(response.text)
# Añadir la respuesta al historial
st.session_state.messages.append({
'role': MODEL_ROLE,
'content': response.text,
'avatar': AI_AVATAR_ICON
})
# Guardar el historial actualizado
joblib.dump(
st.session_state.messages,
f'data/{st.session_state.chat_id}-st_messages',
)
joblib.dump(
st.session_state.gemini_history,
f'data/{st.session_state.chat_id}-gemini_messages',
)
except ValueError as e:
st.error("Error: El mensaje no puede estar vacío. Por favor, escribe algo.")
# Limpiar el prompt del estado de la sesión
st.session_state.prompt = None
# Limpiar el prompt del estado de la sesión
st.session_state.prompt = None
|