Spaces:
Runtime error
Runtime error
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 | |
# Importar el diccionario de fórmulas | |
from puv_formulas import puv_formulas | |
# Función para obtener descripciones de fórmulas para el prompt | |
def get_formulas_for_prompt(): | |
prompt_text = "FÓRMULAS DE PROPUESTAS ÚNICAS DE VALOR (PUVs):\n\n" | |
for key, formula in puv_formulas.items(): | |
prompt_text += f"FÓRMULA {key}:\n" | |
prompt_text += f"- Descripción: {formula['description']}\n" | |
prompt_text += "- Ejemplos:\n" | |
# Añadir algunos ejemplos (limitados para no hacer el prompt demasiado largo) | |
for i, example in enumerate(formula['examples'][:2]): | |
prompt_text += f" * Ejemplo {i+1}: {example['uvp']}\n" | |
prompt_text += "\n" | |
return prompt_text | |
# Definición del prompt multipersona para el sistema | |
SYSTEM_PROMPT = f""" | |
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 | |
{get_formulas_for_prompt()} | |
INSTRUCCIONES PARA CREAR PUVs: | |
1. Si el usuario no ha proporcionado información sobre su producto/servicio y audiencia objetivo, solicítala de manera amable y directa. | |
2. Si el usuario ha proporcionado información sobre su producto/servicio y audiencia objetivo, pero no ha elegido una fórmula específica, pregúntale qué fórmula le gustaría utilizar y cuántos ejemplos desea. Presenta las opciones disponibles: | |
- Fórmula Tradicional | |
- Fórmula Anti-tradicional | |
- Contrato Imposible | |
- Reto Ridículo | |
3. Una vez que el usuario haya proporcionado toda la información necesaria (producto/servicio, audiencia objetivo, fórmula elegida y número de ejemplos), procede a: | |
a. Analizar cuidadosamente la información del producto/servicio y la audiencia objetivo | |
b. Identificar los problemas principales, deseos y objeciones de la audiencia | |
c. Determinar los beneficios clave y elementos diferenciadores del producto/servicio | |
d. Crear el número solicitado de propuestas de valor utilizando ÚNICAMENTE la fórmula elegida por el usuario | |
e. Para cada propuesta, explicar por qué es efectiva y cómo se conecta con la audiencia | |
ANÁLISIS INTERNO DEL PÚBLICO OBJETIVO: | |
Para cada solicitud, realiza un análisis interno (no lo incluyas en la respuesta) de los siguientes puntos: | |
1. ANÁLISIS DEL PÚBLICO OBJETIVO - Puntos de Dolor: | |
- ¿Qué frustraciones específicas experimenta esta audiencia? | |
- ¿Cuáles son sus mayores desafíos diarios? | |
- ¿Qué problemas emocionales enfrentan? | |
- ¿Qué han intentado antes que no funcionó? | |
- ¿Qué les impide alcanzar sus objetivos? | |
2. ANÁLISIS DEL PRODUCTO/SERVICIO - Beneficios: | |
- ¿Qué resultados tangibles obtienen los clientes? | |
- ¿Qué transformación específica ofrece? | |
- ¿Cuál es el método único o diferenciador? | |
- ¿Qué ventajas competitivas tiene? | |
- ¿Qué beneficios emocionales proporciona? | |
INSTRUCCIONES CRÍTICAS: | |
- Cada PUV debe ser específica y medible | |
- Enfócate en el viaje de transformación | |
- Usa lenguaje natural y conversacional | |
- Evita frases genéricas y palabras de moda | |
- Máximo 2 líneas por PUV | |
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. | |
""" | |
# 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? | |
3️⃣ **Fórmula de PUV**: ¿Qué tipo de fórmula prefieres? | |
4️⃣ **Número de ejemplos**: ¿Cuántos ejemplos te gustaría recibir? | |
### Fórmulas disponibles: | |
- **Fórmula Tradicional**: Comienza con "Yo ayudo a..." seguido de avatar y transformación | |
- **Fórmula Anti-tradicional**: Usa frases como "Yo transformo...", "Me especializo en..." | |
- **Contrato Imposible**: Usa frases como "¿Te imaginas poder...", "Soy el antídoto para..." | |
- **Reto Ridículo**: Usa anécdotas personales y humor para conectar con la audiencia | |
**¡Comencemos!** Comparte los detalles de tu producto/servicio y audiencia objetivo. | |
""" | |
# Función para mostrar texto con efecto de escritura | |
def mostrar_con_efecto_escritura(mensaje, velocidad=0.05): | |
with st.chat_message(name=MODEL_ROLE, avatar=AI_AVATAR_ICON): | |
message_placeholder = st.empty() | |
full_response = '' | |
# Indicador de escritura | |
typing_indicator = st.empty() | |
typing_indicator.markdown("*Estoy analizando tu información...*") | |
# Mostrar respuesta por fragmentos | |
for palabra in mensaje.split(' '): | |
full_response += palabra + ' ' | |
time.sleep(velocidad) | |
message_placeholder.write(full_response + '▌') | |
# Eliminar indicador y mostrar respuesta completa | |
typing_indicator.empty() | |
message_placeholder.write(mensaje) | |
return mensaje | |
# 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: | |
with st.chat_message(name=MODEL_ROLE, avatar=AI_AVATAR_ICON): | |
st.markdown(WELCOME_MESSAGE) | |
st.session_state.messages.append({ | |
'role': MODEL_ROLE, | |
'content': WELCOME_MESSAGE, | |
'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, velocidad=0.05) | |
# 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 | |