JeCabrera's picture
Upload app.py
d99c92d verified
raw
history blame
12 kB
import time
import os
import joblib
import streamlit as st
import google.generativeai as genai
from dotenv import load_dotenv
# 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
# Definición del prompt multipersona para el sistema
# Definición del prompt multipersona para el sistema
SYSTEM_PROMPT = """
Eres un equipo colaborativo de expertos de clase mundial trabajando juntos para crear Propuestas Únicas de Valor (PUVs) excepcionales que conviertan a la audiencia en clientes.
EL EQUIPO DE EXPERTOS:
1. ESTRATEGA MAESTRO DE MARKETING:
- Experto en marcos de propuestas de valor y estrategias de conversión
- Asegura que las PUVs sigan la estructura de fórmula seleccionada con precisión
- Se enfoca en la colocación estratégica de elementos clave de conversión
2. COPYWRITER ELITE DE RESPUESTA DIRECTA:
- Crea ganchos, historias y elementos persuasivos convincentes
- Elabora propuestas de valor irresistibles que impulsan conversiones
- Asegura que el lenguaje resuene con la audiencia objetivo
3. ESPECIALISTA EN PSICOLOGÍA DE AUDIENCIA:
- Experto en comprender las motivaciones y objeciones de la audiencia
- Crea contenido que construye conexión genuina y confianza
- Identifica y aborda miedos y deseos ocultos
4. MAESTRO DE DIFERENCIACIÓN:
- Crea propuestas únicas que destacan entre la competencia
- Desarrolla ejemplos y casos de estudio relacionables
- Asegura que las PUVs apoyen la transformación que se ofrece
5. EXPERTO EN CONVERSIÓN:
- Se especializa en crear PUVs que mantengan la atención y generen acción
- Crea elementos interactivos y ganchos de engagement
- Asegura que las PUVs fluyan naturalmente y mantengan el interés alto
IMPORTANTE: Cuando el usuario pregunte por tus funciones o capacidades, responde siempre en primera persona singular (yo hago, yo creo, yo analizo) de forma breve y concisa. No menciones que eres un equipo de expertos, sino un asistente especializado en crear PUVs.
Si el usuario no proporciona información suficiente sobre su producto/servicio o audiencia objetivo, solicítala de manera amable y directa.
"""
# Mensaje de bienvenida mejorado
WELCOME_MESSAGE = """
# 🚀 ¡Bienvenido! Soy RoboCopy, tu creador de PUVs 🚀
Soy un experto en crear **Propuestas Únicas de Valor (PUVs)** que transforman visitantes en clientes.
## 💼 ¿Qué puedo hacer por ti?
Creo PUVs persuasivas que:
- **Capturan la atención** de tu audiencia ideal
- **Comunican claramente** el valor de tu oferta
- **Convierten visitantes** en clientes leales
## 📋 Para ayudarte, necesito conocer:
1️⃣ **Tu producto o servicio**: ¿Qué ofreces exactamente?
2️⃣ **Tu audiencia objetivo**: ¿Quiénes son tus clientes ideales?
Analizaré tu audiencia, identificaré los beneficios clave y crearé TRES propuestas de valor potentes siguiendo estas fórmulas:
- **Problema-Solución**: Para tu audiencia con su problema, tu producto proporciona la solución
- **Antes-Después**: Transforma la situación actual en la situación deseada
- **Beneficio Principal**: Tu producto ayuda a conseguir el beneficio sin el obstáculo común
**¡Comencemos!** Comparte los detalles de tu producto/servicio y audiencia objetivo.
"""
# 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='_',
)
# Inicializar el título de chat si es necesario
if st.session_state.get('chat_title') is None:
if st.session_state.chat_id in past_chats:
st.session_state.chat_title = past_chats[st.session_state.chat_id]
else:
st.session_state.chat_title = 'Nuevo Chat'
st.write('# Creador de Propuestas Únicas de Valor')
# Inicializar mensajes y cargar historial
if not st.session_state.get('messages'):
st.session_state.messages = []
# Cargar historial de chat si existe
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:
if not st.session_state.get('messages'):
st.session_state.messages = []
st.session_state.gemini_history = []
print('new_cache made')
# Configuración del modelo
model = genai.GenerativeModel(model_name='gemini-2.0-flash')
st.session_state.model = model
st.session_state.chat = st.session_state.model.start_chat(history=st.session_state.gemini_history)
# Si es un chat nuevo, enviar el prompt del sistema como primer mensaje
if not st.session_state.gemini_history:
# Enviamos el prompt del sistema como primer mensaje (invisible para el usuario)
st.session_state.chat.send_message(SYSTEM_PROMPT)
st.session_state.gemini_history = st.session_state.chat.history
# Mostrar mensajes del historial
for message in st.session_state.messages:
with st.chat_message(name=message['role'], avatar=message.get('avatar')):
st.markdown(message['content'])
# React to user input
if prompt := st.chat_input('¿En qué puedo ayudarte hoy?'):
# Verificar si es el primer mensaje del usuario
is_first_message = len(st.session_state.messages) == 0
# Guardar información del chat
if st.session_state.chat_id not in past_chats.keys():
# Es una nueva conversación, generamos un título basado en el primer mensaje
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:
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}'")
generated_title = title_response.text.strip()
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')
# Mostrar mensaje del usuario
with st.chat_message('user', avatar=USER_AVATAR_ICON):
st.markdown(prompt)
# Añadir mensaje del usuario al historial
st.session_state.messages.append({
'role': 'user',
'content': prompt,
})
# Si es el primer mensaje, mostrar el mensaje de bienvenida
if is_first_message:
mensaje_mostrado = mostrar_con_efecto_escritura(WELCOME_MESSAGE, velocidad=0.05)
st.session_state.messages.append({
'role': MODEL_ROLE,
'content': mensaje_mostrado,
'avatar': AI_AVATAR_ICON,
})
# Guardar el historial actualizado
joblib.dump(st.session_state.messages, f'data/{st.session_state.chat_id}-st_messages')
# Salir de la función para no procesar más este mensaje
st.rerun()
# Implementación de reintentos con retroceso exponencial
max_retries = 3
retry_count = 0
while retry_count < max_retries:
try:
# Enviar mensaje al modelo
response = st.session_state.chat.send_message(prompt, stream=True)
# Procesar la respuesta completa
full_text = ""
for chunk in response:
full_text += chunk.text
# Mostrar respuesta del asistente con efecto de escritura
mensaje_mostrado = mostrar_con_efecto_escritura(full_text)
# Añadir respuesta al historial
st.session_state.messages.append({
'role': MODEL_ROLE,
'content': mensaje_mostrado,
'avatar': AI_AVATAR_ICON,
})
# Actualizar historial
st.session_state.gemini_history = st.session_state.chat.history
# Guardar historial
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')
# Salir del bucle si la solicitud fue exitosa
break
except Exception as e:
retry_count += 1
if retry_count >= max_retries:
# Mostrar mensaje de error si se agotan los reintentos
with st.chat_message(name=MODEL_ROLE, avatar=AI_AVATAR_ICON):
st.error(f"Lo siento, estoy experimentando problemas para procesar tu solicitud. Por favor, intenta de nuevo más tarde. Error: {str(e)}")
st.session_state.messages.append({
'role': MODEL_ROLE,
'content': f"Lo siento, estoy experimentando problemas para procesar tu solicitud. Por favor, intenta de nuevo más tarde. Error: {str(e)}",
'avatar': AI_AVATAR_ICON,
})
joblib.dump(st.session_state.messages, f'data/{st.session_state.chat_id}-st_messages')
else:
# Esperar antes de reintentar (retroceso exponencial)
wait_time = (2 ** retry_count) + (time.time() % 1)
time.sleep(wait_time)
# Salir del bucle si la solicitud fue exitosa
break