JeCabrera commited on
Commit
9d3f971
·
verified ·
1 Parent(s): 0cdebe7

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +306 -497
app.py CHANGED
@@ -1,13 +1,12 @@
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()
@@ -15,509 +14,319 @@ load_dotenv()
15
  # Configurar la API de Google
16
  genai.configure(api_key=os.getenv("GOOGLE_API_KEY"))
17
 
18
- # Create a reusable function for the UI input section
19
- def create_input_section(col, audience_key, product_key, formula_options, formula_key):
20
- audience = col.text_input("¿Quién es tu público objetivo?", placeholder="Ejemplo: Emprendedores digitales", key=audience_key)
21
- product = col.text_input("¿Sobre qué tema es tu webinar?", placeholder="Ejemplo: Marketing en redes sociales", key=product_key)
22
-
23
- selected_formula_key = col.selectbox(
24
- "Selecciona un framework de webinar" if "script" in audience_key else "Selecciona una fórmula para tus nombres de webinar",
25
- options=list(formula_options.keys()),
26
- key=formula_key
27
- )
28
-
29
- return audience, product, selected_formula_key
30
-
31
- # Create a reusable function for generation and display
32
- def generate_and_display(col, generator_func, audience, product, temperature, selected_formula, **kwargs):
33
- if validate_inputs(audience, product):
34
- try:
35
- with col:
36
- with st.spinner("Generando contenido...", show_time=True):
37
- # Extract only the parameters that the generator function accepts
38
- if generator_func.__name__ == "generate_webinar_script":
39
- generated_content = generator_func(
40
- audience=audience,
41
- topic=product,
42
- temperature=temperature,
43
- selected_formula=selected_formula
44
- )
45
- else:
46
- generated_content = generator_func(
47
- audience=audience,
48
- topic=product,
49
- temperature=temperature,
50
- selected_formula=selected_formula,
51
- **kwargs
52
- )
53
-
54
- # For webinar scripts, add download buttons
55
- if "script" in kwargs.get("content_type", ""):
56
- timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
57
- st.download_button(
58
- label="DESCARGAR GUION DE MI WEBINAR",
59
- data=generated_content,
60
- file_name=f"guion_webinar_{timestamp}.md",
61
- mime="text/markdown",
62
- key="download_top"
63
- )
64
-
65
- st.subheader("Tu " + ("guión de webinar:" if "script" in kwargs.get("content_type", "") else "nombres de webinar:"))
66
- st.markdown(generated_content)
67
-
68
- # Add bottom download button for scripts
69
- if "script" in kwargs.get("content_type", ""):
70
- st.download_button(
71
- label="DESCARGAR GUION DE MI WEBINAR",
72
- data=generated_content,
73
- file_name=f"guion_webinar_{timestamp}.md",
74
- mime="text/markdown",
75
- key="download_bottom"
76
- )
77
- except ValueError as e:
78
- col.error(f"Error: {str(e)}")
79
- else:
80
- col.error("Por favor, proporciona el público objetivo y el tema del webinar.")
81
-
82
- # Función para crear la configuración del modelo (evita duplicación)
83
- def create_model_config(temperature):
84
- return {
85
  "temperature": temperature,
86
- "top_p": 0.65,
87
- "top_k": 360,
88
- "max_output_tokens": 8196,
89
  }
 
90
 
