JeCabrera commited on
Commit
4b010b5
verified
1 Parent(s): 4890aef

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +498 -306
app.py CHANGED
@@ -1,12 +1,14 @@
1
- import streamlit as st
2
  from dotenv import load_dotenv
 
3
  import os
4
  import google.generativeai as genai
5
- from style import styles
6
- from prompts import create_instruction
7
- from consciousness_levels import CONSCIOUSNESS_LEVELS
8
-
9
- st.set_page_config(page_title="Generador de Cliente Ideal", page_icon="馃懁", layout="wide")
 
 
10
 
11
  # Cargar las variables de entorno
12
  load_dotenv()
@@ -14,319 +16,509 @@ load_dotenv()
14
  # Configurar la API de Google
15
  genai.configure(api_key=os.getenv("GOOGLE_API_KEY"))
16
 
17
- # Inicializar variables de estado en session_state si no existen
18
- if 'perfil_cliente' not in st.session_state:
19
- st.session_state.perfil_cliente = None
20
- if 'producto' not in st.session_state:
21
- st.session_state.producto = ""
22
- if 'habilidades' not in st.session_state:
23
- st.session_state.habilidades = ""
24
- if 'creatividad' not in st.session_state:
25
- st.session_state.creatividad = 1.0
26
- # Update the default format in session state
27
- if 'formato' not in st.session_state:
28
- st.session_state.formato = "Jung's_Avatar" # Changed from previous format name
29
- if 'nivel_conciencia' not in st.session_state:
30
- # Usar el primer nivel del diccionario como valor predeterminado
31
- first_key = list(CONSCIOUSNESS_LEVELS.keys())[0]
32
- st.session_state.nivel_conciencia = first_key.replace("_", " ")
33
-
34
- # Funci贸n para generar el perfil de cliente ideal
35
- @st.cache_resource
36
- def get_model(temperature):
37
- generation_config = {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
  "temperature": temperature,
 
 
 
39
  }
40
- return genai.GenerativeModel('gemini-2.0-flash', generation_config=generation_config)
41
 
42
- # REMOVED: The format selection dropdown that was here
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
 
44
- def generate_buyer_persona(product, skills, target_audience, temperature, consciousness_level="Ninguno", format_type="Jung's_Avatar"):
45
- if not product or not skills:
46
- return "Por favor, completa los campos de producto y habilidades."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47
 
48
- try:
49
- model = get_model(temperature)
50
- instruction = create_instruction(
51
- format_type=format_type,
52
- product_service=product,
53
- skills=skills,
54
- consciousness_level=consciousness_level,
55
- target_audience=target_audience,
56
- gender=None # Adding gender parameter with None as default
57
- )
58
-
59
- # A帽adir instrucci贸n expl铆cita para respuesta en espa帽ol
60
- instruction += "\n\nIMPORTANTE: La respuesta debe estar completamente en espa帽ol."
61
-
62
- response = model.generate_content([instruction], generation_config={"temperature": temperature})
63
- return response.parts[0].text if response and response.parts else "Error generando el perfil de cliente ideal."
64
- except Exception as e:
65
- return f"Error al generar el perfil: {str(e)}"
66
-
67
- # Modificar la funci贸n update_profile para que no use spinner
68
- def update_profile():
69
- # Solo actualizar la variable de sesi贸n
70
- st.session_state.submitted = True
71
-
72
- # Leer el contenido del archivo manual.md si existe
73
- try:
74
- with open("manual.md", "r", encoding="utf-8") as file:
75
- manual_content = file.read()
76
- # Mostrar el contenido del manual en el sidebar
77
- st.sidebar.markdown(manual_content)
78
- except FileNotFoundError:
79
- st.sidebar.warning("Manual not found. Please create a manual.md file.")
80
- except Exception as e:
81
- st.sidebar.error(f"Error loading manual: {str(e)}")
82
-
83
- # Ocultar elementos de la interfaz
84
- st.markdown(styles["main_layout"], unsafe_allow_html=True)
85
-
86
- # Centrar el t铆tulo y el subt铆tulo
87
- st.markdown("<h1 style='text-align: center;'>Generador de Perfil de Cliente Ideal</h1>", unsafe_allow_html=True)
88
- st.markdown("<h4 style='text-align: center;'>Crea un perfil detallado de tu cliente ideal basado en tu producto y habilidades.</h4>", unsafe_allow_html=True)
89
-
90
- # A帽adir CSS personalizado para el bot贸n
91
- st.markdown(styles["button"], unsafe_allow_html=True)
92
- # A帽adir CSS personalizado para el bot贸n de descarga
93
- st.markdown(styles["download_button"], unsafe_allow_html=True)
94
-
95
- # Crear columnas
96
- col1, col2 = st.columns([1, 2])
97
-
98
- # Columna de entrada
99
- with col1:
100
- product = st.text_area("驴Qu茅 producto o servicio ofreces?",
101
- value=st.session_state.producto,
102
- placeholder="Ejemplo: Curso de Ingl茅s",
103
- key="producto_input",
104
- height=70)
105
- st.session_state.producto = product
106
 
