Spaces:
Sleeping
Sleeping
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 | |
# 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='_', | |
) | |
# Save new chats after a message has been sent to AI | |
# TODO: Give user a chance to name chat | |
st.session_state.chat_title = f'SesiónChat-{st.session_state.chat_id}' | |
st.write('# Chatea con Gemini') | |
# 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: | |
st.session_state.messages = [] | |
st.session_state.gemini_history = [] | |
print('new_cache made') | |
st.session_state.model = genai.GenerativeModel('gemini-2.0-flash') | |
st.session_state.chat = st.session_state.model.start_chat( | |
history=st.session_state.gemini_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']) | |
# Inicializar variables de estado | |
if 'show_examples' not in st.session_state: | |
st.session_state.show_examples = True | |
if 'selected_persona' not in st.session_state: | |
st.session_state.selected_persona = "estratega" | |
# Función para seleccionar persona | |
def select_puv_persona(): | |
st.sidebar.write("## 🧠 Selecciona un Experto") | |
# Inicializar la selección de persona si no existe | |
if 'selected_persona' not in st.session_state: | |
st.session_state.selected_persona = "estratega" | |
# Crear botones para cada persona | |
cols = st.sidebar.columns(len(PUV_PERSONAS)) | |
for i, (persona_id, persona) in enumerate(PUV_PERSONAS.items()): | |
with cols[i]: | |
if st.button(f"{persona['emoji']}\n{persona['nombre']}", key=f"btn_{persona_id}"): | |
st.session_state.selected_persona = persona_id | |
st.rerun() | |
# Mostrar descripción de la persona seleccionada | |
persona_actual = PUV_PERSONAS[st.session_state.selected_persona] | |
st.sidebar.markdown(f"**{persona_actual['emoji']} {persona_actual['nombre']}**") | |
st.sidebar.markdown(f"_{persona_actual['descripcion']}_") | |
return st.session_state.selected_persona | |
# Función para modificar el prompt con la personalidad seleccionada | |
def apply_persona_to_prompt(prompt, persona_id): | |
if persona_id in PUV_PERSONAS: | |
persona = PUV_PERSONAS[persona_id] | |
return f"{persona['prompt_prefix']}\n\nConsulta original: {prompt}" | |
return prompt | |
# Función para manejar mensajes | |
def add_message(role, content, avatar=None): | |
message = { | |
'role': role, | |
'content': content | |
} | |
if avatar: | |
message['avatar'] = avatar | |
st.session_state.messages.append(message) | |
return message | |
# Función para guardar el estado del chat | |
def save_chat_state(): | |
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', | |
) | |
# Función para generar título de chat | |
def generate_chat_title(prompt): | |
temp_title = f'SesiónChat-{st.session_state.chat_id}' | |
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: | |
return generated_title | |
except Exception as e: | |
print(f"Error al generar título: {e}") | |
return temp_title | |
# Función para operaciones con manejo de errores | |
def safe_operation(operation, default_value, error_message="Error en operación"): | |
try: | |
return operation() | |
except Exception as e: | |
print(f"{error_message}: {e}") | |
return default_value | |
# Definición de perfiles de expertos en PUV | |
PUV_PERSONAS = { | |
"estratega": { | |
"nombre": "Estratega de Marketing", | |
"descripcion": "Experto en posicionamiento estratégico y diferenciación de marca", | |
"prompt_prefix": "Como estratega de marketing especializado en posicionamiento de marca, voy a ayudarte a crear una PUV que destaque tu ventaja competitiva. ", | |
"emoji": "🎯" | |
}, | |
"copywriter": { | |
"nombre": "Copywriter Persuasivo", | |
"descripcion": "Especialista en redacción persuasiva y mensajes de alto impacto", | |
"prompt_prefix": "Como copywriter especializado en mensajes persuasivos, voy a ayudarte a crear una PUV que conecte emocionalmente con tu audiencia. ", | |
"emoji": "✍️" | |
}, | |
"analista": { | |
"nombre": "Analista de Mercado", | |
"descripcion": "Experto en análisis de mercado y comportamiento del consumidor", | |
"prompt_prefix": "Como analista de mercado especializado en comportamiento del consumidor, voy a ayudarte a crear una PUV basada en insights de tu audiencia. ", | |
"emoji": "📊" | |
}, | |
"innovador": { | |
"nombre": "Innovador Disruptivo", | |
"descripcion": "Especialista en propuestas innovadoras y disruptivas", | |
"prompt_prefix": "Como especialista en innovación disruptiva, voy a ayudarte a crear una PUV que rompa con los paradigmas de tu industria. ", | |
"emoji": "💡" | |
} | |
} | |
# Modificar el procesamiento del prompt | |
if prompt := st.chat_input('¿En qué puedo ayudarte hoy con tu Propuesta Única de Valor?'): | |
# Guardar el prompt original | |
original_prompt = prompt | |
# Aplicar la personalidad seleccionada al prompt | |
enhanced_prompt = apply_persona_to_prompt(prompt, st.session_state.selected_persona) | |
# Generar título para el chat si es nuevo | |
if st.session_state.chat_id not in past_chats.keys(): | |
st.session_state.chat_title = generate_chat_title(original_prompt) | |
past_chats[st.session_state.chat_id] = st.session_state.chat_title | |
else: | |
st.session_state.chat_title = past_chats[st.session_state.chat_id] | |
joblib.dump(past_chats, 'data/past_chats_list') | |
# Mostrar mensaje del usuario (siempre el original) | |
with st.chat_message('user', avatar=USER_AVATAR_ICON): | |
st.markdown(original_prompt) | |
# Añadir mensaje a la historia | |
add_message('user', original_prompt, USER_AVATAR_ICON) | |
# Enviar el prompt mejorado al modelo | |
response = st.session_state.chat.send_message( | |
enhanced_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("*RoboCopy está escribiendo...*") | |
# Streams in a chunk at a time | |
for chunk in response: | |
# Simulate stream of chunk | |
for word in chunk.text.split(' '): | |
full_response += word + ' ' | |
time.sleep(0.1) # Velocidad ajustada para mejor legibilidad | |
# 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 | |
# Función para manejar mensajes | |
def add_message(role, content, avatar=None): | |
message = { | |
'role': role, | |
'content': content | |
} | |
if avatar: | |
message['avatar'] = avatar | |
st.session_state.messages.append(message) | |
return message | |
# Función para guardar el estado del chat | |
def save_chat_state(): | |
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', | |
) | |
# Función para generar título de chat | |
def generate_chat_title(prompt): | |
temp_title = f'SesiónChat-{st.session_state.chat_id}' | |
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: | |
return generated_title | |
except Exception as e: | |
print(f"Error al generar título: {e}") | |
return temp_title | |
# Función para operaciones con manejo de errores | |
def safe_operation(operation, default_value, error_message="Error en operación"): | |
try: | |
return operation() | |
except Exception as e: | |
print(f"{error_message}: {e}") | |
return default_value | |
# Definición de perfiles de expertos en PUV | |
PUV_PERSONAS = { | |
"estratega": { | |
"nombre": "Estratega de Marketing", | |
"descripcion": "Experto en posicionamiento estratégico y diferenciación de marca", | |
"prompt_prefix": "Como estratega de marketing especializado en posicionamiento de marca, voy a ayudarte a crear una PUV que destaque tu ventaja competitiva. ", | |
"emoji": "🎯" | |
}, | |
"copywriter": { | |
"nombre": "Copywriter Persuasivo", | |
"descripcion": "Especialista en redacción persuasiva y mensajes de alto impacto", | |
"prompt_prefix": "Como copywriter especializado en mensajes persuasivos, voy a ayudarte a crear una PUV que conecte emocionalmente con tu audiencia. ", | |
"emoji": "✍️" | |
}, | |
"analista": { | |
"nombre": "Analista de Mercado", | |
"descripcion": "Experto en análisis de mercado y comportamiento del consumidor", | |
"prompt_prefix": "Como analista de mercado especializado en comportamiento del consumidor, voy a ayudarte a crear una PUV basada en insights de tu audiencia. ", | |
"emoji": "📊" | |
}, | |
"innovador": { | |
"nombre": "Innovador Disruptivo", | |
"descripcion": "Especialista en propuestas innovadoras y disruptivas", | |
"prompt_prefix": "Como especialista en innovación disruptiva, voy a ayudarte a crear una PUV que rompa con los paradigmas de tu industria. ", | |
"emoji": "💡" | |
} | |
} | |
# Función para seleccionar persona | |
def select_puv_persona(): | |
st.sidebar.write("## 🧠 Selecciona un Experto") | |
# Inicializar la selección de persona si no existe | |
if 'selected_persona' not in st.session_state: | |
st.session_state.selected_persona = "estratega" | |
# Crear botones para cada persona | |
cols = st.sidebar.columns(len(PUV_PERSONAS)) | |
for i, (persona_id, persona) in enumerate(PUV_PERSONAS.items()): | |
with cols[i]: | |
if st.button(f"{persona['emoji']}\n{persona['nombre']}", key=f"btn_{persona_id}"): | |
st.session_state.selected_persona = persona_id | |
st.rerun() | |
# Mostrar descripción de la persona seleccionada | |
persona_actual = PUV_PERSONAS[st.session_state.selected_persona] | |
st.sidebar.markdown(f"**{persona_actual['emoji']} {persona_actual['nombre']}**") | |
st.sidebar.markdown(f"_{persona_actual['descripcion']}_") | |
return st.session_state.selected_persona | |
# Función para modificar el prompt con la personalidad seleccionada | |
def apply_persona_to_prompt(prompt, persona_id): | |
if persona_id in PUV_PERSONAS: | |
persona = PUV_PERSONAS[persona_id] | |
return f"{persona['prompt_prefix']}\n\nConsulta original: {prompt}" | |
return prompt | |