91
- # Función para inicializar el modelo
92
- def initialize_model(temperature):
93
- config = create_model_config(temperature)
94
- return genai.GenerativeModel(
95
- model_name="gemini-2.0-flash",
96
- generation_config=config,
97
- )
98
-
99
- # Refactored model interaction function to reduce duplication
100
- def generate_content(prompt_instructions, temperature):
101
- model = initialize_model(temperature)
102
- chat_session = model.start_chat(
103
- history=[
104
- {
105
- "role": "user",
106
- "parts": [prompt_instructions],
107
- },
108
- ]
109
- )
110
- response = chat_session.send_message("Generate the content following exactly the provided instructions. All content must be in Spanish.")
111
- return response.text
112
-
113
- # Función para generar nombres de webinars
114
- def generate_webinar_names(number_of_names, target_audience, product, temperature, selected_formula, selected_angle=None):
115
- # Incluir las instrucciones del sistema en el prompt principal
116
- 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.
117
-
118
- FORMAT RULES:
119
- - Each webinar name must start with number and period
120
- - One webinar name per line
121
- - No explanations or categories
122
- - Add a line break between each name
123
- - Avoid unnecessary : symbols
124
- - Each webinar name must be a complete and intriguing title
125
- - WRITE ALL WEBINAR NAMES IN SPANISH
126
-
127
- FORMAT EXAMPLE:
128
- 1. Nombre del Webinar 1.
129
-
130
- 2. Nombre del Webinar 2.
131
-
132
- 3. Nombre del Webinar 3.
133
-
134
- 4. Nombre del Webinar 4.
135
-
136
- 5. Nombre del Webinar 5.
137
-
138
- IMPORTANT:
139
- - Each webinar name must be unique and memorable
140
- - Avoid clichés and generalities
141
- - Maintain an intriguing but credible tone
142
- - Adapt speaking language from the audience
143
- - Focus on transformative benefits
144
- - Follow the selected formula structure
145
- - WRITE ALL WEBINAR NAMES IN SPANISH"""
146
-
147
- # Iniciar el prompt con las instrucciones del sistema
148
- webinar_names_instruction = f"{system_prompt}\n\n"
149
-
150
- # Añadir instrucciones de ángulo solo si no es "NINGUNO" y se proporcionó un ángulo
151
- if selected_angle and selected_angle != "NINGUNO":
152
- webinar_names_instruction += f"""
153
- MAIN ANGLE: {selected_angle}
154
- SPECIFIC ANGLE INSTRUCTIONS:
155
- {angles_webinar_names[selected_angle]["instruction"]}
156
-
157
- IMPORTANT: The {selected_angle} angle should be applied as a "style layer" over the formula structure:
158
- 1. Keep the base structure of the formula intact
159
- 2. Apply the tone and style of the {selected_angle} angle
160
- 3. Ensure that each element of the formula reflects the angle
161
- 4. The angle affects "how" it is said, not "what" is said
162
-
163
- SUCCESSFUL EXAMPLES OF THE {selected_angle} ANGLE:
164
- """
165
- for example in angles_webinar_names[selected_angle]["examples"]:
166
- webinar_names_instruction += f"- {example}\n"
167
-
168
- # Instrucciones específicas para la tarea
169
- webinar_names_instruction += (
170
- f"\nYour task is to create {number_of_names} irresistible webinar names for {target_audience} "
171
- f"that instantly capture attention and generate registrations for a webinar about {product}. "
172
- f"Focus on awakening genuine interest and communicating the value they will get by registering."
173
- f"\n\n"
174
- f"IMPORTANT: Carefully study these examples of the selected formula. "
175
- f"Each example represents the style and structure to follow"
176
- f":\n\n"
177
- )
178
-
179
- # Agregar ejemplos aleatorios de la fórmula (keeping examples in Spanish)
180
- random_examples = random.sample(selected_formula['examples'], min(5, len(selected_formula['examples'])))
181
- webinar_names_instruction += "EXAMPLES OF THE FORMULA TO FOLLOW:\n"
182
- for i, example in enumerate(random_examples, 1):
183
- webinar_names_instruction += f"{i}. {example}\n"
184
-
185
- # Instrucciones específicas (translated to English)
186
- webinar_names_instruction += "\nSPECIFIC INSTRUCTIONS:\n"
187
- webinar_names_instruction += "1. Maintain the same structure and length as the previous examples\n"
188
- webinar_names_instruction += "2. Use the same tone and writing style\n"
189
- webinar_names_instruction += "3. Replicate the phrase construction patterns\n"
190
- webinar_names_instruction += "4. Preserve the level of specificity and detail\n"
191
- webinar_names_instruction += f"5. Adapt the content for {target_audience} while maintaining the essence of the examples\n\n"
192
- webinar_names_instruction += f"FORMULA TO FOLLOW:\n{selected_formula['description']}\n\n"
193
- webinar_names_instruction += f"""
194
- GENERATE NOW:
195
- Create {number_of_names} webinar names that faithfully follow the style and structure of the examples shown.
196
- """
197
-
198
- # Enviar el mensaje al modelo
199
- # Use the common generate_content function
200
- return generate_content(webinar_names_instruction, temperature)
201
-
202
- def generate_webinar_script(audience, topic, temperature, selected_formula):
203
- model = initialize_model(temperature)
204
-
205
- # Incluir las instrucciones del sistema en el prompt principal
206
- 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.
207
-
208
- THE EXPERT TEAM:
209
-
210
- 1. MASTER WEBINAR STRATEGIST:
211
- - Expert in webinar frameworks and conversion strategies
212
- - Trained in the Perfect Webinar methodology by Russell Brunson
213
- - Ensures the script follows the selected framework structure precisely
214
- - Focuses on strategic placement of key conversion elements
215
-
216
- 2. ELITE DIRECT RESPONSE COPYWRITER:
217
- - Trained by Gary Halbert, Gary Bencivenga, and David Ogilvy
218
- - Creates compelling hooks, stories, and persuasive elements
219
- - Crafts irresistible calls to action that drive conversions
220
- - Ensures the language resonates with the target audience
221
-
222
- 3. AUDIENCE PSYCHOLOGY SPECIALIST:
223
- - Expert in understanding audience motivations and objections
224
- - Creates content that builds genuine connection and trust
225
- - Identifies and addresses hidden fears and desires
226
- - Ensures the content feels personal and relevant
227
-
228
- 4. STORYTELLING MASTER:
229
- - Creates compelling narratives that illustrate key points
230
- - Develops relatable examples and case studies
231
- - Ensures stories support the transformation being offered
232
- - Makes complex concepts accessible through narrative
233
-
234
- 5. WEBINAR ENGAGEMENT EXPERT:
235
- - Specializes in maintaining audience attention throughout
236
- - Creates interactive elements and engagement hooks
237
- - Develops compelling transitions between sections
238
- - Ensures the webinar flows naturally and keeps interest high
239
-
240
- AUDIENCE UNDERSTANDING:
241
- You understand how real people interact with webinar content:
242
- - They quickly lose interest if the content feels generic or corporate
243
- - They only stay engaged when the content feels personal and sparks genuine curiosity
244
- - They respond to messages that seem delivered by a real person, not a corporation
245
- - They engage with content that hooks them from the first line and maintains interest throughout
246
-
247
- FORMAT RULES:
248
- - Create a complete webinar script with clear sections and subsections
249
- - Include specific talking points for each section
250
- - Write in a conversational, engaging tone
251
- - Include persuasive elements and calls to action
252
- - Follow the selected webinar framework structure exactly
253
- - WRITE THE ENTIRE SCRIPT IN SPANISH
254
- - DO NOT include any introductory text or explanations about the script
255
- - Start directly with the webinar content
256
-
257
- IMPORTANT:
258
- - The script must be comprehensive and ready to use
259
- - Include specific examples and stories relevant to the topic
260
- - Maintain a persuasive but authentic tone
261
- - Adapt language to match the target audience
262
- - Focus on transformative benefits and clear value proposition
263
- - Follow the selected formula structure precisely
264
- - WRITE THE ENTIRE SCRIPT IN SPANISH
265
- - DO NOT include phrases like "Aquí tienes un guion completo" or any other meta-commentary
266
-
267
- COLLABORATIVE PROCESS:
268
- As a team of experts, you will:
269
-
270
- 1. Analyze the framework '{selected_formula['description']}' to understand its core principles
271
- 2. Identify how to best adapt this framework for {audience} learning about {topic}
272
- 3. Determine the most effective storytelling opportunities within the framework
273
- 4. Create persuasive language that resonates with {audience}
274
- 5. Ensure the script maintains engagement throughout
275
- 6. Adapt the content to be accessible to beginners while still valuable to experienced individuals
276
- 7. Follow the exact structure provided in the framework
277
-
278
- Each expert will contribute their specialized knowledge to create a cohesive, compelling script that:
279
- - Follows the exact structure of the selected framework
280
- - Engages the audience from start to finish
281
- - Addresses audience pain points and desires
282
- - Presents {topic} in a clear, compelling way
283
- - Drives conversions through strategic calls to action"""
284
 
