import time import os import joblib import streamlit as st import google.generativeai as genai from dotenv import load_dotenv from puv_formulas import puv_formulas from system_prompts import get_puv_system_prompt # Función para cargar CSS personalizado def load_css(file_path): with open(file_path) as f: st.markdown(f'', 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(""" """, 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: 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 st.session_state.chat_title = f'SesiónChat-{st.session_state.chat_id}' # 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']) # Mensaje inicial del sistema si es un chat nuevo if not st.session_state.messages: system_prompt = get_puv_system_prompt() # Mostrar la carátula inicial con el logo centrado col1, col2, col3 = st.columns([1, 2, 1]) with col2: st.image("robocopy_logo.png", width=300) st.markdown("""

PUV Creator

By Jesús Cabrera

🎯 Experto en crear Propuestas de Valor Únicas que convierten audiencia en clientes

""", unsafe_allow_html=True) # Mostrar los ejemplos ejemplos = [ {"texto": "¿Qué es una Propuesta de Valor Única? 🎯", "prompt": "Explícame qué es una Propuesta de Valor Única (PUV) y por qué es importante para mi negocio"}, {"texto": "¿Cómo puedo crear mi PUV? 📝", "prompt": "Guíame paso a paso en el proceso de crear una Propuesta de Valor Única efectiva"}, {"texto": "¿Qué elementos debe tener mi PUV? ✨", "prompt": "¿Cuáles son los elementos esenciales que debe incluir una Propuesta de Valor Única exitosa?"}, {"texto": "¿Cuál es la mejor fórmula para mi caso? 🤔", "prompt": "Ayúdame a elegir la fórmula más adecuada para mi Propuesta de Valor según mi tipo de negocio"} ] # 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"]): st.session_state.prompt = ejemplo["prompt"] st.rerun() # Inicializar el chat con el sistema prompt st.session_state.chat = st.session_state.model.start_chat( history=st.session_state.gemini_history ) # Enviar el system prompt como primer mensaje st.session_state.chat.send_message(system_prompt) # Solo añadir el mensaje inicial si no es un ejemplo if not st.session_state.get('prompt'): st.session_state.messages.append( dict( role=MODEL_ROLE, content=""" Hola, soy RoboCopy tu asistente especializado en crear Propuestas de Valor Únicas. Para ayudarte a crear PUVs efectivas, necesito conocer: 1. ¿Qué producto o servicio ofreces? 2. ¿A quién va dirigido? (describe tu público objetivo) 3. ¿Qué fórmula prefieres usar? Puedo ofrecerte: - Tradicional: Clara y directa - Anti-tradicional: Innovadora y disruptiva - Contrato Imposible: Audaz y sorprendente - Reto Ridículo: Humorística y relatable 4. ¿Cuántos ejemplos de PUVs necesitas? ¿Empezamos con tu producto o servicio? """, avatar=AI_AVATAR_ICON, ) ) # React to user input if prompt := st.chat_input('Describe tu producto/servicio y audiencia objetivo...'): # 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'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: # 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é 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 = 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') # 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, ) ) # Construir el prompt para el modelo con todas las fórmulas disponibles puv_expert_prompt = """You are a collaborative team of world-class experts working together to create exceptional Unique Value Propositions (UVPs) that convert audience into customers. INTERNAL ANALYSIS (DO NOT OUTPUT): 1. DEEP AVATAR ANALYSIS: A. Daily Life & Cultural Context: - What daily experiences resonate with them? - What cultural references do they understand? - What mental images are easy for them to recall? - What TV shows, movies, or media do they consume? B. Pain Points & Emotional Core: - What is their ONE main pain point? - What consequences does this pain point trigger? - What past painful experience influences current decisions? - What internal conflict do they regularly experience? - What do they need to heal or resolve to feel complete? C. Previous Solutions: - What have they tried before that didn't work? - Why didn't these solutions work for them? - What do other "experts" tell them to do? - What false beliefs do they hold? D. Desires & Transformations: - What are their primary desires? - What is their current vs. desired situation? - What transformation are they seeking? - Why do they need to act now? 2. PRODUCT/SERVICE ANALYSIS: - What is the main benefit or promise? - What makes it unique or different? - What transformation does it offer? - How does it help achieve results? - Why is it superior to existing solutions? 3. MARKET CONTEXT: - What are the common industry solutions? - Why do these solutions fail? - What are the typical misconceptions? - What makes your solution unique? Based on this internal analysis, create UVPs that: 1. Connect directly with the main pain point 2. Address deep emotional motivations 3. Contrast with failed past solutions 4. Present your unique method convincingly 5. Use familiar analogies or metaphors THE EXPERT TEAM: 1. MASTER UVP STRATEGIST: - Expert in UVP frameworks and conversion strategies - Ensures the UVPs follow the selected framework structure precisely - Focuses on strategic placement of key conversion elements 2. ELITE DIRECT RESPONSE COPYWRITER: - Trained by Gary Halbert, Gary Bencivenga, and David Ogilvy - Creates compelling hooks and persuasive elements - Ensures the language resonates with the target audience 3. AUDIENCE PSYCHOLOGY SPECIALIST: - Expert in understanding audience motivations and objections - Creates content that builds genuine connection and trust - Identifies and addresses hidden fears and desires 4. STORYTELLING MASTER: - Creates compelling narratives that illustrate key points - Makes complex concepts accessible through narrative 5. ENGAGEMENT EXPERT: - Specializes in creating memorable and impactful statements - Ensures the UVPs are clear, concise and compelling You are a UVP (Unique Value Proposition) expert. Analyze (internally only, do not output the analysis) the user's message to identify information about their product/service and target audience. If the user hasn't provided all the necessary information, guide them through the process by asking for: 1. What product/service they offer 2. Who their target audience is 3. Which formula they prefer (Tradicional, Anti-tradicional, Contrato Imposible, Reto Ridículo) 4. How many UVP examples they want If the user mentions a specific formula, use that formula from the puv_formulas dictionary. If they don't specify, suggest the most appropriate formula based on their product/service and audience. If the user is asking for UVPs and has provided sufficient information, create the requested number of different UVPs using the specified formula. If the user is asking a question about UVPs or marketing, answer it helpfully. When creating UVPs, follow these CRITICAL INSTRUCTIONS: - Each UVP must be specific and measurable - Focus on the transformation journey - Use natural, conversational language - Avoid generic phrases and buzzwords - Maximum 2 lines per UVP ESTRUCTURAS DE FÓRMULAS PUV: 1. FÓRMULA TRADICIONAL: Estructura obligatoria: "Yo ayudo a [AVATAR] a [TRANSFORMACIÓN]" Elementos requeridos: - Avatar específico y detallado - Transformación medible y concreta - Verbo de acción claro 2. FÓRMULA ANTI-TRADICIONAL: Estructura obligatoria: Comenzar con uno de: - "Yo transformo..." - "Me especializo en..." - "Soy experto/a en..." Elementos requeridos: - Situación actual del avatar con UN problema principal - UNA transformación clara - UN beneficio principal 3. CONTRATO IMPOSIBLE: Estructura obligatoria: 1. Opener audaz: "¿Te imaginas poder..." / "Soy el antídoto para..." / "Revoluciono la manera..." 2. Descripción del producto/servicio de forma inesperada 3. Beneficio transformador increíble 4. Diferenciador anti-tradicional 4. RETO RIDÍCULO: Estructura obligatoria: 1. Anécdota personal humorística 2. Problema específico del mercado 3. Solución obvia pero presentada de forma única PROCESO DE VALIDACIÓN: Antes de generar cada PUV, verificar: 1. ¿Sigue exactamente la estructura de la fórmula elegida? 2. ¿Incluye todos los elementos requeridos? 3. ¿Mantiene el formato específico de la fórmula? 4. ¿Comunica claramente el valor único? If creating UVPs, output in this format: "Basado en tu descripción, aquí tienes [número] propuestas de valor únicas (PUVs) para tu [producto/servicio] usando la fórmula [nombre de fórmula]: 1. [First UVP] 2. [Second UVP] 3. [Third UVP] ... Estas PUVs destacan [principales beneficios]. ¿Hay alguna que te guste más o quieres que ajuste algún aspecto?" If answering a question, provide a helpful, expert response. """ # Combinar el prompt del experto con el mensaje del usuario enhanced_prompt = f"{puv_expert_prompt}\n\nUser message: {prompt}" ## Send message to AI 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("*Generando respuesta...*") # Streams in a chunk at a time for chunk in response: # Simulate stream of chunk for ch in chunk.text: # Eliminamos el split(' ') para procesar carácter por carácter full_response += ch time.sleep(0.01) # Más rápido # 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', ) # Cuando se selecciona un ejemplo if 'prompt' in st.session_state and st.session_state.prompt: # Verificamos que el prompt no esté vacío prompt = st.session_state.prompt # Guardar el chat para después si es nuevo if st.session_state.chat_id not in past_chats.keys(): temp_title = f'SesiónChat-{st.session_state.chat_id}' past_chats[st.session_state.chat_id] = temp_title joblib.dump(past_chats, 'data/past_chats_list') # Mostrar el mensaje del usuario with st.chat_message('user', avatar=USER_AVATAR_ICON): st.markdown(prompt) # Añadir el mensaje del usuario al historial st.session_state.messages.append({ 'role': 'user', 'content': prompt, 'avatar': USER_AVATAR_ICON }) # Procesar la respuesta del modelo if prompt.strip(): with st.chat_message(MODEL_ROLE, avatar=AI_AVATAR_ICON): try: response = st.session_state.chat.send_message(prompt) st.markdown(response.text) # Añadir la respuesta al historial st.session_state.messages.append({ 'role': MODEL_ROLE, 'content': response.text, 'avatar': AI_AVATAR_ICON }) # Guardar el historial actualizado 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', ) except ValueError as e: st.error("Error: El mensaje no puede estar vacío. Por favor, escribe algo.") # Limpiar el prompt del estado de la sesión st.session_state.prompt = None # Limpiar el prompt del estado de la sesión st.session_state.prompt = None