File size: 8,691 Bytes
c76bbac
 
 
 
e75a09b
c76bbac
a339470
939772b
956fbae
 
 
 
348d961
a316975
 
 
 
20ce570
 
a316975
 
df0841b
a316975
 
 
b945abf
 
 
 
 
 
 
 
 
 
 
 
 
 
 
df0841b
 
 
 
 
 
 
 
 
 
 
 
a316975
df0841b
60180b4
df0841b
 
 
 
 
9524460
df0841b
 
 
 
b945abf
5ec6171
df0841b
 
 
 
 
 
 
a316975
348d961
b1cf5e1
 
 
 
 
 
 
 
d97b940
fa342d1
 
c0c55b0
fa342d1
c0c55b0
 
fa342d1
 
d97b940
c0073a3
956fbae
9bd45f9
 
d97b940
5722b78
 
 
 
 
 
 
 
d97b940
e61bf12
d97b940
5722b78
c0c55b0
f6214f2
d97b940
 
5722b78
c069900
f6214f2
d97b940
 
11b06cd
c069900
11b06cd
 
 
d97b940
956fbae
 
dc6ebfc
e0af504
 
 
 
dc6ebfc
 
 
 
 
956fbae
dc6ebfc
 
956fbae
 
 
 
 
 
 
 
320dfb7
 
956fbae
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
320dfb7
956fbae
 
320dfb7
956fbae
 
320dfb7
ea00e74
956fbae
 
320dfb7
 
 
939772b
320dfb7
b194d60
 
 
320dfb7
 
 
 
 
c2ba08b
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
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_unified_puv_prompt
from session_state import SessionState

# Inicializar el estado de la sesión
state = SessionState()

# Función para detectar saludos y generar respuestas personalizadas
def is_greeting(text):
    text = text.lower().strip()
    greetings = ['hola', 'hey', 'saludos', 'buenos días', 'buenas tardes', 'buenas noches', 'hi', 'hello']
    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

def process_message(prompt, is_example=False):
    handle_chat_title(prompt)
    with st.chat_message('user', avatar=USER_AVATAR_ICON):
        st.markdown(prompt)
    state.add_message('user', prompt, USER_AVATAR_ICON)
    enhanced_prompt = get_enhanced_prompt(prompt, is_example)
    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
                state.save_chat_history()
        except Exception as e:
            st.error(f"Error en el streaming: {str(e)}")
            return

def handle_chat_title(prompt):
    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):
    if is_greeting(prompt):
        return f"El usuario te ha saludado con '{prompt}'. Preséntate brevemente, explica qué es una PUV en 1-2 líneas, y haz 1-2 preguntas iniciales para comenzar a crear la PUV del usuario (como a quién se dirige su producto/servicio o qué ofrece). Sé amigable, breve y toma la iniciativa como el experto que eres."
    elif is_example:
        return f"El usuario ha seleccionado un ejemplo: '{prompt}'. Responde de manera conversacional y sencilla, como si estuvieras hablando con un amigo. Evita tecnicismos innecesarios. Enfócate en dar información práctica que ayude al usuario a crear su PUV. Usa ejemplos concretos cuando sea posible. Termina tu respuesta con una pregunta que invite al usuario a compartir información sobre su negocio para poder ayudarle a crear su PUV personalizada."
    return prompt

def stream_response(response, message_placeholder, typing_indicator):
    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

def load_css(file_path):
    with open(file_path) as f:
        st.markdown(f'<style>{f.read()}</style>', unsafe_allow_html=True)

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}")
    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)

def display_initial_header():
    col1, col2, col3 = st.columns([1, 2, 1])
    with col2:
        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)
        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;'>PUV Creator</h1>
            </div>
        """, unsafe_allow_html=True)
        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)
    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 Propuestas de Valor Únicas que convierten audiencia en clientes
            </p>
        </div>
    """, unsafe_allow_html=True)

def display_examples():
    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"}
    ]
    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 = 'ai'
AI_AVATAR_ICON = '🤖'
USER_AVATAR_ICON = '👤'

try:
    os.mkdir('data/')
except:
    pass

try:
    past_chats: dict = joblib.load('data/past_chats_list')
except:
    past_chats = {}

with st.sidebar:
    st.write('# Chats Anteriores')
    if state.chat_id is None:
        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:
        state.chat_id = st.selectbox(
            label='Selecciona un chat anterior',
            options=[new_chat_id, state.chat_id] + list(past_chats.keys()),
            index=1,
            format_func=lambda x: past_chats.get(x, 'Nuevo Chat' if x != state.chat_id else state.chat_title),
            placeholder='_',
        )
    state.chat_title = f'SesiónChat-{state.chat_id}'

state.load_chat_history()
state.initialize_model('gemini-2.0-flash')
state.initialize_chat()

for message in state.messages:
    with st.chat_message(name=message['role'], avatar=message.get('avatar')):
        st.markdown(message['content'])

# NUEVO BLOQUE MODIFICADO
if not state.has_messages():
    display_initial_header()
    display_examples()
    if state.prompt:
        process_message(state.prompt, is_example=True)
        state.clear_prompt()
    system_prompt = get_unified_puv_prompt()
    if state.chat is not None:
        state.chat.send_message(system_prompt)
    else:
        st.error("Error: No se pudo inicializar el chat correctamente.")
else:
    if prompt := st.chat_input('Describe tu producto/servicio y audiencia objetivo...'):
        process_message(prompt, is_example=False)
    if state.has_prompt():
        process_message(state.prompt, is_example=True)
        state.clear_prompt()