285
- # Iniciar el prompt con las instrucciones del sistema
286
- webinar_script_instruction = f"{system_prompt}\n\n"
 
287
 
288
- # Instrucciones específicas para la tarea
289
- webinar_script_instruction += (
290
- f"\nYour task is to create a complete webinar script IN SPANISH for {audience} "
291
- f"about {topic} that is persuasive and converts the audience into customers. "
292
- f"The script must follow exactly the structure of the framework '{selected_formula['description']}' "
293
- f"and must include all the necessary elements for a successful webinar."
294
- f"\n\n"
295
- )
296
-
297
- # Estructura del webinar
298
- webinar_script_instruction += "WEBINAR STRUCTURE TO FOLLOW:\n"
299
- for i, step in enumerate(selected_formula['structure'], 1):
300
- webinar_script_instruction += f"{i}. {step}\n"
301
-
302
- # Ejemplos de webinars exitosos
303
- webinar_script_instruction += "\n\nEXAMPLES OF SUCCESSFUL WEBINARS WITH THIS STRUCTURE:\n"
304
- for i, example in enumerate(selected_formula['examples'], 1):
305
- webinar_script_instruction += f"{i}. {example}\n"
306
-
307
- # Instrucciones específicas
308
- webinar_script_instruction += f"""
309
- SPECIFIC INSTRUCTIONS:
310
- 1. Create a complete script that follows exactly the provided structure
311
- 2. Include persuasive elements and clear calls to action
312
- 3. Adapt the language and examples specifically for {audience}
313
- 4. Focus on the transformative benefits of {topic}
314
- 5. Include relevant stories and examples that reinforce your points
315
- 6. Use a conversational but professional tone
316
- 7. Make sure each section fulfills its specific purpose in the framework
317
- 8. IMPORTANT: Write the entire script in Spanish
318
-
319
- GENERATE NOW:
320
- Create a complete webinar script following faithfully the structure of the selected framework.
321
- """
322
-
323
- # Enviar el mensaje al modelo
324
- chat_session = model.start_chat(
325
- history=[
326
- {
327
- "role": "user",
328
- "parts": [webinar_script_instruction],
329
- },
330
- ]
331
- )
332
- 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).")
333
-
334
- return response.text
335
-
336
- # Función para validar entradas (evita duplicación)
337
- def validate_inputs(audience, product):
338
- has_audience = audience.strip() != ""
339
- has_product = product.strip() != ""
340
- return has_audience and has_product
341
-
342
- # Update the load_css function comment to be more descriptive
343
- def load_css():
344
- css_path = "styles/styles.css"
345
- if os.path.exists(css_path):
346
- try:
347
- with open(css_path, "r") as f:
348
- st.markdown(f"<style>{f.read()}</style>", unsafe_allow_html=True)
349
- except Exception as e:
350
- st.warning(f"Error al cargar el archivo CSS: {str(e)}")
351
- else:
352
- st.warning(f"No se encontró el archivo CSS en {css_path}")
353
-
354
- # Create a reusable function for custom buttons
355
- # At the top of your file, define the styles dictionary
356
- styles = {
357
- "button": """
358
- <style>
359
- div.stButton > button:first-child {
360
- background-color: #FF4B4B;
361
- color: white;
362
- font-weight: bold;
363
- padding: 0.5rem 1rem;
364
- border-radius: 0.5rem;
365
- border: none;
366
- transition: all 0.3s;
367
- }
368
- div.stButton > button:hover {
369
- background-color: #FF2525;
370
- transform: translateY(-2px);
371
- box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
372
- }
373
- </style>
374
- """,
375
- "download_button": """
376
- <style>
377
- div.stDownloadButton > button:first-child {
378
- background-color: #4CAF50;
379
- color: white;
380
- font-weight: bold;
381
- padding: 0.5rem 1rem;
382
- border-radius: 0.5rem;
383
- border: none;
384
- transition: all 0.3s;
385
- }
386
- div.stDownloadButton > button:hover {
387
- background-color: #45a049;
388
- transform: translateY(-2px);
389
- box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
390
- }
391
- </style>
392
- """
393
- }
394
-
395
- # Then modify your create_custom_button function to be much simpler:
396
- def create_custom_button(label, button_key):
397
- # Apply the button style once at the beginning of your app
398
- # (You can move this to your main app code if you prefer)
399
- st.markdown(styles["button"], unsafe_allow_html=True)
400
-
401
- # Just return a regular Streamlit button
402
- return st.button(label, key=button_key)
403
-
404
- # Modify the page config section to include the CSS loading
405
- st.set_page_config(page_title="Perfect Webinar Framework", layout="wide")
406
- load_css()
407
-
408
- # Leer el contenido del archivo manual.md
409
- with open("manual.md", "r", encoding="utf-8") as file:
410
- manual_content = file.read()
411
-
412
- # Mostrar el contenido del manual en el sidebar
413
- st.sidebar.markdown(manual_content)
414
-
415
- # Crear pestañas para la interfaz
416
- tab1, tab2 = st.tabs(["Guiones de Webinar", "Nombres de Webinar"])
417
-
418
- # Primera pestaña - Generador de Guiones de Webinar
419
- with tab1:
420
- tab1.subheader("Script Webinar")
421
-
422
- # Crear columnas para la interfaz
423
- col1, col2 = tab1.columns([1, 2])
424
-
425
- # Columna de entrada usando la función reutilizable
426
- with col1:
427
- webinar_script_audience, webinar_script_product, selected_webinar_formula_key = create_input_section(
428
- col1,
429
- "webinar_script_audience",
430
- "webinar_script_product",
431
- webinar_formulas,
432
- "webinar_formula"
433
  )
