JeCabrera commited on
Commit
12a1f21
·
verified ·
1 Parent(s): 88f1ad5

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +117 -35
app.py CHANGED
@@ -4,6 +4,7 @@ import joblib
4
  import streamlit as st
5
  from dotenv import load_dotenv
6
  from system_prompts import get_unified_reel_prompt # Cambiar de get_unified_puv_prompt a get_unified_reel_prompt
 
7
  from session_state import (
8
  SessionState,
9
  DEFAULT_GEMINI_MODEL,
@@ -13,6 +14,7 @@ from session_state import (
13
 
14
  # Inicializar el estado de la sesión
15
  state = SessionState()
 
16
 
17
  # Función para detectar saludos y generar respuestas personalizadas
18
  def is_greeting(text):
@@ -46,11 +48,14 @@ def process_message(prompt, is_example=False):
46
  typing_indicator.markdown("*Generando respuesta...*")
47
 
48
  response = state.send_message(enhanced_prompt)
49
- full_response = stream_response(response, message_placeholder, typing_indicator)
50
 
51
  if full_response:
52
  state.add_message(MODEL_ROLE, full_response, AI_AVATAR_ICON)
53
- state.gemini_history = state.chat.history
 
 
 
54
  state.save_chat_history()
55
 
56
  except Exception as e:
@@ -68,29 +73,94 @@ def handle_chat_title(prompt):
68
  state.chat_title = past_chats[state.chat_id]
69
  joblib.dump(past_chats, PAST_CHATS_LIST_PATH)
70
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
71
  def get_enhanced_prompt(prompt, is_example):
72
  """Genera el prompt mejorado según el tipo de mensaje"""
 
 
 
 
 
73
  if is_greeting(prompt):
74
  return f"El usuario te ha saludado con '{prompt}'. Preséntate brevemente, explica qué es un Reel y por qué es importante, y haz las 3 preguntas iniciales para comenzar a crear el guion del Reel (audiencia ideal, producto/servicio, y llamado a la acción). Sé amigable, breve y toma la iniciativa como el experto que eres."
75
  elif is_example:
76
  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 Reel. 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 Reel personalizado."
77
  return prompt
78
 
79
- def stream_response(response, message_placeholder, typing_indicator):
80
  """Maneja el streaming de la respuesta"""
81
  full_response = ''
 
 
 
 
82
  try:
83
  for chunk in response:
84
  if chunk.text:
85
  for ch in chunk.text:
86
  full_response += ch
87
- time.sleep(0.01)
88
- typing_indicator.markdown("*Generando respuesta...*")
89
- message_placeholder.markdown(full_response + '▌')
 
 
 
90
  except Exception as e:
91
  st.error(f"Error en el streaming: {str(e)}")
92
  return ''
93
-
 
 
 
 
 
94
  typing_indicator.empty()
95
  message_placeholder.markdown(full_response)
96
  return full_response
@@ -172,7 +242,8 @@ def display_examples():
172
  for idx, ejemplo in enumerate(ejemplos):
173
  with cols[idx]:
174
  if st.button(ejemplo["texto"], key=f"ejemplo_{idx}", help=ejemplo["prompt"]):
175
- state.prompt = ejemplo["prompt"]
 
176
  st.rerun()
177
 
178
  # Cargar variables de entorno
@@ -181,7 +252,6 @@ GOOGLE_API_KEY = os.environ.get('GOOGLE_API_KEY')
181
  if not GOOGLE_API_KEY:
182
  st.error("Falta la variable de entorno GOOGLE_API_KEY. Configúrala para continuar.")
183
  st.stop()
184
- genai.configure(api_key=GOOGLE_API_KEY)
185
 
186
  # Configuración de la aplicación
187
  new_chat_id = f'{time.time()}'
@@ -205,6 +275,7 @@ except (FileNotFoundError, EOFError):
205
  # Sidebar para seleccionar chats anteriores
206
  with st.sidebar:
207
  st.write('# Chats Anteriores')
 
208
  if state.chat_id is None:
209
  state.chat_id = st.selectbox(
210
  label='Selecciona un chat anterior',
@@ -228,8 +299,9 @@ with st.sidebar:
228
  state.load_chat_history()
229
 
230
  # Inicializar el modelo y el chat
231
- state.initialize_model(DEFAULT_GEMINI_MODEL)
232
- state.initialize_chat() # Siempre inicializar el chat después del modelo
 
233
 
234
  # Mostrar mensajes del historial
235
  for message in state.messages:
@@ -239,28 +311,38 @@ for message in state.messages:
239
  ):
240
  st.markdown(message['content'])
241
 
242
- # Mensaje inicial del sistema si es un chat nuevo
243
- if not state.has_messages():
244
- # Mostrar la carátula inicial con el logo centrado
245
- display_initial_header()
246
-
247
- # Mostrar los ejemplos
248
- display_examples()
249
 
250
- # Inicializar el chat con el prompt unificado
251
- system_prompt = get_unified_reel_prompt() # Cambiar de get_unified_puv_prompt a get_unified_reel_prompt
252
- if state.chat is not None: # Verificación adicional de seguridad
253
- state.chat.send_message(system_prompt)
254
- else:
255
- st.error("Error: No se pudo inicializar el chat correctamente.")
256
-
257
- # Procesar entrada del usuario
258
- if prompt := st.chat_input('Describe tu audiencia y el objetivo de tu Reel...'):
259
- process_message(prompt, is_example=False)
260
-
261
- # Procesar ejemplos seleccionados
262
- if state.has_prompt():
263
- prompt = state.prompt
264
- process_message(prompt, is_example=True)
265
- # Limpiar el prompt
266
- state.clear_prompt()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4
  import streamlit as st
5
  from dotenv import load_dotenv
6
  from system_prompts import get_unified_reel_prompt # Cambiar de get_unified_puv_prompt a get_unified_reel_prompt
7
+ from reel_formulas import reel_formulas
8
  from session_state import (
9
  SessionState,
10
  DEFAULT_GEMINI_MODEL,
 
14
 
15
  # Inicializar el estado de la sesión
16
  state = SessionState()
17
+ STREAM_SETTINGS = {'batch_size': 1, 'delay_seconds': 0.01}
18
 
19
  # Función para detectar saludos y generar respuestas personalizadas
20
  def is_greeting(text):
 
48
  typing_indicator.markdown("*Generando respuesta...*")
49
 
50
  response = state.send_message(enhanced_prompt)
51
+ full_response = stream_response(response, message_placeholder, typing_indicator, STREAM_SETTINGS)
52
 
53
  if full_response:
54
  state.add_message(MODEL_ROLE, full_response, AI_AVATAR_ICON)
55
+ if hasattr(state.chat, 'get_history'):
56
+ state.gemini_history = state.chat.get_history()
57
+ else:
58
+ state.gemini_history = getattr(state.chat, 'history', [])
59
  state.save_chat_history()
60
 
61
  except Exception as e:
 
73
  state.chat_title = past_chats[state.chat_id]
74
  joblib.dump(past_chats, PAST_CHATS_LIST_PATH)
75
 
76
+ def detect_formula_selection(prompt):
77
+ """Detecta si el usuario eligió una fórmula por nombre o por número."""
78
+ normalized_prompt = prompt.lower().strip()
79
+ formula_names = list(reel_formulas.keys())
80
+
81
+ # Selección por número (1, 2, 3...)
82
+ if normalized_prompt.isdigit():
83
+ formula_index = int(normalized_prompt) - 1
84
+ if 0 <= formula_index < len(formula_names):
85
+ return formula_names[formula_index]
86
+
87
+ # Selección por nombre parcial/completo
88
+ for formula_name in formula_names:
89
+ if formula_name.lower() in normalized_prompt:
90
+ return formula_name
91
+
92
+ return None
93
+
94
+ def get_user_context_for_formula(max_user_messages=6):
95
+ """Recupera contexto reciente del usuario para rellenar la fórmula elegida."""
96
+ recent_user_messages = [
97
+ m['content'] for m in state.messages
98
+ if m.get('role') == 'user'
99
+ ][-max_user_messages:]
100
+ return "\n".join(f"- {message}" for message in recent_user_messages)
101
+
102
+ def build_formula_prompt(formula_name):
103
+ """Construye un prompt estricto usando la fórmula del diccionario."""
104
+ formula_data = reel_formulas[formula_name]
105
+ formula_description = formula_data.get('description', '').strip()
106
+ user_context = get_user_context_for_formula()
107
+
108
+ return f"""
109
+ El usuario eligió explícitamente esta fórmula: "{formula_name}".
110
+
111
+ APLICA ESTRICTAMENTE la siguiente estructura:
112
+ {formula_description}
113
+
114
+ Contexto real del usuario (úsalo para personalizar el guion):
115
+ {user_context if user_context else '- Sin contexto previo suficiente.'}
116
+
117
+ Instrucciones obligatorias de salida:
118
+ 1) Devuelve SOLO el texto final del Reel (sin encabezados ni etiquetas).
119
+ 2) Respeta el orden y los pasos de la fórmula elegida.
120
+ 3) Incluye un gancho potente y un cierre con llamado a la acción.
121
+ 4) Que tenga longitud aproximada de 60 segundos al leer.
122
+ """
123
+
124
  def get_enhanced_prompt(prompt, is_example):
125
  """Genera el prompt mejorado según el tipo de mensaje"""
126
+ selected_formula = detect_formula_selection(prompt)
127
+ if selected_formula:
128
+ st.session_state.selected_formula = selected_formula
129
+ return build_formula_prompt(selected_formula)
130
+
131
  if is_greeting(prompt):
132
  return f"El usuario te ha saludado con '{prompt}'. Preséntate brevemente, explica qué es un Reel y por qué es importante, y haz las 3 preguntas iniciales para comenzar a crear el guion del Reel (audiencia ideal, producto/servicio, y llamado a la acción). Sé amigable, breve y toma la iniciativa como el experto que eres."
133
  elif is_example:
134
  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 Reel. 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 Reel personalizado."
135
  return prompt
136
 
137
+ def stream_response(response, message_placeholder, typing_indicator, stream_settings):
138
  """Maneja el streaming de la respuesta"""
139
  full_response = ''
140
+ batch_size = max(1, int(stream_settings.get('batch_size', 24)))
141
+ delay_seconds = max(0.0, float(stream_settings.get('delay_seconds', 0.0)))
142
+ pending_chars = 0
143
+
144
  try:
145
  for chunk in response:
146
  if chunk.text:
147
  for ch in chunk.text:
148
  full_response += ch
149
+ pending_chars += 1
150
+ if pending_chars >= batch_size:
151
+ if delay_seconds:
152
+ time.sleep(delay_seconds)
153
+ message_placeholder.markdown(full_response + '▌')
154
+ pending_chars = 0
155
  except Exception as e:
156
  st.error(f"Error en el streaming: {str(e)}")
157
  return ''
158
+
159
+ if pending_chars > 0:
160
+ if delay_seconds:
161
+ time.sleep(delay_seconds)
162
+ message_placeholder.markdown(full_response + '▌')
163
+
164
  typing_indicator.empty()
165
  message_placeholder.markdown(full_response)
166
  return full_response
 
242
  for idx, ejemplo in enumerate(ejemplos):
243
  with cols[idx]:
244
  if st.button(ejemplo["texto"], key=f"ejemplo_{idx}", help=ejemplo["prompt"]):
245
+ st.session_state.pending_example_prompt = ejemplo["prompt"]
246
+ st.session_state.hide_initial_menu = True
247
  st.rerun()
248
 
249
  # Cargar variables de entorno
 
252
  if not GOOGLE_API_KEY:
253
  st.error("Falta la variable de entorno GOOGLE_API_KEY. Configúrala para continuar.")
254
  st.stop()
 
255
 
256
  # Configuración de la aplicación
257
  new_chat_id = f'{time.time()}'
 
275
  # Sidebar para seleccionar chats anteriores
276
  with st.sidebar:
277
  st.write('# Chats Anteriores')
278
+
279
  if state.chat_id is None:
280
  state.chat_id = st.selectbox(
281
  label='Selecciona un chat anterior',
 
299
  state.load_chat_history()
300
 
301
  # Inicializar el modelo y el chat
302
+ system_prompt = get_unified_reel_prompt()
303
+ state.initialize_model(DEFAULT_GEMINI_MODEL, api_key=GOOGLE_API_KEY)
304
+ state.initialize_chat(system_instruction=system_prompt) # Siempre inicializar el chat después del modelo
305
 
306
  # Mostrar mensajes del historial
307
  for message in state.messages:
 
311
  ):
312
  st.markdown(message['content'])
313
 
314
+ # Capturar entrada del usuario antes de renderizar el menú inicial
315
+ user_prompt = st.chat_input('Describe tu audiencia y el objetivo de tu Reel...')
 
 
 
 
 
316
 
317
+ if 'pending_example_prompt' not in st.session_state:
318
+ st.session_state.pending_example_prompt = None
319
+
320
+ if 'hide_initial_menu' not in st.session_state:
321
+ st.session_state.hide_initial_menu = False
322
+
323
+ if state.has_messages():
324
+ st.session_state.hide_initial_menu = True
325
+
326
+ # Renderizar menú inicial en un contenedor limpiable
327
+ initial_menu_container = st.container()
328
+ if (
329
+ not st.session_state.hide_initial_menu
330
+ and not state.has_messages()
331
+ and not user_prompt
332
+ and not st.session_state.pending_example_prompt
333
+ ):
334
+ with initial_menu_container:
335
+ display_initial_header()
336
+ display_examples()
337
+
338
+ # Procesar entrada del usuario (oculta el menú inmediatamente)
339
+ if user_prompt:
340
+ st.session_state.hide_initial_menu = True
341
+ initial_menu_container.empty()
342
+ process_message(user_prompt, is_example=False)
343
+
344
+ # Procesar ejemplo seleccionado (oculta el menú inmediatamente)
345
+ if st.session_state.pending_example_prompt:
346
+ initial_menu_container.empty()
347
+ process_message(st.session_state.pending_example_prompt, is_example=True)
348
+ st.session_state.pending_example_prompt = None