JeCabrera commited on
Commit
fc43c2b
·
verified ·
1 Parent(s): 1af2500

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +531 -186
app.py CHANGED
@@ -1,201 +1,546 @@
1
- import time
2
- import os
3
- import joblib
4
  import streamlit as st
 
5
  import google.generativeai as genai
6
- from dotenv import load_dotenv
7
-
8
- # Función para cargar CSS personalizado
9
- def load_css(file_path):
10
- with open(file_path) as f:
11
- st.markdown(f'<style>{f.read()}</style>', unsafe_allow_html=True)
12
-
13
- # Intentar cargar el CSS personalizado con ruta absoluta para mayor seguridad
14
- try:
15
- css_path = os.path.join(os.path.dirname(__file__), 'static', 'css', 'style.css')
16
- load_css(css_path)
17
- except Exception as e:
18
- print(f"Error al cargar CSS: {e}")
19
- # Si el archivo no existe, crear un estilo básico en línea
20
- st.markdown("""
21
- <style>
22
- .robocopy-title {
23
- color: #4ECDC4 !important;
24
- font-weight: bold;
25
- font-size: 2em;
26
- }
27
- </style>
28
- """, unsafe_allow_html=True)
29
 
 
30
  load_dotenv()
31
- GOOGLE_API_KEY=os.environ.get('GOOGLE_API_KEY')
32
- genai.configure(api_key=GOOGLE_API_KEY)
33
-
34
- new_chat_id = f'{time.time()}'
35
- MODEL_ROLE = 'ai'
36
- AI_AVATAR_ICON = '🤖' # Cambia el emoji por uno de robot para coincidir con tu logo
37
- USER_AVATAR_ICON = '👤' # Añade un avatar para el usuario
38
-
39
- # Create a data/ folder if it doesn't already exist
40
- try:
41
- os.mkdir('data/')
42
- except:
43
- # data/ folder already exists
44
- pass
45
-
46
- # Load past chats (if available)
47
- try:
48
- past_chats: dict = joblib.load('data/past_chats_list')
49
- except:
50
- past_chats = {}
51
-
52
- # Sidebar allows a list of past chats
53
- with st.sidebar:
54
- # Centrar el logo y eliminar el título de RoboCopy
55
- col1, col2, col3 = st.columns([1, 2, 1])
56
- with col2:
57
- st.image("assets/robocopy_logo.png", width=300)
58
-
59
- st.write('# Chats Anteriores')
60
- if st.session_state.get('chat_id') is None:
61
- st.session_state.chat_id = st.selectbox(
62
- label='Selecciona un chat anterior',
63
- options=[new_chat_id] + list(past_chats.keys()),
64
- format_func=lambda x: past_chats.get(x, 'Nuevo Chat'),
65
- placeholder='_',
66
  )
67
- else:
68
- # This will happen the first time AI response comes in
69
- st.session_state.chat_id = st.selectbox(
70
- label='Selecciona un chat anterior',
71
- options=[new_chat_id, st.session_state.chat_id] + list(past_chats.keys()),
72
- index=1,
73
- format_func=lambda x: past_chats.get(x, 'Nuevo Chat' if x != st.session_state.chat_id else st.session_state.chat_title),
74
- placeholder='_',
 
 
 
 
75
  )
76
- # Save new chats after a message has been sent to AI
77
- # TODO: Give user a chance to name chat
78
- st.session_state.chat_title = f'SesiónChat-{st.session_state.chat_id}'
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79
 
80
- st.write('# Chatea con Gemini')
 
 
 
 
 
 
 
81
 
82
- # Chat history (allows to ask multiple questions)
83
- try:
84
- st.session_state.messages = joblib.load(
85
- f'data/{st.session_state.chat_id}-st_messages'
 
 
86
  )
87
- st.session_state.gemini_history = joblib.load(
88
- f'data/{st.session_state.chat_id}-gemini_messages'
 
 
 
 
 
 
 
 
 
89
  )
90
- print('old cache')
91
- except:
92
- st.session_state.messages = []
93
- st.session_state.gemini_history = []
94
- print('new_cache made')
95
- st.session_state.model = genai.GenerativeModel('gemini-2.0-flash')
96
- st.session_state.chat = st.session_state.model.start_chat(
97
- history=st.session_state.gemini_history,
98
- )
99
 