434
 
435
- # Usar la función reutilizable para crear el botón personalizado
436
- submit_webinar_script = create_custom_button("Generar Guión de Webinar", "generate_webinar_script")
437
-
438
- # Opciones avanzadas
439
- with st.expander("Personaliza tu guión de webinar"):
440
- webinar_script_temperature = st.slider("Creatividad", min_value=0.0, max_value=2.0, value=1.0, step=0.1, key="webinar_script_temp")
441
 
442
- selected_webinar_formula = webinar_formulas[selected_webinar_formula_key]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
443
 
444
- # Generar y mostrar el guión usando la función reutilizable
445
- if submit_webinar_script:
446
- generate_and_display(
447
- col2,
448
- generate_webinar_script,
449
- webinar_script_audience,
450
- webinar_script_product,
451
- webinar_script_temperature,
452
- selected_webinar_formula,
453
- content_type="script"
454
- )
455
-
456
- # Segunda pestaña - Generador de Nombres de Webinar
457
- with tab2:
458
- tab2.subheader("Nombres de Webinar")
459
 
460
- # Crear columnas para la interfaz
461
- col1, col2 = tab2.columns([1, 2])
462
 
463
- # Columna de entrada usando la función reutilizable
464
- with col1:
465
- webinar_name_audience, webinar_name_product, selected_webinar_name_formula_key = create_input_section(
466
- col1,
467
- "webinar_name_audience",
468
- "webinar_name_product",
469
- webinar_name_formulas,
470
- "webinar_name_formula"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
471
  )
 