107
- skills = st.text_area("驴Cu谩les son tus habilidades principales?",
108
- value=st.session_state.habilidades,
109
- placeholder="Ejemplo: Ense帽anza, comunicaci贸n, dise帽o de contenidos",
110
- key="habilidades_input",
111
- height=70)
112
- st.session_state.habilidades = skills
 
 
 
 
113
 
114
- # Bot贸n para generar - Movido arriba del acorde贸n
115
- submit = st.button("CREAR MI CLIENTE IDEAL SO脩ADO 鉃も灓", on_click=update_profile)
 
 
 
116
 
117
- # Crear un acorde贸n para las opciones de personalizaci贸n
118
- with st.expander("Personaliza Tu Cliente Ideal So帽ado"):
119
- # Nuevo campo para p煤blico objetivo
120
- if 'publico_objetivo' not in st.session_state:
121
- st.session_state.publico_objetivo = ""
122
-
123
- target_audience = st.text_area("驴Cu谩l es tu p煤blico objetivo? (opcional)",
124
- value=st.session_state.publico_objetivo,
125
- placeholder="Ejemplo: Profesionales entre 25-40 a帽os interesados en desarrollo personal",
126
- key="publico_objetivo_input",
127
- height=70)
128
- st.session_state.publico_objetivo = target_audience
129
-
130
- # Selector de formato
131
- from format.format import buyer_persona_formats
132
-
133
- # Usar directamente las claves del diccionario sin filtrar
134
- format_type = st.selectbox(
135
- "Formato del perfil",
136
- options=list(buyer_persona_formats.keys()),
137
- format_func=lambda x: x.capitalize(),
138
- index=list(buyer_persona_formats.keys()).index(st.session_state.formato) if st.session_state.formato in buyer_persona_formats else 0,
139
- help="Selecciona el formato en el que se presentar谩 el perfil del cliente ideal"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
140
  )
141
- st.session_state.formato = format_type
142
 
143
- # Nivel de creatividad con slider
144
- temperature = st.slider("Nivel de creatividad",
145
- min_value=0.0,
146
- max_value=2.0,
147
- value=st.session_state.creatividad,
148
- step=0.1,
149
- key="creatividad_slider")
150
- st.session_state.creatividad = temperature
151
 
152
- # Selector de nivel de conciencia
153
- consciousness_options = []
154
- for i, key in enumerate(CONSCIOUSNESS_LEVELS.keys(), 1):
155
- # Replace underscores with spaces in the key
156
- display_name = key.replace("_", " ")
157
- consciousness_options.append(f"Nivel {i} - {display_name}")
158
 
159
- nivel_conciencia_display = st.selectbox(
160
- "Nivel de conciencia del cliente ideal",
161
- consciousness_options,
162
- index=0,
163
- help="Selecciona el nivel de conciencia en el que se encuentra tu cliente ideal"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
164
  )
165
 
166
- # Extract the original key from the display name
167
- level_number = nivel_conciencia_display.split(" - ")[0].replace("Nivel ", "")
168
- original_key = list(CONSCIOUSNESS_LEVELS.keys())[int(level_number) - 1]
169
- nivel_conciencia = original_key.replace("_", " ")
170
 
