JeCabrera's picture
Upload 12 files
ea00e74 verified
raw
history blame
12.5 kB
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, get_puv_expert_prompt
from session_state import SessionState
# Inicializar el estado de la sesión
state = SessionState()
# Función para detectar saludos y generar respuestas personalizadas
def is_greeting(text):
"""Detecta si el texto es un saludo simple"""
text = text.lower().strip()
greetings = ['hola', 'hey', 'saludos', 'buenos días', 'buenas tardes', 'buenas noches', 'hi', 'hello']
return any(greeting in text for greeting in greetings) and len(text.split()) < 4
def get_greeting_response():
"""Genera una respuesta amigable para saludos"""
return """¡Hola! 😊 ¡Qué bueno verte por aquí!
Soy RoboCopy, tu asistente personal para crear Propuestas Únicas de Valor que realmente conecten con tu audiencia.
Mi especialidad es ayudarte a destacar lo que hace único a tu negocio:
• Transformar tus características en beneficios irresistibles
• Crear mensajes que capturan la atención inmediata de tus clientes ideales
• Diferenciarte de la competencia con palabras que venden
¿Te gustaría que continuemos trabajando en tu Propuesta Única de Valor o tienes alguna otra pregunta?"""
# Función para procesar mensajes (unifica la lógica de procesamiento)
def process_message(prompt, is_example=False):
"""Procesa un mensaje del usuario, ya sea directo o de un ejemplo"""
# Guardar el chat para después si es nuevo
if state.chat_id not in past_chats.keys():
# Es una nueva conversación, generemos un título basado en el primer mensaje
temp_title = f'SesiónChat-{state.chat_id}'
past_chats[state.chat_id] = temp_title
# Generar título para el chat
generated_title = state.generate_chat_title(prompt)
# Actualizamos el título en past_chats
if generated_title:
state.chat_title = generated_title
past_chats[state.chat_id] = generated_title
else:
state.chat_title = temp_title
else:
# Ya existe esta conversación, usamos el título guardado
state.chat_title = past_chats[state.chat_id]
joblib.dump(past_chats, 'data/past_chats_list')
# Mostrar mensaje del usuario
with st.chat_message('user', avatar=USER_AVATAR_ICON):
st.markdown(prompt)
# Añadir mensaje del usuario al historial
state.add_message('user', prompt, USER_AVATAR_ICON)
# Verificar si es un saludo simple
if is_greeting(prompt):
# Mostrar respuesta personalizada para saludos
greeting_response = get_greeting_response()
with st.chat_message(name=MODEL_ROLE, avatar=AI_AVATAR_ICON):
st.markdown(greeting_response)
# Añadir respuesta al historial
state.add_message(
role=MODEL_ROLE,
content=greeting_response,
avatar=AI_AVATAR_ICON,
)
state.save_chat_history()
return
# Procesar respuesta del modelo
if prompt.strip():
# Obtener el prompt del experto
puv_expert_prompt = get_puv_expert_prompt()
# Combinar el prompt del experto con el mensaje del usuario
enhanced_prompt = f"{puv_expert_prompt}\n\nUser message: {prompt}"
with st.chat_message(MODEL_ROLE, avatar=AI_AVATAR_ICON):
try:
if is_example:
# Para ejemplos, no necesitamos streaming
response = state.chat.send_message(prompt)
st.markdown(response.text)
# Añadir respuesta al historial
state.add_message(MODEL_ROLE, response.text, AI_AVATAR_ICON)
else:
# Para entrada directa, usamos streaming
response = state.chat.send_message(
enhanced_prompt,
stream=True,
)
message_placeholder = st.empty()
full_response = ''
# Añadir indicador de "escribiendo..."
typing_indicator = st.empty()
typing_indicator.markdown("*Generando respuesta...*")
# Mostrar respuesta por fragmentos
for chunk in response:
for ch in chunk.text:
full_response += ch
time.sleep(0.01)
message_placeholder.write(full_response + '▌')
# Eliminar indicador de escritura
typing_indicator.empty()
# Mostrar respuesta completa
message_placeholder.write(full_response)
# Añadir respuesta al historial
state.add_message(
role=MODEL_ROLE,
content=state.chat.history[-1].parts[0].text,
avatar=AI_AVATAR_ICON,
)
state.gemini_history = state.chat.history
# Guardar historial actualizado
state.save_chat_history()
except ValueError as e:
st.error("Error: El mensaje no puede estar vacío. Por favor, escribe algo.")
# 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)
# Función de utilidad para mostrar la carátula inicial
def display_initial_header():
col1, col2, col3 = st.columns([1, 2, 1])
with col2:
# Centrar la imagen
st.markdown("""
<style>
div.stImage {
text-align: center;
display: block;
margin-left: auto;
margin-right: auto;
}
</style>
""", unsafe_allow_html=True)
st.image("robocopy_logo.png", width=300, use_container_width=True)
# Título
st.markdown("""
<div style='text-align: center; margin-top: -35px; width: 100%;'>
<h1 class='robocopy-title' style='width: 100%; text-align: center;'>PUV Creator</h1>
</div>
""", unsafe_allow_html=True)
# Subtítulo
st.markdown("""
<div style='text-align: center; width: 100%;'>
<p style='font-size: 16px; color: #4ECDC4; width: 100%; text-align: center;'>By Jesús Cabrera</p>
</div>
""", unsafe_allow_html=True)
# Descripción (ahora fuera de la columna para ocupar todo el ancho)
st.markdown("""
<div style='text-align: center; width: 100%;'>
<p style='font-size: 16px; background-color: #1E3A5F; padding: 12px; border-radius: 8px; margin-top: 10px; color: #4ECDC4; width: 100%; text-align: center; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);'>
🎯 Experto en crear Propuestas de Valor Únicas que convierten audiencia en clientes
</p>
</div>
""", unsafe_allow_html=True)
# Función para mostrar ejemplos de preguntas
def display_examples():
ejemplos = [
{"texto": "¿Qué es una Propuesta de Valor Única? 🎯", "prompt": "Explícame qué es una Propuesta de Valor Única (PUV) y por qué es importante para mi negocio"},
{"texto": "¿Cómo puedo crear mi PUV? 📝", "prompt": "Guíame paso a paso en el proceso de crear una Propuesta de Valor Única efectiva"},
{"texto": "¿Qué elementos debe tener mi PUV? ✨", "prompt": "¿Cuáles son los elementos esenciales que debe incluir una Propuesta de Valor Única exitosa?"},
{"texto": "¿Cuál es la mejor fórmula para mi caso? 🤔", "prompt": "Ayúdame a elegir la fórmula más adecuada para mi Propuesta de Valor según mi tipo de negocio"}
]
# 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"]):
state.prompt = ejemplo["prompt"]
st.rerun()
# Cargar variables de entorno
load_dotenv()
GOOGLE_API_KEY=os.environ.get('GOOGLE_API_KEY')
genai.configure(api_key=GOOGLE_API_KEY)
# Configuración de la aplicación
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
# Crear carpeta de datos si no existe
try:
os.mkdir('data/')
except:
# data/ folder already exists
pass
# Cargar chats anteriores
try:
past_chats: dict = joblib.load('data/past_chats_list')
except:
past_chats = {}
# Sidebar para seleccionar chats anteriores
with st.sidebar:
st.write('# Chats Anteriores')
if state.chat_id is None:
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
state.chat_id = st.selectbox(
label='Selecciona un chat anterior',
options=[new_chat_id, state.chat_id] + list(past_chats.keys()),
index=1,
format_func=lambda x: past_chats.get(x, 'Nuevo Chat' if x != state.chat_id else state.chat_title),
placeholder='_',
)
# Save new chats after a message has been sent to AI
state.chat_title = f'SesiónChat-{state.chat_id}'
# Cargar historial del chat
state.load_chat_history()
# Inicializar el modelo
state.initialize_model('gemini-2.0-flash')
state.initialize_chat()
# Mostrar mensajes del historial
for message in 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 state.has_messages():
# Mostrar la carátula inicial con el logo centrado
display_initial_header()
# Mostrar los ejemplos
display_examples()
# Inicializar el chat con los prompts del sistema
system_prompt = get_puv_system_prompt()
expert_prompt = get_puv_expert_prompt()
state.chat.send_message(system_prompt)
state.chat.send_message(expert_prompt)
# Solo añadir el mensaje inicial si no es un ejemplo
if not state.has_prompt():
state.add_message(
role=MODEL_ROLE,
content="""👋 Hola, soy RoboCopy.
Tu asistente especializado en crear Propuestas de Valor Únicas (PUVs) que convierten visitantes en clientes.
Puedo ayudarte a:
✅ Crear PUVs impactantes usando diferentes fórmulas
✅ Analizar tu producto/servicio para destacar su valor único
✅ Identificar los elementos clave que atraerán a tu audiencia
✅ Optimizar tu mensaje para diferentes segmentos de mercado
Para empezar a crear PUVs efectivas, necesito conocer:
- ¿Qué producto o servicio ofreces?
- ¿A quién va dirigido? (describe tu público objetivo)
Espero ansioso tu respuesta, para crear tu Propuesta Única de Valor.""",
avatar=AI_AVATAR_ICON,
)
# Procesar entrada del usuario
if prompt := st.chat_input('Describe tu producto/servicio y audiencia objetivo...'):
process_message(prompt, is_example=False)
# Procesar ejemplos seleccionados
if state.has_prompt():
prompt = state.prompt
process_message(prompt, is_example=True)
# Limpiar el prompt
state.clear_prompt()