Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -13,33 +13,49 @@ state = SessionState()
|
|
13 |
|
14 |
# Función para detectar saludos y generar respuestas personalizadas
|
15 |
def is_greeting(text):
|
|
|
16 |
text = text.lower().strip()
|
17 |
greetings = ['hola', 'hey', 'saludos', 'buenos días', 'buenas tardes', 'buenas noches', 'hi', 'hello']
|
|
|
|
|
|
|
18 |
is_simple_greeting = any(greeting in text for greeting in greetings) and len(text.split()) < 4
|
19 |
return is_simple_greeting and len(state.messages) == 0
|
20 |
|
|
|
21 |
def process_message(prompt, is_example=False):
|
|
|
22 |
handle_chat_title(prompt)
|
|
|
23 |
with st.chat_message('user', avatar=USER_AVATAR_ICON):
|
24 |
st.markdown(prompt)
|
|
|
25 |
state.add_message('user', prompt, USER_AVATAR_ICON)
|
|
|
|
|
26 |
enhanced_prompt = get_enhanced_prompt(prompt, is_example)
|
|
|
|
|
27 |
with st.chat_message(MODEL_ROLE, avatar=AI_AVATAR_ICON):
|
28 |
try:
|
29 |
message_placeholder = st.empty()
|
30 |
typing_indicator = st.empty()
|
31 |
typing_indicator.markdown("*Generando respuesta...*")
|
|
|
32 |
response = state.send_message(enhanced_prompt)
|
33 |
full_response = stream_response(response, message_placeholder, typing_indicator)
|
|
|
34 |
if full_response:
|
35 |
state.add_message(MODEL_ROLE, full_response, AI_AVATAR_ICON)
|
36 |
state.gemini_history = state.chat.history
|
37 |
state.save_chat_history()
|
|
|
38 |
except Exception as e:
|
39 |
st.error(f"Error en el streaming: {str(e)}")
|
40 |
return
|
41 |
|
42 |
def handle_chat_title(prompt):
|
|
|
43 |
if state.chat_id not in past_chats:
|
44 |
temp_title = f'SesiónChat-{state.chat_id}'
|
45 |
generated_title = state.generate_chat_title(prompt)
|
@@ -50,13 +66,34 @@ def handle_chat_title(prompt):
|
|
50 |
joblib.dump(past_chats, 'data/past_chats_list')
|
51 |
|
52 |
def get_enhanced_prompt(prompt, is_example):
|
|
|
53 |
if is_greeting(prompt):
|
54 |
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."
|
55 |
elif is_example:
|
56 |
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."
|
57 |
return prompt
|
58 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
59 |
def stream_response(response, message_placeholder, typing_indicator):
|
|
|
60 |
full_response = ''
|
61 |
try:
|
62 |
for chunk in response:
|
@@ -69,19 +106,23 @@ def stream_response(response, message_placeholder, typing_indicator):
|
|
69 |
except Exception as e:
|
70 |
st.error(f"Error en el streaming: {str(e)}")
|
71 |
return ''
|
|
|
72 |
typing_indicator.empty()
|
73 |
message_placeholder.markdown(full_response)
|
74 |
return full_response
|
75 |
|
|
|
76 |
def load_css(file_path):
|
77 |
with open(file_path) as f:
|
78 |
st.markdown(f'<style>{f.read()}</style>', unsafe_allow_html=True)
|
79 |
|
|
|
80 |
try:
|
81 |
css_path = os.path.join(os.path.dirname(__file__), 'static', 'css', 'style.css')
|
82 |
load_css(css_path)
|
83 |
except Exception as e:
|
84 |
print(f"Error al cargar CSS: {e}")
|
|
|
85 |
st.markdown("""
|
86 |
<style>
|
87 |
.robocopy-title {
|
@@ -93,9 +134,11 @@ except Exception as e:
|
|
93 |
</style>
|
94 |
""", unsafe_allow_html=True)
|
95 |
|
|
|
96 |
def display_initial_header():
|
97 |
col1, col2, col3 = st.columns([1, 2, 1])
|
98 |
with col2:
|
|
|
99 |
st.markdown("""
|
100 |
<style>
|
101 |
div.stImage {
|
@@ -107,16 +150,22 @@ def display_initial_header():
|
|
107 |
</style>
|
108 |
""", unsafe_allow_html=True)
|
109 |
st.image("robocopy_logo.png", width=300, use_container_width=True)
|
|
|
|
|
110 |
st.markdown("""
|
111 |
<div style='text-align: center; margin-top: -35px; width: 100%;'>
|
112 |
<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>
|
113 |
</div>
|
114 |
""", unsafe_allow_html=True)
|
|
|
|
|
115 |
st.markdown("""
|
116 |
<div style='text-align: center; width: 100%;'>
|
117 |
<p style='font-size: 16px; color: white; width: 100%; text-align: center; margin-top: -20px;'>By Jesús Cabrera</p>
|
118 |
</div>
|
119 |
""", unsafe_allow_html=True)
|
|
|
|
|
120 |
st.markdown("""
|
121 |
<div style='text-align: center; width: 100%;'>
|
122 |
<p style='font-size: 16px; background-color: transparent; padding: 12px; border-radius: 8px; margin-top: -20px; color: white; width: 100%; text-align: center;'>
|
@@ -125,6 +174,7 @@ def display_initial_header():
|
|
125 |
</div>
|
126 |
""", unsafe_allow_html=True)
|
127 |
|
|
|
128 |
def display_examples():
|
129 |
ejemplos = [
|
130 |
{"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"},
|
@@ -132,6 +182,8 @@ def display_examples():
|
|
132 |
{"texto": "¿Qué elementos debe tener mi PUV? ✨", "prompt": "¿Cuáles son los elementos esenciales que debe incluir una Propuesta de Valor Única exitosa?"},
|
133 |
{"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"}
|
134 |
]
|
|
|
|
|
135 |
cols = st.columns(4)
|
136 |
for idx, ejemplo in enumerate(ejemplos):
|
137 |
with cols[idx]:
|
@@ -147,19 +199,23 @@ genai.configure(api_key=GOOGLE_API_KEY)
|
|
147 |
# Configuración de la aplicación
|
148 |
new_chat_id = f'{time.time()}'
|
149 |
MODEL_ROLE = 'ai'
|
150 |
-
AI_AVATAR_ICON = '🤖'
|
151 |
-
USER_AVATAR_ICON = '👤'
|
152 |
|
|
|
153 |
try:
|
154 |
os.mkdir('data/')
|
155 |
except:
|
|
|
156 |
pass
|
157 |
|
|
|
158 |
try:
|
159 |
past_chats: dict = joblib.load('data/past_chats_list')
|
160 |
except:
|
161 |
past_chats = {}
|
162 |
|
|
|
163 |
with st.sidebar:
|
164 |
st.write('# Chats Anteriores')
|
165 |
if state.chat_id is None:
|
@@ -170,6 +226,7 @@ with st.sidebar:
|
|
170 |
placeholder='_',
|
171 |
)
|
172 |
else:
|
|
|
173 |
state.chat_id = st.selectbox(
|
174 |
label='Selecciona un chat anterior',
|
175 |
options=[new_chat_id, state.chat_id] + list(past_chats.keys()),
|
@@ -177,31 +234,46 @@ with st.sidebar:
|
|
177 |
format_func=lambda x: past_chats.get(x, 'Nuevo Chat' if x != state.chat_id else state.chat_title),
|
178 |
placeholder='_',
|
179 |
)
|
|
|
180 |
state.chat_title = f'SesiónChat-{state.chat_id}'
|
181 |
|
|
|
182 |
state.load_chat_history()
|
|
|
|
|
183 |
state.initialize_model('gemini-2.0-flash')
|
184 |
-
state.initialize_chat()
|
185 |
|
|
|
186 |
for message in state.messages:
|
187 |
-
with st.chat_message(
|
|
|
|
|
|
|
188 |
st.markdown(message['content'])
|
189 |
|
190 |
-
#
|
191 |
if not state.has_messages():
|
|
|
192 |
display_initial_header()
|
|
|
|
|
193 |
display_examples()
|
194 |
-
|
195 |
-
|
196 |
-
state.clear_prompt()
|
197 |
system_prompt = get_unified_puv_prompt()
|
198 |
-
if state.chat is not None:
|
199 |
state.chat.send_message(system_prompt)
|
200 |
else:
|
201 |
st.error("Error: No se pudo inicializar el chat correctamente.")
|
202 |
-
|
203 |
-
|
204 |
-
|
205 |
-
|
206 |
-
|
207 |
-
|
|
|
|
|
|
|
|
|
|
|
|
13 |
|
14 |
# Función para detectar saludos y generar respuestas personalizadas
|
15 |
def is_greeting(text):
|
16 |
+
"""Detecta si el texto es un saludo simple"""
|
17 |
text = text.lower().strip()
|
18 |
greetings = ['hola', 'hey', 'saludos', 'buenos días', 'buenas tardes', 'buenas noches', 'hi', 'hello']
|
19 |
+
|
20 |
+
# Solo considerar como saludo si es el primer mensaje del usuario
|
21 |
+
# y es un saludo simple
|
22 |
is_simple_greeting = any(greeting in text for greeting in greetings) and len(text.split()) < 4
|
23 |
return is_simple_greeting and len(state.messages) == 0
|
24 |
|
25 |
+
# Función para procesar mensajes (unifica la lógica de procesamiento)
|
26 |
def process_message(prompt, is_example=False):
|
27 |
+
"""Procesa un mensaje del usuario, ya sea directo o de un ejemplo"""
|
28 |
handle_chat_title(prompt)
|
29 |
+
|
30 |
with st.chat_message('user', avatar=USER_AVATAR_ICON):
|
31 |
st.markdown(prompt)
|
32 |
+
|
33 |
state.add_message('user', prompt, USER_AVATAR_ICON)
|
34 |
+
|
35 |
+
# Obtener el prompt mejorado primero
|
36 |
enhanced_prompt = get_enhanced_prompt(prompt, is_example)
|
37 |
+
|
38 |
+
# Mover la respuesta del modelo después del mensaje del usuario
|
39 |
with st.chat_message(MODEL_ROLE, avatar=AI_AVATAR_ICON):
|
40 |
try:
|
41 |
message_placeholder = st.empty()
|
42 |
typing_indicator = st.empty()
|
43 |
typing_indicator.markdown("*Generando respuesta...*")
|
44 |
+
|
45 |
response = state.send_message(enhanced_prompt)
|
46 |
full_response = stream_response(response, message_placeholder, typing_indicator)
|
47 |
+
|
48 |
if full_response:
|
49 |
state.add_message(MODEL_ROLE, full_response, AI_AVATAR_ICON)
|
50 |
state.gemini_history = state.chat.history
|
51 |
state.save_chat_history()
|
52 |
+
|
53 |
except Exception as e:
|
54 |
st.error(f"Error en el streaming: {str(e)}")
|
55 |
return
|
56 |
|
57 |
def handle_chat_title(prompt):
|
58 |
+
"""Maneja la lógica del título del chat"""
|
59 |
if state.chat_id not in past_chats:
|
60 |
temp_title = f'SesiónChat-{state.chat_id}'
|
61 |
generated_title = state.generate_chat_title(prompt)
|
|
|
66 |
joblib.dump(past_chats, 'data/past_chats_list')
|
67 |
|
68 |
def get_enhanced_prompt(prompt, is_example):
|
69 |
+
"""Genera el prompt mejorado según el tipo de mensaje"""
|
70 |
if is_greeting(prompt):
|
71 |
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."
|
72 |
elif is_example:
|
73 |
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."
|
74 |
return prompt
|
75 |
|
76 |
+
def process_model_response(enhanced_prompt):
|
77 |
+
"""Procesa la respuesta del modelo"""
|
78 |
+
with st.chat_message(MODEL_ROLE, avatar=AI_AVATAR_ICON):
|
79 |
+
try:
|
80 |
+
message_placeholder = st.empty()
|
81 |
+
typing_indicator = st.empty()
|
82 |
+
typing_indicator.markdown("*Generando respuesta...*")
|
83 |
+
|
84 |
+
response = state.send_message(enhanced_prompt)
|
85 |
+
full_response = stream_response(response, message_placeholder, typing_indicator)
|
86 |
+
|
87 |
+
# Actualizar historial
|
88 |
+
state.add_message(role=MODEL_ROLE, content=full_response, avatar=AI_AVATAR_ICON)
|
89 |
+
state.gemini_history = state.chat.history
|
90 |
+
state.save_chat_history()
|
91 |
+
|
92 |
+
except Exception as e:
|
93 |
+
st.error(f"Error: {str(e)}")
|
94 |
+
|
95 |
def stream_response(response, message_placeholder, typing_indicator):
|
96 |
+
"""Maneja el streaming de la respuesta"""
|
97 |
full_response = ''
|
98 |
try:
|
99 |
for chunk in response:
|
|
|
106 |
except Exception as e:
|
107 |
st.error(f"Error en el streaming: {str(e)}")
|
108 |
return ''
|
109 |
+
|
110 |
typing_indicator.empty()
|
111 |
message_placeholder.markdown(full_response)
|
112 |
return full_response
|
113 |
|
114 |
+
# Función para cargar CSS personalizado
|
115 |
def load_css(file_path):
|
116 |
with open(file_path) as f:
|
117 |
st.markdown(f'<style>{f.read()}</style>', unsafe_allow_html=True)
|
118 |
|
119 |
+
# Intentar cargar el CSS personalizado con ruta absoluta para mayor seguridad
|
120 |
try:
|
121 |
css_path = os.path.join(os.path.dirname(__file__), 'static', 'css', 'style.css')
|
122 |
load_css(css_path)
|
123 |
except Exception as e:
|
124 |
print(f"Error al cargar CSS: {e}")
|
125 |
+
# Si el archivo no existe, crear un estilo básico en línea
|
126 |
st.markdown("""
|
127 |
<style>
|
128 |
.robocopy-title {
|
|
|
134 |
</style>
|
135 |
""", unsafe_allow_html=True)
|
136 |
|
137 |
+
# Función de utilidad para mostrar la carátula inicial
|
138 |
def display_initial_header():
|
139 |
col1, col2, col3 = st.columns([1, 2, 1])
|
140 |
with col2:
|
141 |
+
# Centrar la imagen
|
142 |
st.markdown("""
|
143 |
<style>
|
144 |
div.stImage {
|
|
|
150 |
</style>
|
151 |
""", unsafe_allow_html=True)
|
152 |
st.image("robocopy_logo.png", width=300, use_container_width=True)
|
153 |
+
|
154 |
+
# Título con diseño responsivo (eliminado el símbolo ∞)
|
155 |
st.markdown("""
|
156 |
<div style='text-align: center; margin-top: -35px; width: 100%;'>
|
157 |
<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>
|
158 |
</div>
|
159 |
""", unsafe_allow_html=True)
|
160 |
+
|
161 |
+
# Subtítulo con margen superior ajustado a -30px
|
162 |
st.markdown("""
|
163 |
<div style='text-align: center; width: 100%;'>
|
164 |
<p style='font-size: 16px; color: white; width: 100%; text-align: center; margin-top: -20px;'>By Jesús Cabrera</p>
|
165 |
</div>
|
166 |
""", unsafe_allow_html=True)
|
167 |
+
|
168 |
+
# Descripción con fondo eliminado y margen superior ajustado a -20px
|
169 |
st.markdown("""
|
170 |
<div style='text-align: center; width: 100%;'>
|
171 |
<p style='font-size: 16px; background-color: transparent; padding: 12px; border-radius: 8px; margin-top: -20px; color: white; width: 100%; text-align: center;'>
|
|
|
174 |
</div>
|
175 |
""", unsafe_allow_html=True)
|
176 |
|
177 |
+
# Función para mostrar ejemplos de preguntas
|
178 |
def display_examples():
|
179 |
ejemplos = [
|
180 |
{"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"},
|
|
|
182 |
{"texto": "¿Qué elementos debe tener mi PUV? ✨", "prompt": "¿Cuáles son los elementos esenciales que debe incluir una Propuesta de Valor Única exitosa?"},
|
183 |
{"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"}
|
184 |
]
|
185 |
+
|
186 |
+
# Crear los botones de ejemplo
|
187 |
cols = st.columns(4)
|
188 |
for idx, ejemplo in enumerate(ejemplos):
|
189 |
with cols[idx]:
|
|
|
199 |
# Configuración de la aplicación
|
200 |
new_chat_id = f'{time.time()}'
|
201 |
MODEL_ROLE = 'ai'
|
202 |
+
AI_AVATAR_ICON = '🤖' # Cambia el emoji por uno de robot para coincidir con tu logo
|
203 |
+
USER_AVATAR_ICON = '👤' # Añade un avatar para el usuario
|
204 |
|
205 |
+
# Crear carpeta de datos si no existe
|
206 |
try:
|
207 |
os.mkdir('data/')
|
208 |
except:
|
209 |
+
# data/ folder already exists
|
210 |
pass
|
211 |
|
212 |
+
# Cargar chats anteriores
|
213 |
try:
|
214 |
past_chats: dict = joblib.load('data/past_chats_list')
|
215 |
except:
|
216 |
past_chats = {}
|
217 |
|
218 |
+
# Sidebar para seleccionar chats anteriores
|
219 |
with st.sidebar:
|
220 |
st.write('# Chats Anteriores')
|
221 |
if state.chat_id is None:
|
|
|
226 |
placeholder='_',
|
227 |
)
|
228 |
else:
|
229 |
+
# This will happen the first time AI response comes in
|
230 |
state.chat_id = st.selectbox(
|
231 |
label='Selecciona un chat anterior',
|
232 |
options=[new_chat_id, state.chat_id] + list(past_chats.keys()),
|
|
|
234 |
format_func=lambda x: past_chats.get(x, 'Nuevo Chat' if x != state.chat_id else state.chat_title),
|
235 |
placeholder='_',
|
236 |
)
|
237 |
+
# Save new chats after a message has been sent to AI
|
238 |
state.chat_title = f'SesiónChat-{state.chat_id}'
|
239 |
|
240 |
+
# Cargar historial del chat
|
241 |
state.load_chat_history()
|
242 |
+
|
243 |
+
# Inicializar el modelo y el chat
|
244 |
state.initialize_model('gemini-2.0-flash')
|
245 |
+
state.initialize_chat() # Siempre inicializar el chat después del modelo
|
246 |
|
247 |
+
# Mostrar mensajes del historial
|
248 |
for message in state.messages:
|
249 |
+
with st.chat_message(
|
250 |
+
name=message['role'],
|
251 |
+
avatar=message.get('avatar'),
|
252 |
+
):
|
253 |
st.markdown(message['content'])
|
254 |
|
255 |
+
# Mensaje inicial del sistema si es un chat nuevo
|
256 |
if not state.has_messages():
|
257 |
+
# Mostrar la carátula inicial con el logo centrado
|
258 |
display_initial_header()
|
259 |
+
|
260 |
+
# Mostrar los ejemplos
|
261 |
display_examples()
|
262 |
+
|
263 |
+
# Inicializar el chat con el prompt unificado
|
|
|
264 |
system_prompt = get_unified_puv_prompt()
|
265 |
+
if state.chat is not None: # Verificación adicional de seguridad
|
266 |
state.chat.send_message(system_prompt)
|
267 |
else:
|
268 |
st.error("Error: No se pudo inicializar el chat correctamente.")
|
269 |
+
|
270 |
+
# Procesar entrada del usuario
|
271 |
+
if prompt := st.chat_input('Describe tu producto/servicio y audiencia objetivo...'):
|
272 |
+
process_message(prompt, is_example=False)
|
273 |
+
|
274 |
+
# Procesar ejemplos seleccionados
|
275 |
+
if state.has_prompt():
|
276 |
+
prompt = state.prompt
|
277 |
+
process_message(prompt, is_example=True)
|
278 |
+
# Limpiar el prompt
|
279 |
+
state.clear_prompt()
|