171
- # Get the description from the CONSCIOUSNESS_LEVELS dictionary
172
- if original_key in CONSCIOUSNESS_LEVELS:
173
- nivel_info = CONSCIOUSNESS_LEVELS[original_key]["estado_mental"]
174
- st.info(f"**{nivel_conciencia}**: {nivel_info}")
175
-
176
- st.session_state.nivel_conciencia = nivel_conciencia
177
-
178
- # Columna de resultados
179
- with col2:
180
- # Verificar si se ha enviado el formulario
181
- if 'submitted' in st.session_state and st.session_state.submitted:
182
- if st.session_state.producto and st.session_state.habilidades:
183
- with st.spinner("Creando tu Cliente Ideal So帽ado..."):
184
- # Generar el perfil del cliente
185
- buyer_persona = generate_buyer_persona(
186
- st.session_state.producto,
187
- st.session_state.habilidades,
188
- st.session_state.publico_objetivo,
189
- st.session_state.creatividad,
190
- st.session_state.nivel_conciencia,
191
- st.session_state.formato
192
- )
193
-
194
- # Mejorar la limpieza del perfil para eliminar estructuras JSON/diccionario
195
- if isinstance(buyer_persona, str):
196
- import re
197
- import json
198
-
199
- # Eliminar marcadores de c贸digo markdown como ```json, ```python, etc.
200
- buyer_persona = re.sub(r'```[a-z]*\n', '', buyer_persona)
201
- buyer_persona = re.sub(r'```', '', buyer_persona)
202
-
203
- # Intentar detectar y limpiar formato JSON
204
- if '{' in buyer_persona and '}' in buyer_persona:
205
- # Intentar extraer solo el contenido textual, eliminando estructuras JSON
206
- # Primero, intentar encontrar el JSON completo
207
- json_pattern = r'(\{.*?\})'
208
- json_matches = re.findall(json_pattern, buyer_persona, re.DOTALL)
209
-
210
- if json_matches:
211
- for json_match in json_matches:
212
- # Intentar extraer el contenido real del JSON
213
- try:
214
- # Reemplazar el JSON con una cadena vac铆a
215
- buyer_persona = buyer_persona.replace(json_match, '')
216
- except:
217
- pass
218
-
219
- # Limpiar l铆neas que parecen ser parte de un diccionario
220
- lines = buyer_persona.split('\n')
221
- cleaned_lines = []
222
- for line in lines:
223
- # Omitir l铆neas que parecen ser claves de diccionario
224
- if not re.match(r'^\s*["\']?[a-zA-Z_]+["\']?\s*:', line):
225
- cleaned_lines.append(line)
226
-
227
- buyer_persona = '\n'.join(cleaned_lines)
228
-
229
- # Eliminar llaves sueltas y corchetes
230
- buyer_persona = re.sub(r'[{}[\]]', '', buyer_persona)
231
-
232
- # Eliminar comillas y dos puntos que parecen ser de un diccionario
233
- buyer_persona = re.sub(r'["\']\s*:\s*["\']', '', buyer_persona)
234
-
235
- # Eliminar "template", "description", "example" y otras palabras clave comunes en el formato
236
- keywords = ["template", "description", "example", "Nivel de conciencia"]
237
- for keyword in keywords:
238
- buyer_persona = re.sub(rf'["\']?{keyword}["\']?\s*:\s*["\']?', '', buyer_persona)
239
-
240
- # Eliminar comillas sueltas
241
- buyer_persona = re.sub(r'^\s*["\']|["\']$', '', buyer_persona)
242
-
243
- # Normalizar espacios en blanco y sangr铆as
244
- lines = buyer_persona.split('\n')
245
- cleaned_lines = []
246
- for line in lines:
247
- # Eliminar sangr铆as excesivas pero mantener estructura b谩sica
248
- cleaned_line = line.strip()
249
- if cleaned_line: # Solo agregar l铆neas no vac铆as
250
- cleaned_lines.append(cleaned_line)
251
-
252
- # Unir las l铆neas con saltos de l铆nea adecuados
253
- buyer_persona = '\n'.join(cleaned_lines)
254
-
255
- # Eliminar espacios en blanco adicionales
256
- buyer_persona = re.sub(r'\n\s*\n', '\n\n', buyer_persona)
257
- buyer_persona = buyer_persona.strip()
258
-
259
- # Al inicio del archivo, junto con las otras inicializaciones
260
- if 'perfil_cliente_plain' not in st.session_state:
261
- st.session_state.perfil_cliente_plain = None
262
-
263
- # Guardar versi贸n sin formato antes de aplicar estilos HTML
264
- st.session_state.perfil_cliente_plain = buyer_persona
265
-
266
- # Convertir los asteriscos de formato Markdown a HTML para una correcta visualizaci贸n
267
- # Convertir **texto** a <strong>texto</strong>
268
- buyer_persona = re.sub(r'\*\*(.*?)\*\*', r'<strong>\1</strong>', buyer_persona)
269
- # Convertir *texto* a <em>texto</em>
270
- buyer_persona = re.sub(r'\*(.*?)\*', r'<em>\1</em>', buyer_persona)
271
-
272
- # Mejorar el formato HTML para una mejor visualizaci贸n
273
- buyer_persona = """
274
- <div style="line-height: 1.3; text-align: left;">
275
- {}
276
- </div>
277
- """.format(buyer_persona.replace('\n', '<br><br>'))
278
-
279
- # Guardar en session_state
280
- st.session_state.perfil_cliente = buyer_persona
281
- # Resetear el estado de env铆o
282
- st.session_state.submitted = False
283
-
284
- # Mostrar resultados
285
- if not isinstance(st.session_state.perfil_cliente, str):
286
- st.error("Error al generar el perfil de cliente ideal")
287
- else:
288
- # Crear un contenedor para el resultado
289
- result_container = st.container()
290
-
291
- # Aplicar estilos del contenedor
292
- st.markdown(styles["results_container"], unsafe_allow_html=True)
293
-
294
- with result_container:
295
- # Mostrar el t铆tulo
296
- st.markdown("<div class='results-title'>Tu Cliente Ideal</div>", unsafe_allow_html=True)
297
-
298
- # Mostrar el contenido como HTML para asegurar el formato correcto
299
- st.markdown(st.session_state.perfil_cliente, unsafe_allow_html=True)
300
-
301
- # Opci贸n para descargar
302
- # En la secci贸n de descarga anterior, asegurarse de usar la versi贸n plain si existe
303
- st.download_button(
304
- label="DESCARGAR MI CLIENTE SO脩ADO 鉃も灓",
305
- data=st.session_state.perfil_cliente_plain if st.session_state.perfil_cliente_plain else st.session_state.perfil_cliente,
306
- file_name="cliente_ideal.txt",
307
- mime="text/plain"
308
- )
309
  else:
