Spaces:
Running
Running
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 | |
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 | |
""" | |
# Mensaje de bienvenida mejorado | |
WELCOME_MESSAGE = """ | |
# 🚀 ¡Bienvenido a RoboCopy PUV Creator! 🚀 | |
Somos un equipo de expertos en marketing y psicología de audiencia, especializados en crear **Propuestas Únicas de Valor (PUVs)** que transforman visitantes en clientes. | |
## 💼 ¿Qué podemos hacer por ti? | |
Creamos PUVs poderosas y persuasivas que: | |
- **Capturan la atención** de tu audiencia ideal | |
- **Comunican claramente** el valor único de tu oferta | |
- **Convierten visitantes** en clientes leales | |
## 📋 Para ayudarte, necesitamos conocer: | |
1️⃣ **Tu producto o servicio**: ¿Qué ofreces exactamente? Cuéntanos sus características principales. | |
2️⃣ **Tu audiencia objetivo**: ¿Quiénes son tus clientes ideales? Sé lo más específico posible. | |
3️⃣ **La fórmula que prefieres**: | |
- **A) Problema-Solución**: "Para [audiencia] que sufre [problema], [producto] proporciona [solución]" | |
- **B) Antes-Después**: "Transforma [situación actual] en [situación deseada] con [producto]" | |
- **C) Beneficio Principal**: "[Producto] te ayuda a [beneficio principal] sin [obstáculo común]" | |
## 🔍 Nuestro proceso: | |
1. Analizaremos los puntos de dolor de tu audiencia | |
2. Identificaremos los beneficios clave de tu producto/servicio | |
3. Crearemos TRES propuestas únicas de valor potentes y persuasivas | |
**¡Comencemos!** Por favor, comparte los detalles de tu producto/servicio, audiencia objetivo y la fórmula que prefieres. | |
""" | |
# 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 si es un chat nuevo | |
if not st.session_state.get('messages'): | |
st.session_state.messages = [] | |
# Eliminamos el mensaje automático de bienvenida | |
# No añadimos ningún mensaje inicial | |
with st.chat_message(name=MODEL_ROLE, avatar=AI_AVATAR_ICON): | |
st.markdown(WELCOME_MESSAGE) | |
# Guardamos en el historial | |
st.session_state.messages.append( | |
dict( | |
role=MODEL_ROLE, | |
content=WELCOME_MESSAGE, | |
avatar=AI_AVATAR_ICON, | |
) | |
) | |
# 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: | |
if not st.session_state.get('messages'): | |
st.session_state.messages = [] | |
st.session_state.gemini_history = [] | |
print('new_cache made') | |
# Configuración del modelo sin el parámetro system_instruction | |
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) | |
# No guardamos este mensaje en st.session_state.messages para que no se muestre al usuario | |
# pero sí en gemini_history para que el modelo lo recuerde | |
st.session_state.gemini_history = st.session_state.chat.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']) | |
# React to user input | |
if prompt := st.chat_input('¿Cuál es tu producto o servicio?'): # Mensaje más específico | |
# Verificar si es la primera interacción y el usuario pregunta por las funciones | |
is_asking_about_functions = any(keyword in prompt.lower() for keyword in | |
["qué haces", "funciones", "ayudar", "puedes hacer", "cómo funciona", "para qué sirves"]) | |
if is_asking_about_functions and not past_chats.get(st.session_state.chat_id): | |
# Si pregunta por las funciones, mostrar el mensaje de bienvenida | |
response_content = WELCOME_MESSAGE | |
# Display assistant response in chat message container | |
with st.chat_message(name=MODEL_ROLE, avatar=AI_AVATAR_ICON): | |
st.markdown(response_content) | |
# Add to chat history | |
st.session_state.messages.append( | |
dict( | |
role=MODEL_ROLE, | |
content=response_content, | |
avatar=AI_AVATAR_ICON, | |
) | |
) | |
else: | |
# Procesamiento normal para otras preguntas | |
# 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'PUV-{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é producto o servicio 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 = f"PUV: {generated_title}" | |
past_chats[st.session_state.chat_id] = f"PUV: {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, | |
) | |
) | |
# Implementación de reintentos con retroceso exponencial | |
max_retries = 3 | |
retry_count = 0 | |
while retry_count < max_retries: | |
try: | |
## Send message to AI | |
response = st.session_state.chat.send_message( | |
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("*Nuestro equipo de expertos está analizando tu información...*") | |
# Streams in a chunk at a time | |
for chunk in response: | |
# Simulate stream of chunk | |
for ch in chunk.text.split(' '): | |
full_response += ch + ' ' | |
time.sleep(0.1) | |
# 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', | |
) | |
# Si llegamos aquí, la solicitud fue exitosa | |
break | |
except Exception as e: | |
retry_count += 1 | |
if retry_count >= max_retries: | |
# Si agotamos los reintentos, mostramos un mensaje de error | |
with st.chat_message(name=MODEL_ROLE, avatar=AI_AVATAR_ICON): | |
st.error(f"Lo sentimos, estamos experimentando problemas para procesar tu solicitud. Por favor, intenta de nuevo más tarde. Error: {str(e)}") | |
st.session_state.messages.append( | |
dict( | |
role=MODEL_ROLE, | |
content=f"Lo sentimos, estamos 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: | |
# Esperamos antes de reintentar (retroceso exponencial) | |
wait_time = (2 ** retry_count) + (time.time() % 1) | |
time.sleep(wait_time) |