472
 
473
- 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")
 
 
 
 
 
 
 
474
 
475
- # Usar la función reutilizable para crear el botón personalizado
476
- submit_webinar_names = create_custom_button("Generar Nombres de Webinar", "generate_webinar_names")
477
-
478
- # Opciones avanzadas
479
- with st.expander("Personaliza tus nombres de webinar"):
480
- webinar_name_temperature = st.slider("Creatividad", min_value=0.0, max_value=2.0, value=1.0, step=0.1, key="webinar_name_temp")
481
-
482
- # Configurar opciones de ángulo
483
- angle_keys = ["NINGUNO"] + sorted([key for key in angles_webinar_names.keys() if key != "NINGUNO"])
484
- selected_angle = st.selectbox(
485
- "Selecciona el ángulo para tus nombres",
486
- options=angle_keys,
487
- key="webinar_name_angle"
488
- )
489
-
490
- selected_webinar_name_formula = webinar_name_formulas[selected_webinar_name_formula_key]
491
-
492
- # Generar y mostrar los nombres usando la función reutilizable
493
- if submit_webinar_names:
494
- generate_and_display(
495
- col2,
496
- generate_webinar_names,
497
- webinar_name_audience,
498
- webinar_name_product,
499
- webinar_name_temperature,
500
- selected_webinar_name_formula,
501
- content_type="names",
502
- number_of_names=number_of_names,
503
- selected_angle=selected_angle if selected_angle != "NINGUNO" else None
504
  )
505
- if validate_inputs(webinar_name_audience, webinar_name_product):
506
- try:
507
- with col2:
508
- with st.spinner("Generando tus nombres de webinar...", show_time=True):
509
- generated_webinar_names = generate_webinar_names(
510
- number_of_names,
511
- webinar_name_audience,
512
- webinar_name_product,
513
- webinar_name_temperature,
514
- selected_webinar_name_formula,
515
- selected_angle if selected_angle != "NINGUNO" else None
516
- )
517
- st.subheader("Tus nombres de webinar:")
518
- st.markdown(generated_webinar_names)
519
- except ValueError as e:
520
- col2.error(f"Error: {str(e)}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
521
  else:
522
- col2.error("Por favor, proporciona el público objetivo y el tema del webinar.")
523
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
  # 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
+ )