310
- st.warning("Por favor, completa los campos de producto y habilidades antes de generar el perfil.")
311
- # Mostrar resultados anteriores si existen
312
- elif st.session_state.perfil_cliente:
313
- # Crear un contenedor para el resultado
314
- result_container = st.container()
315
-
316
- # Aplicar estilos del contenedor
317
- st.markdown(styles["results_container"], unsafe_allow_html=True)
318
-
319
- with result_container:
320
- # Mostrar el t铆tulo
321
- st.markdown("<div class='results-title'>Tu Cliente Ideal</div>", unsafe_allow_html=True)
322
-
323
- # Mostrar el contenido como HTML para asegurar el formato correcto
324
- st.markdown(st.session_state.perfil_cliente, unsafe_allow_html=True)
325
-
326
- # Opci贸n para descargar
327
- st.download_button(
328
- label="DESCARGAR MI CLIENTE SO脩ADO 鉃も灓",
329
- data=st.session_state.perfil_cliente,
330
- file_name="cliente_ideal.txt",
331
- mime="text/plain"
332
- )
 
 
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 angles import angles
9
+ from formulas.webinar_formulas import webinar_formulas
10
+ from formulas.webinar_name_formulas import webinar_name_formulas
11
+ from formulas.angles_webinar_names import angles_webinar_names
12
 
13
  # Cargar las variables de entorno
14
  load_dotenv()
 
16
  # Configurar la API de Google
17
  genai.configure(api_key=os.getenv("GOOGLE_API_KEY"))
18
 
19
+ # Create a reusable function for the UI input section
20
+ def create_input_section(col, audience_key, product_key, formula_options, formula_key):
21
+ audience = col.text_input("驴Qui茅n es tu p煤blico objetivo?", placeholder="Ejemplo: Emprendedores digitales", key=audience_key)
22
+ product = col.text_input("驴Sobre qu茅 tema es tu webinar?", placeholder="Ejemplo: Marketing en redes sociales", key=product_key)
23
+
24
+ selected_formula_key = col.selectbox(
25
+ "Selecciona un framework de webinar" if "script" in audience_key else "Selecciona una f贸rmula para tus nombres de webinar",
26
+ options=list(formula_options.keys()),
27
+ key=formula_key
28
+ )
29
+
30
+ return audience, product, selected_formula_key
31
+
32
+ # Create a reusable function for generation and display
33
+ def generate_and_display(col, generator_func, audience, product, temperature, selected_formula, **kwargs):
34
+ if validate_inputs(audience, product):
35
+ try:
36
+ with col:
37
+ with st.spinner("Generando contenido...", show_time=True):
38
+ # Extract only the parameters that the generator function accepts
39
+ if generator_func.__name__ == "generate_webinar_script":
40
+ generated_content = generator_func(
41
+ audience=audience,
42
+ topic=product,
43
+ temperature=temperature,
44
+ selected_formula=selected_formula
45
+ )
46
+ else:
47
+ generated_content = generator_func(
48
+ audience=audience,
49
+ topic=product,
50
+ temperature=temperature,
51
+ selected_formula=selected_formula,
52
+ **kwargs
53
+ )
54
+
55
+ # For webinar scripts, add download buttons
56
+ if "script" in kwargs.get("content_type", ""):
57
+ timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
58
+ st.download_button(
59
+ label="DESCARGAR GUION DE MI WEBINAR",
60
+ data=generated_content,
61
+ file_name=f"guion_webinar_{timestamp}.md",
62
+ mime="text/markdown",
63
+ key="download_top"
64
+ )
65
+
66
+ st.subheader("Tu " + ("gui贸n de webinar:" if "script" in kwargs.get("content_type", "") else "nombres de webinar:"))
67
+ st.markdown(generated_content)
68
+
69
+ # Add bottom download button for scripts
70
+ if "script" in kwargs.get("content_type", ""):
71
+ st.download_button(
72
+ label="DESCARGAR GUION DE MI WEBINAR",
73
+ data=generated_content,
74
+ file_name=f"guion_webinar_{timestamp}.md",
75
+ mime="text/markdown",
76
+ key="download_bottom"
77
+ )
78
+ except ValueError as e:
79
+ col.error(f"Error: {str(e)}")
80
+ else:
81
+ col.error("Por favor, proporciona el p煤blico objetivo y el tema del webinar.")
82
+
83
+ # Funci贸n para crear la configuraci贸n del modelo (evita duplicaci贸n)
84
+ def create_model_config(temperature):
85
+ return {
86
  "temperature": temperature,
87
+ "top_p": 0.65,
88
+ "top_k": 360,
89
+ "max_output_tokens": 8196,
90
  }
 
91
 
