Spaces:
Sleeping
Sleeping
File size: 18,070 Bytes
3b22c40 00b706b 3b22c40 00b706b 3b22c40 00b706b 3b22c40 00b706b 3b22c40 00b706b 3b22c40 00b706b 3b22c40 00b706b 3b22c40 00b706b 3b22c40 00b706b 3b22c40 00b706b 3b22c40 00b706b 3b22c40 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 |
import time
import os
import joblib
import streamlit as st
import google.generativeai as genai
from dotenv import load_dotenv
from reels_formulas import reels_formulas
from system_prompts import get_reels_script_prompt
from session_state import SessionState
# Configuración de la página - DEBE SER LA PRIMERA LLAMADA A STREAMLIT
st.set_page_config(
page_title="RoboCopy - Reels Creator",
page_icon="🎬",
layout="wide",
initial_sidebar_state="expanded"
)
# 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']
# Solo considerar como saludo si es el primer mensaje del usuario
# y es un saludo simple
is_simple_greeting = any(greeting in text for greeting in greetings) and len(text.split()) < 4
return is_simple_greeting and len(state.messages) == 0
# 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"""
handle_chat_title(prompt)
with st.chat_message('user', avatar=USER_AVATAR_ICON):
st.markdown(prompt)
# Añadir el mensaje del usuario al historial
state.add_message('user', prompt, USER_AVATAR_ICON)
# Guardar el historial después de cada mensaje del usuario
state.gemini_history = state.chat.history if state.chat else []
save_state()
# Obtener el prompt mejorado primero
enhanced_prompt = get_enhanced_prompt(prompt, is_example)
# Mover la respuesta del modelo después del mensaje del usuario
with st.chat_message(MODEL_ROLE, avatar=AI_AVATAR_ICON):
try:
message_placeholder = st.empty()
typing_indicator = st.empty()
typing_indicator.markdown("*Generando respuesta...*")
response = state.send_message(enhanced_prompt)
full_response = stream_response(response, message_placeholder, typing_indicator)
if full_response:
state.add_message(MODEL_ROLE, full_response, AI_AVATAR_ICON)
state.gemini_history = state.chat.history if state.chat else []
save_state()
except Exception as e:
st.error(f"Error en el streaming: {str(e)}")
return
def handle_chat_title(prompt):
"""Maneja la lógica del título del chat"""
if state.chat_id not in past_chats:
temp_title = f'SesiónChat-{state.chat_id}'
generated_title = state.generate_chat_title(prompt)
state.chat_title = generated_title or temp_title
past_chats[state.chat_id] = state.chat_title
else:
state.chat_title = past_chats[state.chat_id]
joblib.dump(past_chats, 'data/past_chats_list')
def get_enhanced_prompt(prompt, is_example):
"""Genera el prompt mejorado según el tipo de mensaje"""
# Importar las preguntas directamente desde system_prompts.py
from system_prompts import get_discovery_questions
# Obtener la lista de preguntas
discovery_questions = get_discovery_questions()
# Analizar el historial completo para determinar en qué etapa estamos
conversation_history = [msg["content"] for msg in state.messages]
# Verificar si ya hemos hecho ciertas preguntas y recibido respuestas
audiencia_preguntada = any(discovery_questions[0] in msg for msg in conversation_history)
producto_preguntado = any(discovery_questions[1] in msg for msg in conversation_history)
problema_preguntado = any(discovery_questions[2] in msg for msg in conversation_history)
accion_preguntada = any(discovery_questions[3] in msg for msg in conversation_history)
# Verificar si el usuario ya ha respondido a las preguntas
# Buscamos respuestas del usuario después de cada pregunta
user_messages = [msg["content"] for msg in state.messages if msg["role"] == "user"]
model_messages = [msg["content"] for msg in state.messages if msg["role"] == "model"]
# Determinar qué preguntas ya han sido respondidas
audiencia_respondida = audiencia_preguntada and len(user_messages) > 1
producto_respondido = producto_preguntado and len(user_messages) > 2
problema_respondido = problema_preguntado and len(user_messages) > 3
accion_respondida = accion_preguntada and len(user_messages) > 4
# Imprimir información de depuración
print(f"Estado de preguntas: Audiencia={audiencia_preguntada}/{audiencia_respondida}, Producto={producto_preguntado}/{producto_respondido}, Problema={problema_preguntado}/{problema_respondido}, Acción={accion_preguntada}/{accion_respondida}")
print(f"Mensajes de usuario: {len(user_messages)}, Mensajes del modelo: {len(model_messages)}")
if is_greeting(prompt):
return f"El usuario te ha saludado con '{prompt}'. Preséntate brevemente (máximo 2 líneas), explica qué es un guion de Reel en 1 línea, y haz ÚNICAMENTE esta pregunta: '{discovery_questions[0]}'. Recuerda hablar siempre en segunda persona, dirigiéndote al usuario. NUNCA hables en primera persona como si fueras tú quien crea el Reel."
elif is_example:
return f"El usuario ha seleccionado un ejemplo: '{prompt}'. Responde de manera breve y directa, y haz ÚNICAMENTE esta pregunta: '{discovery_questions[0]}'. Recuerda hablar siempre en segunda persona, dirigiéndote al usuario. NUNCA hables en primera persona como si fueras tú quien crea el Reel."
else:
# Determinar la siguiente pregunta basada en el historial completo
if not audiencia_respondida:
return f"Para crear un guion de Reel efectivo, necesito hacerte algunas preguntas clave. Empecemos: {discovery_questions[0]}. Recuerda hablar siempre en segunda persona, dirigiéndote al usuario."
elif audiencia_respondida and not producto_respondido:
return f"Gracias por esa información sobre tu audiencia. Ahora, {discovery_questions[1]}. Recuerda hablar siempre en segunda persona, dirigiéndote al usuario."
elif producto_respondido and not problema_respondido:
return f"Entendido. Ahora, {discovery_questions[2]}. Recuerda hablar siempre en segunda persona, dirigiéndote al usuario."
elif problema_respondido and not accion_respondida:
return f"Perfecto. Por último, {discovery_questions[3]}. Recuerda hablar siempre en segunda persona, dirigiéndote al usuario."
elif accion_respondida:
# Si ya hemos hecho todas las preguntas, proceder a la creación del guion
system_prompt = get_reels_script_prompt()
# Recopilar toda la información proporcionada por el usuario
user_info = "\n".join([f"- {msg}" for msg in user_messages[1:]])
return f"""
Gracias por toda la información proporcionada. Ahora vamos a crear un guion de Reel efectivo basado en tus respuestas:
Información del usuario:
{user_info}
Recuerda hablar siempre en segunda persona, dirigiéndote al usuario. NUNCA hables en primera persona como si fueras tú quien crea el Reel.
{system_prompt}
"""
else:
# Si no podemos determinar la etapa, hacer la primera pregunta
return f"Para crear un guion de Reel efectivo, necesito hacerte algunas preguntas clave. Empecemos: {discovery_questions[0]}. Recuerda hablar siempre en segunda persona, dirigiéndote al usuario."
return prompt
# Añadir una función para guardar el estado después de cada interacción
def save_state():
"""Guarda el estado actual de la sesión"""
try:
# Guardar el historial de mensajes
state.save_chat_history()
# Guardar la lista de chats pasados
joblib.dump(past_chats, 'data/past_chats_list')
# Imprimir mensaje de depuración
print(f"Estado guardado. Mensajes: {len(state.messages)}, Chat ID: {state.chat_id}")
except Exception as e:
print(f"Error al guardar el estado: {e}")
# Modificar la función process_message para guardar el estado después de cada interacción
def process_message(prompt, is_example=False):
"""Procesa un mensaje del usuario, ya sea directo o de un ejemplo"""
handle_chat_title(prompt)
with st.chat_message('user', avatar=USER_AVATAR_ICON):
st.markdown(prompt)
# Añadir el mensaje del usuario al historial
state.add_message('user', prompt, USER_AVATAR_ICON)
# Guardar el historial después de cada mensaje del usuario
state.gemini_history = state.chat.history if state.chat else []
save_state()
# Obtener el prompt mejorado primero
enhanced_prompt = get_enhanced_prompt(prompt, is_example)
# Mover la respuesta del modelo después del mensaje del usuario
with st.chat_message(MODEL_ROLE, avatar=AI_AVATAR_ICON):
try:
message_placeholder = st.empty()
typing_indicator = st.empty()
typing_indicator.markdown("*Generando respuesta...*")
response = state.send_message(enhanced_prompt)
full_response = stream_response(response, message_placeholder, typing_indicator)
if full_response:
state.add_message(MODEL_ROLE, full_response, AI_AVATAR_ICON)
state.gemini_history = state.chat.history if state.chat else []
save_state()
except Exception as e:
st.error(f"Error en el streaming: {str(e)}")
return
def process_model_response(enhanced_prompt):
"""Procesa la respuesta del modelo"""
with st.chat_message(MODEL_ROLE, avatar=AI_AVATAR_ICON):
try:
message_placeholder = st.empty()
typing_indicator = st.empty()
typing_indicator.markdown("*Generando respuesta...*")
response = state.send_message(enhanced_prompt)
full_response = stream_response(response, message_placeholder, typing_indicator)
# Actualizar historial
state.add_message(role=MODEL_ROLE, content=full_response, avatar=AI_AVATAR_ICON)
state.gemini_history = state.chat.history
state.save_chat_history()
except Exception as e:
st.error(f"Error: {str(e)}")
def stream_response(response, message_placeholder, typing_indicator):
"""Maneja el streaming de la respuesta"""
full_response = ''
try:
for chunk in response:
if chunk.text:
for ch in chunk.text:
full_response += ch
time.sleep(0.01)
typing_indicator.markdown("*Generando respuesta...*")
message_placeholder.markdown(full_response + '▌')
except Exception as e:
st.error(f"Error en el streaming: {str(e)}")
return ''
typing_indicator.empty()
message_placeholder.markdown(full_response)
return full_response
# 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: white !important;
font-weight: bold;
font-size: clamp(2.5em, 5vw, 4em);
line-height: 1.2;
}
</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 con diseño responsivo
st.markdown("""
<div style='text-align: center; margin-top: -35px; width: 100%;'>
<h1 class='robocopy-title' style='width: 100%; text-align: center; color: white !important; font-size: clamp(2.5em, 5vw, 4em); line-height: 1.2;'>Reels Creator</h1>
</div>
""", unsafe_allow_html=True)
# Subtítulo con margen superior ajustado a -30px
st.markdown("""
<div style='text-align: center; width: 100%;'>
<p style='font-size: 16px; color: white; width: 100%; text-align: center; margin-top: -20px;'>By Jesús Cabrera</p>
</div>
""", unsafe_allow_html=True)
# Descripción con fondo eliminado y margen superior ajustado a -20px
st.markdown("""
<div style='text-align: center; width: 100%;'>
<p style='font-size: 16px; background-color: transparent; padding: 12px; border-radius: 8px; margin-top: -20px; color: white; width: 100%; text-align: center;'>
🎬 Experto en crear guiones de Reels virales para Instagram y Facebook
</p>
</div>
""", unsafe_allow_html=True)
# Función para mostrar ejemplos de preguntas
def display_examples():
ejemplos = [
{"texto": "¿Qué es un Reel efectivo? 🎬", "prompt": "Explícame qué es un Reel efectivo y por qué es importante para mi estrategia de redes sociales"},
{"texto": "¿Cómo puedo crear mi guion de Reel? 📝", "prompt": "Guíame paso a paso en el proceso de crear un guion de Reel efectivo"},
{"texto": "¿Qué elementos debe tener mi Reel? ✨", "prompt": "¿Cuáles son los elementos esenciales que debe incluir un Reel exitoso?"},
{"texto": "¿Cuál es la mejor fórmula para mi caso? 🤔", "prompt": "Ayúdame a elegir la fórmula más adecuada para mi guion de Reel según mi nicho"}
]
# 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 = 'model'
USER_AVATAR_ICON = '👤'
AI_AVATAR_ICON = '🤖'
# Crear directorio de datos si no existe
os.makedirs('data', exist_ok=True)
# Cargar historial de chats pasados
try:
past_chats = joblib.load('data/past_chats_list')
except:
past_chats = {}
# Inicializar el sistema de prompt
system_prompt = get_reels_script_prompt()
# Inicializar el modelo si no está inicializado
if state.model is None:
state.initialize_model('gemini-2.0-flash') # Especificar la versión del modelo
state.initialize_chat()
# Sidebar para navegación y opciones
with st.sidebar:
st.markdown("## 🎬 RoboCopy - Reels Creator")
st.markdown("---")
# Botón para nueva conversación
if st.button("🆕 Nueva Conversación", use_container_width=True):
state.chat_id = new_chat_id
state.messages = []
state.gemini_history = []
state.initialize_chat()
st.rerun()
# Selector de chats pasados
st.markdown("### 💬 Conversaciones Pasadas")
# Mostrar chats pasados en orden inverso (más recientes primero)
past_chat_ids = list(past_chats.keys())
past_chat_ids.sort(reverse=True)
for chat_id in past_chat_ids:
chat_title = past_chats[chat_id]
if st.button(f"📝 {chat_title}", key=f"chat_{chat_id}", use_container_width=True):
state.chat_id = chat_id
state.load_chat_history(chat_id)
state.initialize_chat(state.gemini_history)
st.rerun()
# Información adicional
st.markdown("---")
st.markdown("### ℹ️ Información")
st.markdown("""
**RoboCopy - Reels Creator** te ayuda a crear guiones efectivos para tus Reels de Instagram y Facebook.
Simplemente describe tu nicho, audiencia y objetivo, y te ayudaremos a crear un guion optimizado para generar engagement.
""")
# Contenido principal
if state.chat_id is None:
state.chat_id = new_chat_id
# Mostrar la carátula inicial
display_initial_header()
# Mostrar ejemplos de preguntas
st.markdown("### 💡 Ejemplos de preguntas para comenzar:")
display_examples()
# Mostrar historial de mensajes
for message in state.messages:
with st.chat_message(message["role"], avatar=message.get("avatar")):
st.markdown(message["content"])
# Procesar el prompt si existe
if state.has_prompt():
prompt = state.prompt
state.clear_prompt()
process_message(prompt, is_example=True)
# Input para nuevo mensaje
user_input = st.chat_input("Escribe tu mensaje aquí...")
if user_input:
process_message(user_input) |