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, 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() |