92
+ # Funci贸n para inicializar el modelo
93
+ def initialize_model(temperature):
94
+ config = create_model_config(temperature)
95
+ return genai.GenerativeModel(
96
+ model_name="gemini-2.0-flash",
97
+ generation_config=config,
98
+ )
99
+
100
+ # Refactored model interaction function to reduce duplication
101
+ def generate_content(prompt_instructions, temperature):
102
+ model = initialize_model(temperature)
103
+ chat_session = model.start_chat(
104
+ history=[
105
+ {
106
+ "role": "user",
107
+ "parts": [prompt_instructions],
108
+ },
109
+ ]
110
+ )
111
+ response = chat_session.send_message("Generate the content following exactly the provided instructions. All content must be in Spanish.")
112
+ return response.text
113
 
114
+ # Funci贸n para generar nombres de webinars
115
+ def generate_webinar_names(number_of_names, target_audience, product, temperature, selected_formula, selected_angle=None):
116
+ # Incluir las instrucciones del sistema en el prompt principal
117
+ system_prompt = """You are a world-class copywriter, with expertise in crafting compelling webinar titles that immediately capture the audience's attention and drive registrations.
118
+
119
+ FORMAT RULES:
120
+ - Each webinar name must start with number and period
121
+ - One webinar name per line
122
+ - No explanations or categories
123
+ - Add a line break between each name
124
+ - Avoid unnecessary : symbols
125
+ - Each webinar name must be a complete and intriguing title
126
+ - WRITE ALL WEBINAR NAMES IN SPANISH
127
+
128
+ FORMAT EXAMPLE:
129
+ 1. Nombre del Webinar 1.
130
+
131
+ 2. Nombre del Webinar 2.
132
+
133
+ 3. Nombre del Webinar 3.
134
+
135
+ 4. Nombre del Webinar 4.
136
+
137
+ 5. Nombre del Webinar 5.
138
+
139
+ IMPORTANT:
140
+ - Each webinar name must be unique and memorable
141
+ - Avoid clich茅s and generalities
142
+ - Maintain an intriguing but credible tone
143
+ - Adapt speaking language from the audience
144
+ - Focus on transformative benefits
145
+ - Follow the selected formula structure
146
+ - WRITE ALL WEBINAR NAMES IN SPANISH"""
147
+
148
+ # Iniciar el prompt con las instrucciones del sistema
149
+ webinar_names_instruction = f"{system_prompt}\n\n"
150
 
151
+ # A帽adir instrucciones de 谩ngulo solo si no es "NINGUNO" y se proporcion贸 un 谩ngulo
152
+ if selected_angle and selected_angle != "NINGUNO":
153
+ webinar_names_instruction += f"""
154
+ MAIN ANGLE: {selected_angle}
155
+ SPECIFIC ANGLE INSTRUCTIONS:
156
+ {angles_webinar_names[selected_angle]["instruction"]}
157
+
158
+ IMPORTANT: The {selected_angle} angle should be applied as a "style layer" over the formula structure:
159
+ 1. Keep the base structure of the formula intact
160
+ 2. Apply the tone and style of the {selected_angle} angle
161
+ 3. Ensure that each element of the formula reflects the angle
162
+ 4. The angle affects "how" it is said, not "what" is said
163
+
164
+ SUCCESSFUL EXAMPLES OF THE {selected_angle} ANGLE:
165
+ """
166
+ for example in angles_webinar_names[selected_angle]["examples"]:
167
+ webinar_names_instruction += f"- {example}\n"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
168
 
169
+ # Instrucciones espec铆ficas para la tarea
170
+ webinar_names_instruction += (
171
+ f"\nYour task is to create {number_of_names} irresistible webinar names for {target_audience} "
172
+ f"that instantly capture attention and generate registrations for a webinar about {product}. "
173
+ f"Focus on awakening genuine interest and communicating the value they will get by registering."
174
+ f"\n\n"
175
+ f"IMPORTANT: Carefully study these examples of the selected formula. "
176
+ f"Each example represents the style and structure to follow"
177
+ f":\n\n"
178
+ )
179
 
180
+ # Agregar ejemplos aleatorios de la f贸rmula (keeping examples in Spanish)
181
+ random_examples = random.sample(selected_formula['examples'], min(5, len(selected_formula['examples'])))
182
+ webinar_names_instruction += "EXAMPLES OF THE FORMULA TO FOLLOW:\n"
183
+ for i, example in enumerate(random_examples, 1):
184
+ webinar_names_instruction += f"{i}. {example}\n"
185
 