100
- # Display chat messages from history on app rerun
101
- for message in st.session_state.messages:
102
- with st.chat_message(
103
- name=message['role'],
104
- avatar=message.get('avatar'),
105
- ):
106
- st.markdown(message['content'])
107
-
108
- # React to user input
109
- if prompt := st.chat_input('¿En qué puedo ayudarte hoy?'): # Mensaje más amigable
110
- # Save this as a chat for later
111
- if st.session_state.chat_id not in past_chats.keys():
112
- # Es una nueva conversación, generemos un título basado en el primer mensaje
113
- # Primero, guardamos un título temporal
114
- temp_title = f'SesiónChat-{st.session_state.chat_id}'
115
- past_chats[st.session_state.chat_id] = temp_title
116
-
117
- # Generamos un título basado en el contenido del mensaje
118
- try:
119
- # Usamos el mismo modelo para generar un título corto
120
- title_generator = genai.GenerativeModel('gemini-2.0-flash')
121
- title_response = title_generator.generate_content(
122
- f"Genera un título corto (máximo 5 palabras) que describa de qué trata esta consulta, sin usar comillas ni puntuación: '{prompt}'")
123
-
124
- # Obtenemos el título generado
125
- generated_title = title_response.text.strip()
126
-
127
- # Actualizamos el título en past_chats
128
- if generated_title:
129
- st.session_state.chat_title = generated_title
130
- past_chats[st.session_state.chat_id] = generated_title
131
- else:
132
- st.session_state.chat_title = temp_title
133
- except Exception as e:
134
- print(f"Error al generar título: {e}")
135
- st.session_state.chat_title = temp_title
136
- else:
137
- # Ya existe esta conversación, usamos el título guardado
138
- st.session_state.chat_title = past_chats[st.session_state.chat_id]
139
-
140
- joblib.dump(past_chats, 'data/past_chats_list')
141
-
142
- # Display user message in chat message container
143
- with st.chat_message('user', avatar=USER_AVATAR_ICON): # Añade el avatar del usuario
144
- st.markdown(prompt)
145
- # Add user message to chat history
146
- st.session_state.messages.append(
147
- dict(
148
- role='user',
149
- content=prompt,
150
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
151
  )
152
- ## Send message to AI
153
- response = st.session_state.chat.send_message(
154
- prompt,
155
- stream=True,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
156
  )
157
- # Display assistant response in chat message container
158
- with st.chat_message(
159
- name=MODEL_ROLE,
160
- avatar=AI_AVATAR_ICON,
161
- ):
162
- message_placeholder = st.empty()
163
- full_response = ''
164
- assistant_response = response
165
-
166
- # Añade un indicador de "escribiendo..."
167
- typing_indicator = st.empty()
168
- typing_indicator.markdown("*RoboCopy está escribiendo...*")
169
-
170
- # Streams in a chunk at a time
171
- for chunk in response:
172
- # Simulate stream of chunk
173
- # TODO: Chunk missing `text` if API stops mid-stream ("safety"?)
174
- for ch in chunk.text.split(' '):
175
- full_response += ch + ' '
176
- time.sleep(0.1) # Aumentado de 0.05 a 0.1 segundos para una velocidad más lenta
177
- # Rewrites with a cursor at end
178
- message_placeholder.write(full_response + '▌')
179
- # Elimina el indicador de escritura
180
- typing_indicator.empty()
181
- # Write full message with placeholder
182
- message_placeholder.write(full_response)
183
-
184
- # Add assistant response to chat history
185
- st.session_state.messages.append(
186
- dict(
187
- role=MODEL_ROLE,
188
- content=st.session_state.chat.history[-1].parts[0].text,
189
- avatar=AI_AVATAR_ICON,
190
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
191
  )
192
- st.session_state.gemini_history = st.session_state.chat.history
193
- # Save to file
194
- joblib.dump(
195
- st.session_state.messages,
196
- f'data/{st.session_state.chat_id}-st_messages',
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
197
  )
198
- joblib.dump(
199
- st.session_state.gemini_history,
200
- f'data/{st.session_state.chat_id}-gemini_messages',
201
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from dotenv import load_dotenv
 
 
2
  import streamlit as st
3
+ import os
4
  import google.generativeai as genai
5
+ import random
6
+ import datetime
7
+ from streamlit import session_state as state
8
+ from formulas.webinar_formulas import webinar_formulas
9
+ from formulas.webinar_name_formulas import webinar_name_formulas
10
+ from formulas.angles_webinar_names import angles_webinar_names
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
 
12
+ # Cargar las variables de entorno
13
  load_dotenv()
14
+
15
+ # Configurar la API de Google
16
+ genai.configure(api_key=os.getenv("GOOGLE_API_KEY"))
17
+
18
+ # Función auxiliar para mostrar el contenido generado y los botones de descarga
19
+ # In the display_generated_content function, modify the names section:
20
+
21
+ def display_generated_content(col, generated_content, content_type):
22
+ timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
23
+
24
+ # Determinar el tipo de contenido para personalizar los botones y títulos
25
+ if content_type == "script":
26
+ download_label = "DESCARGAR GUIÓN DE WEBINAR ▶▶"
27
+ file_name = f"guion_webinar_{timestamp}.txt"
28
+ subheader_text = "Tu guión de webinar:"
29
+
30
+ # Mostrar botón de descarga superior para guiones
31
+ col.download_button(
32
+ label=download_label,
33
+ data=generated_content,
34
+ file_name=file_name,
35
+ mime="text/plain",
36
+ key=f"download_top_{content_type}"
 
 
 
 
 
 
 
 
 
 
 
 
37
  )
38
+
39
+ # Mostrar el contenido generado
40
+ col.subheader(subheader_text)
41
+ col.markdown(generated_content)
42
+
43
+ # Mostrar botón de descarga inferior
44
+ col.download_button(
45
+ label=download_label,
46
+ data=generated_content,
47
+ file_name=file_name,
48
+ mime="text/plain",
49
+ key=f"download_bottom_{content_type}"
50
  )
51
+ else: # nombres
52
+ subheader_text = "Tus nombres de webinar:"
53
+ file_name = f"nombres_webinar_{timestamp}.txt"
54
+ download_label = "DESCARGAR NOMBRES DE WEBINAR ▶▶"
55
+
56
+ # Contar el número de nombres generados
57
+ num_names = len([line for line in generated_content.split('\n') if line.strip().startswith(('1.', '2.', '3.', '4.', '5.', '6.', '7.', '8.', '9.', '10.', '11.', '12.', '13.', '14.', '15.'))])
58
+
59
+ # Para los nombres, usar HTML personalizado en lugar de st.download_button
60
+ import base64
61
+
62
+ # Codificar el contenido para descarga
63
+ b64 = base64.b64encode(generated_content.encode()).decode()
64
+
65
+ # Crear botón de descarga superior con clase específica
66
+ top_button_html = f"""
67
+ <div class="custom-download-button names-download-button top-button">
68
+ <a href="data:text/plain;base64,{b64}" download="{file_name}">
69
+ {download_label}
70
+ </a>
71
+ </div>
72
+ """
73
+
74
+ # Crear botón de descarga inferior con clase específica
75
+ bottom_button_html = f"""
76
+ <div class="custom-download-button names-download-button bottom-button">
77
+ <a href="data:text/plain;base64,{b64}" download="{file_name}">
78
+ {download_label}
79
+ </a>
80
+ </div>
81
+ """
82
+
83
+ # Mostrar botón de descarga superior
84
+ col.markdown(top_button_html, unsafe_allow_html=True)
85
+
86
+ # Mostrar el contenido generado
87
+ col.subheader(subheader_text)
88
+ col.markdown(generated_content)
89
+
90
+ # Mostrar botón de descarga inferior
91
+ col.markdown(bottom_button_html, unsafe_allow_html=True)
92
+
93
+ # Implementar la función generate_and_display para reemplazar código duplicado
94
+ def generate_and_display(col, generator_func, audience, product, temperature, selected_formula, content_type, **kwargs):
95
+ if validate_inputs(audience, product):
96
+ try:
97
+ with col:
98
+ with st.spinner(f"Generando {'guión' if content_type == 'script' else 'nombres'} de webinar...", show_time=True):
99
+ # Llamar a la función generadora con los parámetros adecuados
100
+ generated_content = generator_func(
101
+ audience=audience,
102
+ topic=product,
103
+ temperature=temperature,
104
+ selected_formula=selected_formula,
105
+ **kwargs
106
+ )
107
+
108
+ # Mostrar el contenido generado usando la función auxiliar
109
+ display_generated_content(col, generated_content, content_type)
110
+
111
+ except ValueError as e:
112
+ col.error(f"Error: {str(e)}")
113
+ else:
114
+ col.error("Por favor, proporciona el público objetivo y el tema del webinar.")
115
 
116
+ # Función para crear la configuración del modelo (evita duplicación)
117
+ def create_model_config(temperature):
118
+ return {
119
+ "temperature": temperature,
120
+ "top_p": 0.65,
121
+ "top_k": 360,
122
+ "max_output_tokens": 8196,
123
+ }
124
 
125
+ # Función para inicializar el modelo
126
+ def initialize_model(temperature):
127
+ config = create_model_config(temperature)
128
+ return genai.GenerativeModel(
129
+ model_name="gemini-2.0-flash",
130
+ generation_config=config,
131
  )
132
+
133
+ # Refactored model interaction function to reduce duplication
134
+ def generate_content(prompt_instructions, temperature):
135
+ model = initialize_model(temperature)
136
+ chat_session = model.start_chat(
137
+ history=[
138
+ {
139
+ "role": "user",
140
+ "parts": [prompt_instructions],
141
+ },
142
+ ]
143
  )
144
+ response = chat_session.send_message("Generate the content following exactly the provided instructions. All content must be in Spanish.")
145
+ return response.text
 
 
 
 
 
 
 
146
 
147
+ # Función para generar nombres de webinars
148
+ # Refactorizar la función generate_webinar_names para que acepte los mismos parámetros que generate_webinar_script
149
+ def generate_webinar_names(audience, topic, temperature, selected_formula, number_of_names=5, selected_angle=None, **kwargs):
150
+ # Incluir las instrucciones del sistema en el prompt principal
151
+ system_prompt = """You are a world-class copywriter, with expertise in crafting compelling and disruptive webinar titles that immediately capture the audience's attention and drive registrations.
152
+
153
+ FORMAT RULES:
154
+ - Each webinar name must start with number and period
155
+ - One webinar name per line
156
+ - No explanations or categories
157
+ - Add a line break between each name
158
+ - Avoid unnecessary : symbols
159
+ - Each webinar name must be a complete, intriguing and creative title
160
+ - WRITE ALL WEBINAR NAMES IN SPANISH
161
+
162
+ FORMAT EXAMPLE:
163
+ 1. Nombre del Webinar 1.
164
+
165
+ 2. Nombre del Webinar 2.
166
+
167
+ 3. Nombre del Webinar 3.
168
+
169
+ 4. Nombre del Webinar 4.
170
+
171
+ 5. Nombre del Webinar 5.
172
+
173
+ IMPORTANT:
174
+ - Each webinar name must be unique, memorable and disruptive
175
+ - Create curiosity and intrigue with unexpected combinations
176
+ - Use creative language that stands out from typical webinar titles
177
+ - Incorporate pattern interrupts that make people stop scrolling
178
+ - Adapt speaking language from the audience
179
+ - Focus on transformative benefits with creative angles
180
+ - Follow the selected formula structure but add creative twists
181
+ - WRITE ALL WEBINAR NAMES IN SPANISH"""
182
+
183
+ # Iniciar el prompt con las instrucciones del sistema
184
+ webinar_names_instruction = f"{system_prompt}\n\n"
185
+
186
+ # Añadir instrucciones de ángulo solo si no es "NINGUNO" y se proporcionó un ángulo
187
+ if selected_angle and selected_angle != "NINGUNO":
188
+ webinar_names_instruction += f"""
189
+ MAIN ANGLE: {selected_angle}
190
+ SPECIFIC ANGLE INSTRUCTIONS:
191
+ {angles_webinar_names[selected_angle]["instruction"]}
192
+
193
+ IMPORTANT: The {selected_angle} angle should be applied as a "style layer" over the formula structure:
194
+ 1. Keep the base structure of the formula intact
195
+ 2. Apply the tone and style of the {selected_angle} angle
196
+ 3. Ensure that each element of the formula reflects the angle
197
+ 4. The angle affects "how" it is said, not "what" is said
198
+
199
+ SUCCESSFUL EXAMPLES OF THE {selected_angle} ANGLE:
200
+ """
201
+ for example in angles_webinar_names[selected_angle]["examples"]:
202
+ webinar_names_instruction += f"- {example}\n"
203
+
204
+ # Instrucciones específicas para la tarea
205
+ webinar_names_instruction += (
206
+ f"\nYour task is to create {number_of_names} irresistible, creative and disruptive webinar names for {audience} "
207
+ f"that instantly capture attention and generate registrations for a webinar about {topic}. "
208
+ f"Focus on awakening genuine curiosity, creating intrigue, and communicating the value they will get by registering."
209
+ f"\n\n"
210
+ f"IMPORTANT: Use these examples of the selected formula as inspiration, but make your titles more creative and disruptive. "
211
+ f"Each example represents a base structure to follow, but add unexpected elements and creative twists"
212
+ f":\n\n"
213
  )
214
+
215
+ # Agregar ejemplos aleatorios de la fórmula (keeping examples in Spanish)
216
+ random_examples = random.sample(selected_formula['examples'], min(5, len(selected_formula['examples'])))
217
+ webinar_names_instruction += "EXAMPLES OF THE FORMULA TO FOLLOW (BUT MAKE YOURS MORE CREATIVE):\n"
218
+ for i, example in enumerate(random_examples, 1):
219
+ webinar_names_instruction += f"{i}. {example}\n"
220
+
221
+ # Instrucciones específicas (translated to English)
222
+ webinar_names_instruction += "\nSPECIFIC INSTRUCTIONS:\n"
223
+ webinar_names_instruction += "1. Use the same basic structure as the examples but add creative twists\n"
224
+ webinar_names_instruction += "2. Create curiosity gaps that make people want to learn more\n"
225
+ webinar_names_instruction += "3. Use unexpected word combinations that surprise the reader\n"
226
+ webinar_names_instruction += "4. Incorporate pattern interrupts that make people stop and think\n"
227
+ webinar_names_instruction += f"5. Adapt the content for {audience} while making titles more memorable and disruptive\n\n"
228
+ webinar_names_instruction += f"FORMULA TO FOLLOW (AS A BASE):\n{selected_formula['description']}\n\n"
229
+ webinar_names_instruction += f"""
230
+ CREATIVE TECHNIQUES TO APPLY:
231
+ 1. Use unexpected metaphors or analogies
232
+ 2. Create intriguing contrasts or paradoxes
233
+ 3. Challenge conventional wisdom with provocative statements
234
+ 4. Use power words that evoke emotion
235
+ 5. Create curiosity with incomplete loops or questions
236
+ 6. Use specific numbers or data points that seem unusual
237
+
238
+ GENERATE NOW:
239
+ Create {number_of_names} creative, disruptive webinar names that use the formula structure as a base but add unexpected creative elements to make them stand out.
240
+ """
241
+
242
+ # Enviar el mensaje al modelo
243
+ # Use the common generate_content function
244
+ return generate_content(webinar_names_instruction, temperature)
245
+
246
+ # Update the create_input_section function to include the product/offer field
247
+ def create_input_section(col, audience_key, product_key, formulas, formula_key, offer_key=None):
248
+ audience = col.text_input("¿Quién es tu público objetivo?", placeholder="Ejemplo: Emprendedores digitales", key=audience_key)
249
+ product = col.text_input("¿Sobre qué tema es tu webinar?", placeholder="Ejemplo: Marketing de afiliados", key=product_key)
250
+
251
+ # Add the new product/offer field if a key is provided
252
+ offer = None
253
+ if offer_key:
254
+ offer = col.text_input("¿Cuál es tu producto u oferta?", placeholder="Ejemplo: Curso de marketing de afiliados", key=offer_key)
255
+
256
+ # Formula selection
257
+ formula_keys = list(formulas.keys())
258
+ selected_formula_key = col.selectbox(
259
+ "Selecciona un framework de webinar",
260
+ options=formula_keys,
261
+ key=formula_key
262
  )
263
+
264
+ if offer_key:
265
+ return audience, product, selected_formula_key, offer
266
+ else:
267
+ return audience, product, selected_formula_key
268
+
269
+ # Update the generate_webinar_script function to include the offer parameter
270
+ def generate_webinar_script(audience, topic, temperature, selected_formula, offer=None, creative_idea=None):
271
+ model = initialize_model(temperature)
272
+
273
+ # Include offer in the system prompt if provided
274
+ offer_text = f" and selling {offer}" if offer else ""
275
+
276
+ # Incluir las instrucciones del sistema en el prompt principal
277
+ system_prompt = f"""You are a collaborative team of world-class experts working together to create an exceptional webinar script that converts audience into customers.
278
+
279
+ THE EXPERT TEAM:
280
+
281
+ 1. MASTER WEBINAR STRATEGIST:
282
+ - Expert in webinar frameworks and conversion strategies
283
+ - Trained in the Perfect Webinar methodology by Russell Brunson
284
+ - Ensures the script follows the selected framework structure precisely
285
+ - Focuses on strategic placement of key conversion elements
286
+
287
+ 2. ELITE DIRECT RESPONSE COPYWRITER:
288
+ - Trained by Gary Halbert, Gary Bencivenga, and David Ogilvy
289
+ - Creates compelling hooks, stories, and persuasive elements
290
+ - Crafts irresistible calls to action that drives conversions
291
+ - Ensures the language resonates with the target audience
292
+
293
+ 3. AUDIENCE PSYCHOLOGY SPECIALIST:
294
+ - Expert in understanding audience motivations and objections
295
+ - Creates content that builds genuine connection and trust
296
+ - Identifies and addresses hidden fears and desires
297
+ - Ensures the content feels personal and relevant
298
+
299
+ 4. STORYTELLING MASTER:
300
+ - Creates compelling narratives that illustrate key points
301
+ - Develops relatable examples and case studies
302
+ - Ensures stories support the transformation being offered
303
+ - Makes complex concepts accessible through narrative
304
+
305
+ 5. WEBINAR ENGAGEMENT EXPERT:
306
+ - Specializes in maintaining audience attention throughout
307
+ - Creates interactive elements and engagement hooks
308
+ - Develops compelling transitions between sections
309
+ - Ensures the webinar flows naturally and keeps interest high
310
+
311
+ FORMAT REQUIREMENTS:
312
+ - Create a complete webinar script with clear sections and subsections
313
+ - Include specific talking points for each section
314
+ - Write in a conversational, engaging tone
315
+ - Include persuasive elements and calls to action
316
+ - Follow the selected webinar framework structure exactly
317
+ - WRITE THE ENTIRE SCRIPT IN SPANISH
318
+ - Start directly with the webinar content without introductory text
319
+ - DO NOT include any explanatory text at the beginning like "Here's the webinar script..." or "I've created a webinar script..."
320
+
321
+ COLLABORATIVE PROCESS:
322
+ As a team of experts, you will:
323
+ 1. Analyze the framework '{selected_formula['description']}' to understand its core principles
324
+ 2. Identify how to best adapt this framework for {audience} learning about {topic}{offer_text}
325
+ 3. Create persuasive language that resonates with {audience}
326
+ 4. Ensure the script maintains engagement throughout
327
+ 5. Follow the exact structure provided in the framework"""
328
+
329
+ # Añadir instrucciones para la idea creativa si existe
330
+ if creative_idea:
331
+ system_prompt += f"""
332
+ CREATIVE CONCEPT:
333
+ Use the following creative concept as the central theme for the webinar:
334
+ "{creative_idea}"
335
+
336
+ CREATIVE CONCEPT INSTRUCTIONS:
337
+ 1. This concept should be the unifying theme across the entire webinar
338
+ 2. Use it as a metaphor or analogy throughout the presentation
339
+ 3. Develop different aspects of this concept in each section
340
+ 4. Make sure the concept naturally connects to the product benefits
341
+ 5. The concept should make the webinar more memorable and engaging
342
+ """
343
+
344
+ # Update the task instructions to include the offer
345
+ offer_instruction = f" and selling {offer}" if offer else ""
346
+
347
+ # Instrucciones específicas para la tarea
348
+ webinar_script_instruction = (
349
+ f"{system_prompt}\n\n"
350
+ f"\nYour task is to create a complete webinar script IN SPANISH for {audience} "
351
+ f"about {topic}{offer_instruction} that is persuasive and converts the audience into customers. "
352
+ f"The script must follow exactly the structure of the framework '{selected_formula['description']}' "
353
+ f"and must include all the necessary elements for a successful webinar."
354
+ f"\n\n"
355
  )
356
+
357
+ # Estructura del webinar
358
+ webinar_script_instruction += "WEBINAR STRUCTURE TO FOLLOW:\n"
359
+ for i, step in enumerate(selected_formula['structure'], 1):
360
+ webinar_script_instruction += f"{i}. {step}\n"
361
+
362
+ # Ejemplos de webinars exitosos
363
+ webinar_script_instruction += "\n\nEXAMPLES OF SUCCESSFUL WEBINARS WITH THIS STRUCTURE:\n"
364
+ for i, example in enumerate(selected_formula['examples'], 1):
365
+ webinar_script_instruction += f"{i}. {example}\n"
366
+
367
+ # Instrucciones específicas - Reforzar el español
368
+ webinar_script_instruction += f"""
369
+ SPECIFIC INSTRUCTIONS:
370
+ 1. Create a complete script that follows exactly the provided structure
371
+ 2. Include persuasive elements and clear calls to action
372
+ 3. Adapt the language and examples specifically for {audience}
373
+ 4. Focus on the transformative benefits of {topic}
374
+ 5. Include relevant stories and examples that reinforce your points
375
+ 6. Use a conversational but professional tone
376
+ 7. Make sure each section fulfills its specific purpose in the framework
377
+ 8. IMPORTANT: Write the ENTIRE script in Spanish (neutral Latin American Spanish)
378
+ 9. DO NOT include any introductory text like "Here's the webinar script..." or "I've created a webinar script..."
379
+ 10. Start directly with the webinar title and content
380
+ 11. ALL section titles, headers, and content MUST be in Spanish
381
+ 12. Ensure ALL examples, stories, and calls to action are in Spanish
382
+
383
+ GENERATE NOW:
384
+ Create a complete webinar script following faithfully the structure of the selected framework, entirely in Spanish.
385
+ """
386
+
387
+ # Enviar el mensaje al modelo
388
+ chat_session = model.start_chat(
389
+ history=[
390
+ {
391
+ "role": "user",
392
+ "parts": [webinar_script_instruction],
393
+ },
394
+ ]
395
  )
396
+ response = chat_session.send_message("Generate the webinar script IN NEUTRAL SPANISH following exactly the provided structure. All content must be in neutral Spanish (not Spain Spanish). Start directly with the webinar content without any introductory text.")
397
+
398
+ return response.text
399
+
400
+ # Función para validar entradas (evita duplicación)
401
+ def validate_inputs(audience, product):
402
+ has_audience = audience.strip() != ""
403
+ has_product = product.strip() != ""
404
+ return has_audience and has_product
405
+
406
+ # Update the load_css function comment to be more descriptive
407
+ def load_css():
408
+ css_path = "styles/styles.css"
409
+ if os.path.exists(css_path):
410
+ try:
411
+ with open(css_path, "r") as f:
412
+ st.markdown(f"<style>{f.read()}</style>", unsafe_allow_html=True)
413
+ except Exception as e:
414
+ st.warning(f"Error al cargar el archivo CSS: {str(e)}")
415
+ else:
416
+ st.warning(f"No se encontró el archivo CSS en {css_path}")
417
+
418
+ # Modify the page config section to include the CSS loading and remove menu
419
+ st.set_page_config(
420
+ page_title="Perfect Webinar Framework",
421
+ layout="wide",
422
+ initial_sidebar_state="expanded",
423
+ menu_items=None # This removes the three dots menu
424
+ )
425
+ load_css() # This will load the styles from styles.css
426
+
427
+ # Leer el contenido del archivo manual.md
428
+ with open("manual.md", "r", encoding="utf-8") as file:
429
+ manual_content = file.read()
430
+
431
+ # Mostrar el contenido del manual en el sidebar
432
+ st.sidebar.markdown(manual_content)
433
+
434
+ # Agregar título y subtítulo usando HTML
435
+ st.markdown("<h1 style='text-align: center;'>Perfect Webinar Framework</h1>", unsafe_allow_html=True)
436
+ st.markdown("<h3 style='text-align: center;'>Crea guiones y títulos de webinars persuasivos que convierten</h3>", unsafe_allow_html=True)
437
+
438
+ # Crear pestañas para la interfaz
439
+ tab1, tab2 = st.tabs(["Guiones de Webinar", "Nombres de Webinar"])
440
+
441
+ # Primera pestaña - Generador de Guiones de Webinar
442
+ with tab1:
443
+ tab1.subheader("Script Webinar")
444
+
445
+ # Crear columnas para la interfaz
446
+ col1, col2 = tab1.columns([1, 2])
447
+
448
+ # Columna de entrada usando la función reutilizable
449
+ with col1:
450
+ # Inputs básicos (fuera del acordeón)
451
+ webinar_script_audience = st.text_input("¿Quién es tu público objetivo?", placeholder="Ejemplo: Emprendedores digitales", key="webinar_script_audience")
452
+ webinar_script_product = st.text_input("¿Sobre qué tema es tu webinar?", placeholder="Ejemplo: Marketing de afiliados", key="webinar_script_product")
453
+ webinar_script_offer = st.text_input("¿Cuál es tu producto u oferta?", placeholder="Ejemplo: Curso de marketing de afiliados", key="webinar_script_offer")
454
+
455
+ # Botón de generación (movido aquí, justo después de los campos principales)
456
+ submit_webinar_script = st.button("GENERAR GUIÓN DE WEBINAR ▶▶", key="generate_webinar_script")
457
+
458
+ # Opciones avanzadas en el acordeón
459
+ with st.expander("Personaliza tu guión de webinar"):
460
+ # Selector de fórmula (ahora dentro del acordeón)
461
+ selected_webinar_formula_key = st.selectbox(
462
+ "Selecciona un framework de webinar",
463
+ options=list(webinar_formulas.keys()),
464
+ key="webinar_formula"
465
+ )
466
+
467
+ # Nuevo campo para la idea creativa
468
+ creative_idea = st.text_area(
469
+ "Idea creativa (opcional)",
470
+ placeholder="Introduce una idea o concepto creativo que quieras usar como tema central en tu webinar",
471
+ help="Este concepto será el tema unificador a lo largo de tu webinar, haciéndolo más memorable y atractivo",
472
+ key="webinar_creative_idea"
473
+ )
474
+
475
+ # Slider de creatividad (ya existente)
476
+ webinar_script_temperature = st.slider("Creatividad", min_value=0.0, max_value=2.0, value=1.0, step=0.1, key="webinar_script_temp")
477
+
478
+ selected_webinar_formula = webinar_formulas[selected_webinar_formula_key]
479
+
480
+ # Usar la función generate_and_display para generar y mostrar el guión
481
+ if submit_webinar_script:
482
+ generate_and_display(
483
+ col=col2,
484
+ generator_func=generate_webinar_script,
485
+ audience=webinar_script_audience,
486
+ product=webinar_script_product,
487
+ temperature=webinar_script_temperature,
488
+ selected_formula=selected_webinar_formula,
489
+ content_type="script",
490
+ offer=webinar_script_offer if webinar_script_offer.strip() else None,
491
+ creative_idea=creative_idea if creative_idea.strip() else None
492
+ )
493
+
494
+ # Segunda pestaña - Generador de Nombres de Webinar
495
+ with tab2:
496
+ tab2.subheader("Nombres de Webinar")
497
+
498
+ # Crear columnas para la interfaz
499
+ col1, col2 = tab2.columns([1, 2])
500
+
501
+ # Columna de entrada
502
+ with col1:
503
+ # Inputs básicos
504
+ webinar_names_audience = st.text_input("¿Quién es tu público objetivo?", placeholder="Ejemplo: Emprendedores digitales", key="webinar_names_audience")
505
+ webinar_names_product = st.text_input("¿Sobre qué tema es tu webinar?", placeholder="Ejemplo: Marketing de afiliados", key="webinar_names_product")
506
+
507
+ # Botón de generación (movido aquí, justo después de los campos principales)
508
+ submit_webinar_names = st.button("GENERAR NOMBRES DE WEBINAR ▶▶", key="generate_webinar_names")
509
+
510
+ # Opciones avanzadas en el acordeón
511
+ with st.expander("Personaliza tus nombres de webinar"):
512
+ # Selector de fórmula
513
+ selected_name_formula_key = st.selectbox(
514
+ "Selecciona una fórmula para tus nombres",
515
+ options=list(webinar_name_formulas.keys()),
516
+ key="webinar_name_formula"
517
+ )
518
+
519
+ # Selector de ángulo
520
+ selected_angle = st.selectbox(
521
+ "Selecciona un ángulo (opcional)",
522
+ options=["NINGUNO"] + list(angles_webinar_names.keys()),
523
+ key="webinar_name_angle"
524
+ )
525
+
526
+ # Número de nombres a generar
527
+ number_of_names = st.slider("Número de nombres a generar", min_value=3, max_value=15, value=5, step=1, key="number_of_names")
528
+
529
+ # Slider de creatividad
530
+ webinar_names_temperature = st.slider("Creatividad", min_value=0.0, max_value=2.0, value=1.0, step=0.1, key="webinar_names_temp")
531
+
532
+ selected_name_formula = webinar_name_formulas[selected_name_formula_key]
533
+
534
+ # Usar la función generate_and_display para generar y mostrar los nombres
535
+ if submit_webinar_names:
536
+ generate_and_display(
537
+ col=col2,
538
+ generator_func=generate_webinar_names,
539
+ audience=webinar_names_audience,
540
+ product=webinar_names_product,
541
+ temperature=webinar_names_temperature,
542
+ selected_formula=selected_name_formula,
543
+ content_type="names",
544
+ number_of_names=number_of_names,
545
+ selected_angle=selected_angle if selected_angle != "NINGUNO" else None
546
+ )