JeCabrera commited on
Commit
e994448
·
verified ·
1 Parent(s): 6748f9a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +64 -144
app.py CHANGED
@@ -2,19 +2,14 @@ import time
2
  import os
3
  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 reel_formulas import reel_formulas
8
- from session_state import (
9
- SessionState,
10
- DEFAULT_GEMINI_MODEL,
11
- DATA_DIR,
12
- PAST_CHATS_LIST_PATH,
13
- )
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,14 +43,11 @@ def process_message(prompt, is_example=False):
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:
@@ -71,96 +63,50 @@ def handle_chat_title(prompt):
71
  past_chats[state.chat_id] = state.chat_title
72
  else:
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
@@ -238,21 +184,17 @@ def display_examples():
238
  ]
239
 
240
  # Crear los botones de ejemplo
241
- selected_prompt = None
242
  cols = st.columns(4)
243
  for idx, ejemplo in enumerate(ejemplos):
244
  with cols[idx]:
245
  if st.button(ejemplo["texto"], key=f"ejemplo_{idx}", help=ejemplo["prompt"]):
246
- st.session_state.pending_example_prompt = ejemplo["prompt"]
247
- st.session_state.hide_initial_menu = True
248
  st.rerun()
249
 
250
  # Cargar variables de entorno
251
  load_dotenv()
252
- GOOGLE_API_KEY = os.environ.get('GOOGLE_API_KEY')
253
- if not GOOGLE_API_KEY:
254
- st.error("Falta la variable de entorno GOOGLE_API_KEY. Configúrala para continuar.")
255
- st.stop()
256
 
257
  # Configuración de la aplicación
258
  new_chat_id = f'{time.time()}'
@@ -260,33 +202,22 @@ MODEL_ROLE = 'ai'
260
  AI_AVATAR_ICON = '🤖' # Cambia el emoji por uno de robot para coincidir con tu logo
261
  USER_AVATAR_ICON = '👤' # Añade un avatar para el usuario
262
 
263
- # Resolver usuario actual (Firebase Auth token -> uid, fallback local)
264
- firebase_store = FirebaseSessionStore.from_env()
265
- id_token = st.query_params.get("token")
266
- firebase_uid = None
267
- if firebase_store and id_token:
268
- firebase_uid = firebase_store.verify_id_token(id_token)
269
- state.set_storage(firebase_uid or state.user_id, firebase_store)
270
-
271
- user_data_dir = f'{DATA_DIR}/users/{state.user_id}'
272
- past_chats_path = f'{user_data_dir}/past_chats_list'
273
-
274
  # Crear carpeta de datos si no existe
275
  try:
276
- os.mkdir(DATA_DIR)
277
- except FileExistsError:
 
278
  pass
279
 
280
  # Cargar chats anteriores
281
  try:
282
- past_chats = joblib.load(PAST_CHATS_LIST_PATH)
283
- except (FileNotFoundError, EOFError):
284
  past_chats = {}
285
 
286
  # Sidebar para seleccionar chats anteriores
287
  with st.sidebar:
288
  st.write('# Chats Anteriores')
289
-
290
  if state.chat_id is None:
291
  state.chat_id = st.selectbox(
292
  label='Selecciona un chat anterior',
@@ -310,9 +241,8 @@ with st.sidebar:
310
  state.load_chat_history()
311
 
312
  # Inicializar el modelo y el chat
313
- system_prompt = get_unified_reel_prompt()
314
- state.initialize_model(DEFAULT_GEMINI_MODEL, api_key=GOOGLE_API_KEY)
315
- state.initialize_chat(system_instruction=system_prompt) # Siempre inicializar el chat después del modelo
316
 
317
  # Mostrar mensajes del historial
318
  for message in state.messages:
@@ -322,38 +252,28 @@ for message in state.messages:
322
  ):
323
  st.markdown(message['content'])
324
 