186
+ # Instrucciones espec铆ficas (translated to English)
187
+ webinar_names_instruction += "\nSPECIFIC INSTRUCTIONS:\n"
188
+ webinar_names_instruction += "1. Maintain the same structure and length as the previous examples\n"
189
+ webinar_names_instruction += "2. Use the same tone and writing style\n"
190
+ webinar_names_instruction += "3. Replicate the phrase construction patterns\n"
191
+ webinar_names_instruction += "4. Preserve the level of specificity and detail\n"
192
+ webinar_names_instruction += f"5. Adapt the content for {target_audience} while maintaining the essence of the examples\n\n"
193
+ webinar_names_instruction += f"FORMULA TO FOLLOW:\n{selected_formula['description']}\n\n"
194
+ webinar_names_instruction += f"""
195
+ GENERATE NOW:
196
+ Create {number_of_names} webinar names that faithfully follow the style and structure of the examples shown.
197
+ """
198
+
199
+ # Enviar el mensaje al modelo
200
+ # Use the common generate_content function
201
+ return generate_content(webinar_names_instruction, temperature)
202
+
203
+ def generate_webinar_script(audience, topic, temperature, selected_formula):
204
+ model = initialize_model(temperature)
205
+
206
+ # Incluir las instrucciones del sistema en el prompt principal
207
+ 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.
208
+
209
+ THE EXPERT TEAM:
210
+
211
+ 1. MASTER WEBINAR STRATEGIST:
212
+ - Expert in webinar frameworks and conversion strategies
213
+ - Trained in the Perfect Webinar methodology by Russell Brunson
214
+ - Ensures the script follows the selected framework structure precisely
215
+ - Focuses on strategic placement of key conversion elements
216
+
217
+ 2. ELITE DIRECT RESPONSE COPYWRITER:
218
+ - Trained by Gary Halbert, Gary Bencivenga, and David Ogilvy
219
+ - Creates compelling hooks, stories, and persuasive elements
220
+ - Crafts irresistible calls to action that drive conversions
221
+ - Ensures the language resonates with the target audience
222
+
223
+ 3. AUDIENCE PSYCHOLOGY SPECIALIST:
224
+ - Expert in understanding audience motivations and objections
225
+ - Creates content that builds genuine connection and trust
226
+ - Identifies and addresses hidden fears and desires
227
+ - Ensures the content feels personal and relevant
228
+
229
+ 4. STORYTELLING MASTER:
230
+ - Creates compelling narratives that illustrate key points
231
+ - Develops relatable examples and case studies
232
+ - Ensures stories support the transformation being offered
233
+ - Makes complex concepts accessible through narrative
234
+
235
+ 5. WEBINAR ENGAGEMENT EXPERT:
236
+ - Specializes in maintaining audience attention throughout
237
+ - Creates interactive elements and engagement hooks
238
+ - Develops compelling transitions between sections
239
+ - Ensures the webinar flows naturally and keeps interest high
240
+
241
+ AUDIENCE UNDERSTANDING:
242
+ You understand how real people interact with webinar content:
243
+ - They quickly lose interest if the content feels generic or corporate
244
+ - They only stay engaged when the content feels personal and sparks genuine curiosity
245
+ - They respond to messages that seem delivered by a real person, not a corporation
246
+ - They engage with content that hooks them from the first line and maintains interest throughout
247
+
248
+ FORMAT RULES:
249
+ - Create a complete webinar script with clear sections and subsections
250
+ - Include specific talking points for each section
251
+ - Write in a conversational, engaging tone
252
+ - Include persuasive elements and calls to action
253
+ - Follow the selected webinar framework structure exactly
254
+ - WRITE THE ENTIRE SCRIPT IN SPANISH
255
+ - DO NOT include any introductory text or explanations about the script
256
+ - Start directly with the webinar content
257
+
258
+ IMPORTANT:
259
+ - The script must be comprehensive and ready to use
260
+ - Include specific examples and stories relevant to the topic
261
+ - Maintain a persuasive but authentic tone
262
+ - Adapt language to match the target audience
263
+ - Focus on transformative benefits and clear value proposition
264
+ - Follow the selected formula structure precisely
265
+ - WRITE THE ENTIRE SCRIPT IN SPANISH
266
+ - DO NOT include phrases like "Aqu铆 tienes un guion completo" or any other meta-commentary
267
+
268
+ COLLABORATIVE PROCESS:
269
+ As a team of experts, you will:
270
+
271
+ 1. Analyze the framework '{selected_formula['description']}' to understand its core principles
272
+ 2. Identify how to best adapt this framework for {audience} learning about {topic}
273
+ 3. Determine the most effective storytelling opportunities within the framework
274
+ 4. Create persuasive language that resonates with {audience}
275
+ 5. Ensure the script maintains engagement throughout
276
+ 6. Adapt the content to be accessible to beginners while still valuable to experienced individuals
277
+ 7. Follow the exact structure provided in the framework
278
+
279
+ Each expert will contribute their specialized knowledge to create a cohesive, compelling script that:
280
+ - Follows the exact structure of the selected framework
281
+ - Engages the audience from start to finish
282
+ - Addresses audience pain points and desires
283
+ - Presents {topic} in a clear, compelling way
284
+ - Drives conversions through strategic calls to action"""
285
+
286
+ # Iniciar el prompt con las instrucciones del sistema
287
+ webinar_script_instruction = f"{system_prompt}\n\n"
288
+
289
+ # Instrucciones espec铆ficas para la tarea
290
+ webinar_script_instruction += (
291
+ f"\nYour task is to create a complete webinar script IN SPANISH for {audience} "
292
+ f"about {topic} that is persuasive and converts the audience into customers. "
293
+ f"The script must follow exactly the structure of the framework '{selected_formula['description']}' "
294
+ f"and must include all the necessary elements for a successful webinar."
295
+ f"\n\n"
296
+ )
297
+
298
+ # Estructura del webinar
299
+ webinar_script_instruction += "WEBINAR STRUCTURE TO FOLLOW:\n"
300
+ for i, step in enumerate(selected_formula['structure'], 1):
301
+ webinar_script_instruction += f"{i}. {step}\n"
302
+
303
+ # Ejemplos de webinars exitosos
304
+ webinar_script_instruction += "\n\nEXAMPLES OF SUCCESSFUL WEBINARS WITH THIS STRUCTURE:\n"
305
+ for i, example in enumerate(selected_formula['examples'], 1):
306
+ webinar_script_instruction += f"{i}. {example}\n"
307
+
308
+ # Instrucciones espec铆ficas
309
+ webinar_script_instruction += f"""
310
+ SPECIFIC INSTRUCTIONS:
311
+ 1. Create a complete script that follows exactly the provided structure
312
+ 2. Include persuasive elements and clear calls to action
313
+ 3. Adapt the language and examples specifically for {audience}
314
+ 4. Focus on the transformative benefits of {topic}
315
+ 5. Include relevant stories and examples that reinforce your points
316
+ 6. Use a conversational but professional tone
317
+ 7. Make sure each section fulfills its specific purpose in the framework
318
+ 8. IMPORTANT: Write the entire script in Spanish
319
+
320
+ GENERATE NOW:
321
+ Create a complete webinar script following faithfully the structure of the selected framework.
322
+ """
323
+
324
+ # Enviar el mensaje al modelo
325
+ chat_session = model.start_chat(
326
+ history=[
327
+ {
328
+ "role": "user",
329
+ "parts": [webinar_script_instruction],
330
+ },
331
+ ]
332
+ )
333
+ 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).")
334
+
335
+ return response.text
336
+
337
+ # Funci贸n para validar entradas (evita duplicaci贸n)
338
+ def validate_inputs(audience, product):
339
+ has_audience = audience.strip() != ""
340
+ has_product = product.strip() != ""
341
+ return has_audience and has_product
342
+
343
+ # Update the load_css function comment to be more descriptive
344
+ def load_css():
345
+ css_path = "styles/styles.css"
346
+ if os.path.exists(css_path):
347
+ try:
348
+ with open(css_path, "r") as f:
349
+ st.markdown(f"<style>{f.read()}</style>", unsafe_allow_html=True)
350
+ except Exception as e:
351
+ st.warning(f"Error al cargar el archivo CSS: {str(e)}")
352
+ else:
353
+ st.warning(f"No se encontr贸 el archivo CSS en {css_path}")
354
+
355
+ # Create a reusable function for custom buttons
356
+ # At the top of your file, define the styles dictionary
357
+ styles = {
358
+ "button": """
359
+ <style>
360
+ div.stButton > button:first-child {
361
+ background-color: #FF4B4B;
362
+ color: white;
363
+ font-weight: bold;
364
+ padding: 0.5rem 1rem;
365
+ border-radius: 0.5rem;
366
+ border: none;
367
+ transition: all 0.3s;
368
+ }
369
+ div.stButton > button:hover {
370
+ background-color: #FF2525;
371
+ transform: translateY(-2px);
372
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
373
+ }
374
+ </style>
375
+ """,
376
+ "download_button": """
377
+ <style>
378
+ div.stDownloadButton > button:first-child {
379
+ background-color: #4CAF50;
380
+ color: white;
381
+ font-weight: bold;
382
+ padding: 0.5rem 1rem;
383
+ border-radius: 0.5rem;
384
+ border: none;
385
+ transition: all 0.3s;
386
+ }
387
+ div.stDownloadButton > button:hover {
388
+ background-color: #45a049;
389
+ transform: translateY(-2px);
390
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
391
+ }
392
+ </style>
393
+ """
394
+ }
395
+
396
+ # Then modify your create_custom_button function to be much simpler:
397
+ def create_custom_button(label, button_key):
398
+ # Apply the button style once at the beginning of your app
399
+ # (You can move this to your main app code if you prefer)
400
+ st.markdown(styles["button"], unsafe_allow_html=True)
401
+
402
+ # Just return a regular Streamlit button
403
+ return st.button(label, key=button_key)
404
+
405
+ # Modify the page config section to include the CSS loading
406
+ st.set_page_config(page_title="Perfect Webinar Framework", layout="wide")
407
+ load_css()
408
+
409
+ # Leer el contenido del archivo manual.md
410
+ with open("manual.md", "r", encoding="utf-8") as file:
411
+ manual_content = file.read()
412
+
413
+ # Mostrar el contenido del manual en el sidebar
414
+ st.sidebar.markdown(manual_content)
415
+
416
+ # Crear pesta帽as para la interfaz
417
+ tab1, tab2 = st.tabs(["Guiones de Webinar", "Nombres de Webinar"])
418
+
419
+ # Primera pesta帽a - Generador de Guiones de Webinar
420
+ with tab1:
421
+ tab1.subheader("Script Webinar")
422
+
423
+ # Crear columnas para la interfaz
424
+ col1, col2 = tab1.columns([1, 2])
425
+
426
+ # Columna de entrada usando la funci贸n reutilizable
427
+ with col1:
428
+ webinar_script_audience, webinar_script_product, selected_webinar_formula_key = create_input_section(
429
+ col1,
430
+ "webinar_script_audience",
431
+ "webinar_script_product",
432
+ webinar_formulas,
433
+ "webinar_formula"
434
  )
 
435
 
436
+ # Usar la funci贸n reutilizable para crear el bot贸n personalizado
437
+ submit_webinar_script = create_custom_button("Generar Gui贸n de Webinar", "generate_webinar_script")
 
 
 
 
 
 
438
 
439
+ # Opciones avanzadas
440
+ with st.expander("Personaliza tu gui贸n de webinar"):
441
+ webinar_script_temperature = st.slider("Creatividad", min_value=0.0, max_value=2.0, value=1.0, step=0.1, key="webinar_script_temp")
 
 
 
442
 
443
+ selected_webinar_formula = webinar_formulas[selected_webinar_formula_key]
444
+
445
+ # Generar y mostrar el gui贸n usando la funci贸n reutilizable
446
+ if submit_webinar_script:
447
+ generate_and_display(
448
+ col2,
449
+ generate_webinar_script,
450
+ webinar_script_audience,
451
+ webinar_script_product,
452
+ webinar_script_temperature,
453
+ selected_webinar_formula,
454
+ content_type="script"
455
+ )
456
+
457
+ # Segunda pesta帽a - Generador de Nombres de Webinar
458
+ with tab2:
459
+ tab2.subheader("Nombres de Webinar")
460
+
461
+ # Crear columnas para la interfaz
462
+ col1, col2 = tab2.columns([1, 2])
463
+
464
+ # Columna de entrada usando la funci贸n reutilizable
465
+ with col1:
466
+ webinar_name_audience, webinar_name_product, selected_webinar_name_formula_key = create_input_section(
467
+ col1,
468
+ "webinar_name_audience",
469
+ "webinar_name_product",
470
+ webinar_name_formulas,
471
+ "webinar_name_formula"
472
  )
473
 
474
+ number_of_names = st.selectbox("N煤mero de nombres", options=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], index=4, key="number_of_names")
 
 
 
475
 
476
+ # Usar la funci贸n reutilizable para crear el bot贸n personalizado
477
+ submit_webinar_names = create_custom_button("Generar Nombres de Webinar", "generate_webinar_names")
478
+
479
+ # Opciones avanzadas
480
+ with st.expander("Personaliza tus nombres de webinar"):
481
+ webinar_name_temperature = st.slider("Creatividad", min_value=0.0, max_value=2.0, value=1.0, step=0.1, key="webinar_name_temp")
482
+
483
+ # Configurar opciones de 谩ngulo
484
+ angle_keys = ["NINGUNO"] + sorted([key for key in angles_webinar_names.keys() if key != "NINGUNO"])
485
+ selected_angle = st.selectbox(
486
+ "Selecciona el 谩ngulo para tus nombres",
487
+ options=angle_keys,
488
+ key="webinar_name_angle"
489
+ )
490
+
491
+ selected_webinar_name_formula = webinar_name_formulas[selected_webinar_name_formula_key]
492
+
493
+ # Generar y mostrar los nombres usando la funci贸n reutilizable
494
+ if submit_webinar_names:
495
+ generate_and_display(
496
+ col2,
497
+ generate_webinar_names,
498
+ webinar_name_audience,
499
+ webinar_name_product,
500
+ webinar_name_temperature,
501
+ selected_webinar_name_formula,
502
+ content_type="names",
503
+ number_of_names=number_of_names,
504
+ selected_angle=selected_angle if selected_angle != "NINGUNO" else None
505
+ )
506
+ if validate_inputs(webinar_name_audience, webinar_name_product):
507
+ try:
508
+ with col2:
509
+ with st.spinner("Generando tus nombres de webinar...", show_time=True):
510
+ generated_webinar_names = generate_webinar_names(
511
+ number_of_names,
512
+ webinar_name_audience,
513
+ webinar_name_product,
514
+ webinar_name_temperature,
515
+ selected_webinar_name_formula,
516
+ selected_angle if selected_angle != "NINGUNO" else None
517
+ )
518
+ st.subheader("Tus nombres de webinar:")
519
+ st.markdown(generated_webinar_names)
520
+ except ValueError as e:
521
+ col2.error(f"Error: {str(e)}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
522
  else:
523
+ col2.error("Por favor, proporciona el p煤blico objetivo y el tema del webinar.")
524
+