Spaces:
Running
Running
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 | |
# Configuración inicial del chat | |
if not st.session_state.messages: | |
system_prompt = get_puv_system_prompt() | |
with st.chat_message( | |
name=MODEL_ROLE, | |
avatar=AI_AVATAR_ICON, | |
): | |
st.markdown(""" | |
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? | |
""") | |
# 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) | |
# 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', | |
) | |