325
- # Capturar entrada del usuario antes de renderizar el menú inicial
326
- user_prompt = st.chat_input('Describe tu audiencia y el objetivo de tu Reel...')
327
-
328
- if 'pending_example_prompt' not in st.session_state:
329
- st.session_state.pending_example_prompt = None
330
-
331
- if 'hide_initial_menu' not in st.session_state:
332
- st.session_state.hide_initial_menu = False
333
-
334
- if state.has_messages():
335
- st.session_state.hide_initial_menu = True
336
-
337
- # Renderizar menú inicial en un contenedor limpiable
338
- initial_menu_container = st.container()
339
- if (
340
- not st.session_state.hide_initial_menu
341
- and not state.has_messages()
342
- and not user_prompt
343
- and not st.session_state.pending_example_prompt
344
- ):
345
- with initial_menu_container:
346
- display_initial_header()
347
- display_examples()
348
-
349
- # Procesar entrada del usuario (oculta el menú inmediatamente)
350
- if user_prompt:
351
- st.session_state.hide_initial_menu = True
352
- initial_menu_container.empty()
353
- process_message(user_prompt, is_example=False)
354
 
355
- # Procesar ejemplo seleccionado (oculta el menú inmediatamente)
356
- if st.session_state.pending_example_prompt:
357
- initial_menu_container.empty()
358
- process_message(st.session_state.pending_example_prompt, is_example=True)
359
- st.session_state.pending_example_prompt = None
 
 
 
 
 
 
 
 
 
 
 
 
 
2
  import os
3
  import joblib
4
  import streamlit as st
5
+ import google.generativeai as genai
6
  from dotenv import load_dotenv
7
+ from reel_formulas import reel_formulas # Cambiar de puv_formulas a reel_formulas
8
  from system_prompts import get_unified_reel_prompt # Cambiar de get_unified_puv_prompt a get_unified_reel_prompt
9
+ from session_state import SessionState
 
 
 
 
 
 
10
 
11
  # Inicializar el estado de la sesión
12
  state = SessionState()
 
13
 
14
  # Función para detectar saludos y generar respuestas personalizadas
15
  def is_greeting(text):
 
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:
 
63
  past_chats[state.chat_id] = state.chat_title
64
  else:
65
  state.chat_title = past_chats[state.chat_id]
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 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."
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 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."
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:
100
  if chunk.text:
101
  for ch in chunk.text:
102
  full_response += ch
103
+ time.sleep(0.01)
104
+ typing_indicator.markdown("*Generando respuesta...*")
105
+ message_placeholder.markdown(full_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
 
184
  ]
185
 
186
  # Crear los botones de ejemplo
 
187
  cols = st.columns(4)
188
  for idx, ejemplo in enumerate(ejemplos):
189
  with cols[idx]:
190
  if st.button(ejemplo["texto"], key=f"ejemplo_{idx}", help=ejemplo["prompt"]):
191
+ state.prompt = ejemplo["prompt"]
 
192
  st.rerun()
193
 
194
  # Cargar variables de entorno
195
  load_dotenv()
196
+ GOOGLE_API_KEY=os.environ.get('GOOGLE_API_KEY')
197
+ genai.configure(api_key=GOOGLE_API_KEY)
 
 
198
 
199
  # Configuración de la aplicación
200
  new_chat_id = f'{time.time()}'
 
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:
222
  state.chat_id = st.selectbox(
223
  label='Selecciona un chat anterior',
 
241
  state.load_chat_history()
242
 
243
  # Inicializar el modelo y el chat
244
+ state.initialize_model('gemini-3.1-flash-lite-preview')
245
+ state.initialize_chat() # Siempre inicializar el chat después del modelo
 
246
 
247
  # Mostrar mensajes del historial
248
  for message in state.messages:
 
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_reel_prompt() # Cambiar de get_unified_puv_prompt a get_unified_reel_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 audiencia y el objetivo de tu Reel...'):
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()