JeCabrera commited on
Commit
07aff9f
·
verified ·
1 Parent(s): 0eccbef

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +327 -186
app.py CHANGED
@@ -1,204 +1,345 @@
1
- offer_formulas = {
2
- "Oferta Dorada": {
3
- "description": """
4
- Formula: [Gancho + PROMESA EN MAYÚSCULAS + Beneficio con Autoridad]
 
 
 
 
 
 
 
5
 
6
- Esta fórmula combina tres elementos poderosos para crear ofertas irresistibles:
 
 
 
 
 
7
 
8
- ### **Elementos de la Oferta Dorada:**
 
9
 
10
- #### **Gancho de Atención**
11
- El elemento inicial que captura el interés inmediato del prospecto. Puede ser:
12
- - Una pregunta provocativa que genere curiosidad
13
- - Un dato estadístico sorprendente que rompa creencias
14
- - Una afirmación contundente sobre su situación actual
15
- - Un contraste entre su realidad y lo que podría tener
16
- - Una historia empática que refleje exactamente su dolor
17
 
18
- #### **PROMESA CUANTIFICABLE**
19
- El corazón de la oferta, siempre en mayúsculas para destacar visualmente. Contiene:
20
- - Resultados específicos con números concretos (dinero, porcentajes, tiempo)
21
- - Verbos de acción que transmiten transformación (LOGRA, MULTIPLICA, DOMINA)
22
- - Marcos temporales definidos que crean urgencia (EN 30 DÍAS, EN 2 SEMANAS)
23
- - Indicadores de esfuerzo mínimo (CON SOLO 15 MINUTOS DIARIOS, EN 3 PASOS SIMPLES)
24
- - Eliminación de objeciones (SIN EXPERIENCIA PREVIA, SIN INVERSIONES COSTOSAS)
25
 
26
- #### **Beneficio con Autoridad**
27
- El elemento que solidifica la credibilidad y refuerza la decisión de compra. Incluye:
28
- - Prueba social con números específicos (casos de éxito, testimonios)
29
- - Indicadores de tiempo para ver resultados (desde la primera semana)
30
- - Esfuerzo mínimo requerido (dedicando solo X minutos diarios)
31
- - Elementos de autoridad (estudios, certificaciones, experiencia)
32
- - Garantía implícita o explícita de que funciona
33
 
34
- ### **Estructura:**
35
- "[Gancho que conecta con su problema]
 
 
 
 
 
 
36
 
37
- [PROMESA ESPECÍFICA Y MEDIBLE EN MAYÚSCULAS]
 
38
 
39
- [Solución + Prueba de que funciona + Tiempo para ver resultados]"
40
- """,
41
- "examples": [
42
- # Example 1 - Nicho de desarrollo web
43
- """¿Estás cansado de pasar horas intentando que tu sitio web se vea profesional sin conseguirlo?
44
-
45
- CREA UN SITIO WEB PROFESIONAL EN MENOS DE 48 HORAS SIN CONOCIMIENTOS DE PROGRAMACIÓN NI CONTRATAR A UN DISEÑADOR COSTOSO.
46
-
47
- Más de 1,200 emprendedores han transformado su presencia online siguiendo este proceso paso a paso, aumentando sus conversiones en un 35% desde la primera semana.""",
48
-
49
- # Example 2 - Nicho de finanzas personales
50
- """7 de cada 10 personas llegan a la jubilación sin los ahorros suficientes para mantener su estilo de vida.
51
-
52
- DUPLICA TU PATRIMONIO EN 5 AÑOS INVIRTIENDO SOLO EL 10% DE TUS INGRESOS SIN ASUMIR RIESGOS INNECESARIOS.
53
-
54
- Los resultados hablan por sí solos: más de 3,500 personas han logrado independencia financiera siguiendo estos principios, incluso comenzando desde cero y con deudas.""",
55
-
56
- # Example 3 - Nicho de salud y bienestar
57
- """La mayoría de personas que intentan mejorar su salud abandonan en las primeras semanas por falta de resultados visibles.
58
-
59
- REDUCE TU PORCENTAJE DE GRASA CORPORAL ENTRE UN 8% Y 12% EN 60 DÍAS SIN DIETAS EXTREMAS NI ENTRENAMIENTOS EXTENUANTES.
60
-
61
- Respaldado por estudios clínicos con más de 500 participantes que lograron transformar su composición corporal dedicando solo 25 minutos diarios, viendo resultados medibles desde la segunda semana.""",
62
- ],
63
- "instructions": """
64
- INSTRUCCIONES PARA CREAR UNA OFERTA IRRESISTIBLE:
65
-
66
- 1. PROCESO DE CREACIÓN EN 3 PASOS:
67
-
68
- PASO 1: GANCHO DE ATENCIÓN
69
- - Analiza profundamente la descripción del avatar para entender sus dolores
70
- - Crea un gancho que aborde directamente su mayor frustración
71
- - Enfócate en el impacto emocional de su problema
72
- - Sé creativo y variado en tu enfoque (preguntas, estadísticas, contrastes, empatía)
73
-
74
- PASO 2: PROMESA CUANTIFICABLE
75
- - Escribe una promesa COMPLETAMENTE EN MAYÚSCULAS que incluya:
76
- * Números concretos (dinero, tiempo, resultados)
77
- * Verbos de acción poderosos
78
- * Tiempos específicos
79
- * Indicadores claros de esfuerzo
80
- - Estructura: [RESULTADO DESEADO] SIN [OBJECIÓN O CREENCIA LIMITANTE]
81
- - NUNCA uses signos de exclamación (!) en esta sección
82
- - Evita frases genéricas como "al otro nivel", "en tiempo récord"
83
-
84
- PASO 3: BENEFICIO CON AUTORIDAD Y TIEMPO
85
- - Incluye elementos que demuestren que tu sistema funciona:
86
- * Resultados personales o de clientes con números específicos
87
- * Prueba social (testimonios, casos de estudio)
88
- - Especifica claramente cuándo verán resultados
89
- - Minimiza el esfuerzo requerido del cliente
90
- - Combina todos los elementos en una oferta coherente
91
- - Usa lenguaje ESPECÍFICO y CONCRETO
92
- """
93
- },
94
- "Fórmula Sueño-Obstáculo": {
95
- "description": """
96
- Formula: [Type + Name + Dream + Obstacle]
97
-
98
- This formula connects directly with the client's desires and concerns:
99
 
100
- 1. Type: The type of solution (training, product, or service)
101
- 2. Name: The name of your solution
102
- 3. Dream: The big dream or result that the client wants to achieve
103
- 4. Obstacle: The obstacle that would normally prevent achieving that dream
104
 
105
- **Suggested solution types:**
106
- - Online course
107
- - Webinar
108
- - Training
109
- - Program
110
- - Workshop
111
- - Mentorship
112
- - Consulting
113
- - Membership
114
- - System
115
- - Method
116
- - Service
117
- - Product
118
- - Application
119
- - Community
120
- - Masterclass
121
 
122
- **Suggested opening variations:**
123
- - "Se trata de un..."
124
- - "Presentamos un..."
125
- - "Te ofrecemos un..."
126
- - "Descubre nuestro..."
127
- - "Conoce el..."
128
- - "Hemos creado un..."
129
- - "Imagina tener acceso a un..."
130
- - "Por fin existe un..."
131
- - "Ahora puedes acceder a un..."
132
- - "Tenemos para ti un..."
133
- - "Disfruta de un..."
134
-
135
- **Structure Format (Classic example):**
136
- "Se trata de un (training, product or service) llamado ("name of your solution") que te va a permitir (big dream) aún y cuando (big obstacle)"
137
- """,
138
- "examples": [
139
- # Example 1 - Sales Training
140
- """Se trata de mi exclusivo entrenamiento llamado "Venta Express 3.0" con el que te convertirás en todo un Rockstar de las ventas con el que conectarás con tus clientes para inspirarlos al cierre de una forma fácil y divertida aún y cuando sea la primera vez que escuchen de ti.""",
141
-
142
- # Example 2 - Fitness Program
143
- """Presentamos un programa de transformación física llamado "Metabolismo Activado" que te permitirá perder hasta 10 kilos en 8 semanas aún y cuando hayas intentado todas las dietas sin éxito.""",
144
 
145
- # Example 3 - Digital Marketing
146
- """Descubre nuestro sistema de marketing digital llamado "Tráfico Convertidor" que te ayudará a multiplicar tus ventas online sin necesidad de gastar fortunas en publicidad aún y cuando no tengas experiencia previa en marketing.""",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
147
 
148
- # Example 4 - Personal Finance
149
- """Tenemos para ti un método financiero llamado "Libertad Financiera Acelerada" que te permitirá generar ingresos pasivos y construir patrimonio aún y cuando partas desde cero o tengas deudas actualmente.""",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
150
 
151
- # Example 5 - Language Learning
152
- """Imagina tener acceso a un curso de idiomas llamado "Inglés Sin Barreras Pro" con el que dominarás el inglés conversacional en solo 90 días aún y cuando hayas fracasado con métodos tradicionales en el pasado.""",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
153
 
154
- # Example 6 - Productivity
155
- """Hemos creado un workshop intensivo llamado "Productividad Imparable" que te enseñará a duplicar tus resultados trabajando menos horas aún y cuando tengas una agenda completamente saturada.""",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
156
 
157
- # Example 7 - Relationships
158
- """Por fin existe un programa de coaching llamado "Conexiones Profundas" que te ayudará a encontrar y mantener relaciones saludables y satisfactorias aún y cuando hayas tenido experiencias dolorosas en el pasado."""
159
- ],
160
- "instructions": """
161
- SPECIFIC INSTRUCTIONS FOR THIS FORMULA:
162
- 1. PRODUCT/SERVICE NAME HANDLING:
163
- - CRITICAL: If a product name is provided, YOU MUST USE THAT EXACT NAME. This is non-negotiable.
164
- - If the product name is not empty, use it EXACTLY as provided with no modifications
165
- - Only create a new name if no product name is provided or it contains generic placeholders
166
-
167
- 2. Analyze ALL available information:
168
- - Product/service name (use the name provided)
169
- - Target audience description (avatar_description)
170
- - Content from uploaded files (if any)
171
-
172
- 3. Determine the most appropriate type (curso, webinar, entrenamiento, etc.) based on:
173
- - Any type mentioned in product_name
174
- - The nature of the solution described in avatar_description
175
- - The most suitable format for the target audience's needs
176
-
177
- 4. Create a comprehensive offer by combining:
178
- - The appropriate type (determined in step 3)
179
- - The EXACT product name as provided (if available)
180
- - A compelling dream based on avatar_description
181
- - A relevant obstacle based on avatar_description
182
-
183
- 5. The dream should be ambitious but believable, incorporating:
184
- - Target audience desires from avatar_description
185
- - Explicit goals mentioned in uploaded content (if any)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
186
 
187
- 6. The obstacle should reflect:
188
- - Real problems mentioned in avatar_description
189
- - Challenges that would normally prevent achieving the dream
190
 
191
- 7. IMPORTANT: Vary the way you start the phrase. Instead of always using "Se trata de un...", use different openings such as:
192
- - "Presentamos un..."
193
- - "Te ofrecemos un..."
194
- - "Descubre nuestro..."
195
- - "Conoce el..."
196
- - "Hemos creado un..."
197
- - "Imagina tener acceso a un..."
198
- - "Por fin existe un..."
199
- - "Ahora puedes acceder a un..."
200
- - "Tenemos para ti un..."
201
- - "Disfruta de un..."
202
- """
203
- }
204
- }
 
1
+ import streamlit as st
2
+ import google.generativeai as genai
3
+ import os
4
+ import time
5
+ from dotenv import load_dotenv
6
+ from styles import get_custom_css, get_response_html_wrapper
7
+ from formulas import offer_formulas
8
+ import PyPDF2
9
+ import docx
10
+ from PIL import Image
11
+ import io
12
 
13
+ # Import the bullet generator
14
+ from bullets.generator import create_bullet_instruction
15
+ # Import the bonus generator
16
+ from bonuses.generator import create_bonus_instruction
17
+ # Import the offer instruction generator
18
+ from prompts import create_offer_instruction
19
 
20
+ # Set page to wide mode to use full width
21
+ st.set_page_config(layout="wide")
22
 
23
+ # Load environment variables
24
+ load_dotenv()
 
 
 
 
 
25
 
26
+ # Configure Google Gemini API
27
+ genai.configure(api_key=os.getenv('GOOGLE_API_KEY'))
28
+ model = genai.GenerativeModel('gemini-2.0-flash')
 
 
 
 
29
 
30
+ # Initialize session state variables if they don't exist
31
+ if 'submitted' not in st.session_state:
32
+ st.session_state.submitted = False
33
+ if 'offer_result' not in st.session_state:
34
+ st.session_state.offer_result = ""
35
+ if 'generated' not in st.session_state:
36
+ st.session_state.generated = False
37
 
38
+ # Hide Streamlit menu and footer
39
+ st.markdown("""
40
+ <style>
41
+ #MainMenu {visibility: hidden;}
42
+ footer {visibility: hidden;}
43
+ header {visibility: hidden;}
44
+ </style>
45
+ """, unsafe_allow_html=True)
46
 
47
+ # Custom CSS
48
+ st.markdown(get_custom_css(), unsafe_allow_html=True)
49
 
50
+ # App title and description
51
+ st.markdown('<h1 style="text-align: center;">Great Offer Generator</h1>', unsafe_allow_html=True)
52
+ st.markdown('<h3 style="text-align: center;">Transform your skills into compelling offers!</h3>', unsafe_allow_html=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53
 
54
+ # Create two columns for layout - left column 40%, right column 60%
55
+ col1, col2 = st.columns([4, 6])
 
 
56
 
57
+ # Main input section in left column
58
+ with col1:
59
+ # Define the generate_offer function first
60
+ def handle_generate_button(): # Renamed to avoid conflict
61
+ has_manual_input = bool(skills or product_service)
62
+ has_file_input = bool(uploaded_file is not None and not is_image)
63
+ has_image_input = bool(uploaded_file is not None and is_image)
 
 
 
 
 
 
 
 
 
64
 
65
+ # Simple validation - check if we have at least one input type
66
+ if not (has_manual_input or has_file_input or has_image_input):
67
+ st.error('Por favor ingresa texto o sube un archivo/imagen')
68
+ return
69
+
70
+ st.session_state.submitted = True
71
+ st.session_state.generated = False # Reset generated flag
72
+
73
+ # Store inputs based on what's available
74
+ if has_manual_input:
75
+ st.session_state.skills = skills if skills else ""
76
+ st.session_state.product_service = product_service if product_service else ""
77
+
78
+ if has_file_input:
79
+ st.session_state.file_content = file_content
 
 
 
 
 
 
 
80
 
81
+ if has_image_input:
82
+ st.session_state.image_parts = image_parts
83
+
84
+ # Set input type based on what's available
85
+ if has_image_input:
86
+ if has_manual_input:
87
+ st.session_state.input_type = "manual_image"
88
+ else:
89
+ st.session_state.input_type = "image"
90
+ else:
91
+ if has_manual_input and has_file_input:
92
+ st.session_state.input_type = "both"
93
+ elif has_file_input:
94
+ st.session_state.input_type = "file"
95
+ elif has_manual_input:
96
+ st.session_state.input_type = "manual"
97
+
98
+ # Store common settings
99
+ st.session_state.target_audience = target_audience
100
+ st.session_state.temperature = temperature
101
+ st.session_state.formula_type = formula_type
102
+
103
+ # Keep only the manual input tab
104
+ with st.container():
105
+ skills = st.text_area('💪 Tus Habilidades', height=70,
106
+ help='Lista tus habilidades y experiencia clave')
107
+ product_service = st.text_area('🎯 Producto/Servicio', height=70,
108
+ help='Describe tu producto o servicio')
109
+
110
+ # Generate button moved here - right after product/service
111
+ st.button('Generar Oferta 🎉', on_click=handle_generate_button) # Updated function name
112
+
113
+ # Accordion for additional settings
114
+ with st.expander('⚙️ Configuración Avanzada'):
115
+ target_audience = st.text_area('👥 Público Objetivo', height=70,
116
+ help='Describe tu cliente o público ideal')
117
+
118
+ # Add file/image uploader here
119
+ uploaded_file = st.file_uploader("📄 Sube un archivo o imagen",
120
+ type=['txt', 'pdf', 'docx', 'jpg', 'jpeg', 'png'])
121
+
122
+ if uploaded_file is not None:
123
+ file_type = uploaded_file.name.split('.')[-1].lower()
124
 
125
+ # Handle text files
126
+ if file_type in ['txt', 'pdf', 'docx']:
127
+ if file_type == 'txt':
128
+ try:
129
+ file_content = uploaded_file.read().decode('utf-8')
130
+ except Exception as e:
131
+ st.error(f"Error al leer el archivo TXT: {str(e)}")
132
+ file_content = ""
133
+
134
+ elif file_type == 'pdf':
135
+ try:
136
+ import PyPDF2
137
+ pdf_reader = PyPDF2.PdfReader(uploaded_file)
138
+ file_content = ""
139
+ for page in pdf_reader.pages:
140
+ file_content += page.extract_text() + "\n"
141
+ except Exception as e:
142
+ st.error(f"Error al leer el archivo PDF: {str(e)}")
143
+ file_content = ""
144
+
145
+ elif file_type == 'docx':
146
+ try:
147
+ import docx
148
+ doc = docx.Document(uploaded_file)
149
+ file_content = "\n".join([para.text for para in doc.paragraphs])
150
+ except Exception as e:
151
+ st.error(f"Error al leer el archivo DOCX: {str(e)}")
152
+ file_content = ""
153
+
154
+ # Remove success message - no notification shown
155
+
156
+ # Set file type flag
157
+ is_image = False
158
 
159
+ # Handle image files
160
+ elif file_type in ['jpg', 'jpeg', 'png']:
161
+ try:
162
+ image = Image.open(uploaded_file)
163
+ st.image(image, caption="Imagen cargada", use_container_width=True)
164
+
165
+ image_bytes = uploaded_file.getvalue()
166
+ image_parts = [
167
+ {
168
+ "mime_type": uploaded_file.type,
169
+ "data": image_bytes
170
+ }
171
+ ]
172
+
173
+ # Set file type flag
174
+ is_image = True
175
+ except Exception as e:
176
+ st.error(f"Error al procesar la imagen: {str(e)}")
177
+ is_image = False
178
+
179
+ # Selector de fórmula
180
+ formula_type = st.selectbox(
181
+ '📋 Tipo de Fórmula',
182
+ options=list(offer_formulas.keys()),
183
+ help='Selecciona el tipo de fórmula para tu oferta'
184
+ )
185
+
186
+ temperature = st.slider('🌡️ Nivel de Creatividad', min_value=0.0, max_value=2.0, value=1.0,
187
+ help='Valores más altos hacen que el resultado sea más creativo pero menos enfocado')
188
+
189
+ # Results column
190
+ # In the section where you're generating the offer
191
+ with col2:
192
+ if st.session_state.submitted and not st.session_state.generated:
193
+ with st.spinner('Creando tu oferta perfecta...'):
194
+ # Use the create_offer_instruction function to generate the prompt
195
+ avatar_description = st.session_state.target_audience if hasattr(st.session_state, 'target_audience') and st.session_state.target_audience else 'General audience'
196
 
197
+ # Determine product name based on input type
198
+ if hasattr(st.session_state, 'product_service') and st.session_state.product_service:
199
+ product_name = st.session_state.product_service
200
+ else:
201
+ # Generar un nombre creativo en lugar de usar "Producto/Servicio"
202
+ try:
203
+ creative_name_prompt = f"""Crea un nombre creativo y atractivo para un producto o servicio
204
+ basado en estas habilidades: {st.session_state.skills if hasattr(st.session_state, 'skills') else 'profesional'}.
205
+ El público objetivo es: {avatar_description}.
206
+ Responde SOLO con el nombre, sin explicaciones ni comillas."""
207
+
208
+ creative_name_response = model.generate_content(
209
+ creative_name_prompt,
210
+ generation_config=genai.GenerationConfig(temperature=0.8, max_output_tokens=30)
211
+ )
212
+ product_name = creative_name_response.text.strip().replace('"', '').replace("'", "")
213
+
214
+ # Si el nombre generado está vacío o es demasiado largo, usar un valor predeterminado
215
+ if not product_name or len(product_name) > 50:
216
+ product_name = "Sistema Profesional"
217
+ except Exception:
218
+ product_name = "Sistema Profesional"
219
+
220
+ # Get the instruction using the formula
221
+ # Find where create_offer_instruction is being called, around line 222
222
+ instruction = create_offer_instruction(
223
+ avatar_description=avatar_description,
224
+ product_name=product_name,
225
+ selected_formula_name=st.session_state.formula_type # Changed from selected_formula to st.session_state.formula_type
226
+ )
227
+
228
+ # Add instruction for generating benefit bullets based on the promise
229
+ bullet_content = None
230
+ if hasattr(st.session_state, 'file_content') and st.session_state.input_type in ["file", "both"]:
231
+ bullet_content = st.session_state.file_content
232
+
233
+ instruction += create_bullet_instruction(
234
+ avatar_description=avatar_description,
235
+ product_name=product_name,
236
+ uploaded_content=bullet_content
237
+ )
238
+
239
+ # Add instruction for generating bonuses that complement the offer
240
+ instruction += create_bonus_instruction(
241
+ avatar_description=avatar_description,
242
+ product_name=product_name,
243
+ selected_formula_name=st.session_state.formula_type
244
+ )
245
 
246
+ # Add additional context based on input type
247
+ if st.session_state.input_type == "manual":
248
+ additional_context = f"""
249
+ Additional information:
250
+ Skills: {st.session_state.skills}
251
+ """
252
+ instruction += additional_context
253
+ elif st.session_state.input_type == "file":
254
+ additional_context = f"""
255
+ Additional information from file:
256
+ {st.session_state.file_content}
257
+ """
258
+ instruction += additional_context
259
+ elif st.session_state.input_type == "both":
260
+ additional_context = f"""
261
+ Additional information:
262
+ Skills: {st.session_state.skills}
263
+ File content: {st.session_state.file_content}
264
+ """
265
+ instruction += additional_context
266
+
267
+ try:
268
+ generation_config = genai.GenerationConfig(temperature=st.session_state.temperature)
269
+
270
+ if "image" in st.session_state.input_type:
271
+ response = model.generate_content([instruction, st.session_state.image_parts[0]], generation_config=generation_config)
272
+ else:
273
+ response = model.generate_content(instruction, generation_config=generation_config)
274
+
275
+ # Get the response text
276
+ response_text = response.text
277
+
278
+ # Cuando procesas la respuesta del modelo para la Oferta Dorada
279
+ if st.session_state.formula_type == "Oferta Dorada":
280
+
281
+ # Eliminar espacios al inicio de cada línea (indentación)
282
+ lines = response_text.split('\n')
283
+ lines = [line.lstrip() for line in lines] # Elimina espacios al inicio de cada línea
284
+
285
+ # Asegurarse de que no haya líneas en blanco extras que puedan afectar el formato
286
+ lines = [line for line in lines if line.strip()]
287
+ response_text = '\n\n'.join(lines)
288
+
289
+ # Natural integration of product name in the response
290
+ if hasattr(st.session_state, 'product_service') and st.session_state.product_service:
291
+ product_name = st.session_state.product_service
292
+
293
+ # Split the text into lines to process each part of the formula
294
+ lines = response_text.split('\n')
295
+
296
+ # Process each line for proper product name integration
297
+ for i in range(len(lines)):
298
+ # For the second line (usually the promise in ALL CAPS), make product name uppercase
299
+ if i == 1 and st.session_state.formula_type == "Oferta Dorada":
300
+ lines[i] = lines[i].upper()
301
+
302
+ # Remove quotes around product name in all lines
303
+ lines[i] = lines[i].replace(f'"{product_name}"', product_name)
304
+ lines[i] = lines[i].replace(f"'{product_name}'", product_name)
305
+
306
+ # Ensure product name is properly capitalized in benefit descriptions
307
+ if i >= 2 and "sistema" in lines[i].lower() and product_name.lower() not in lines[i].lower():
308
+ lines[i] = lines[i].replace("sistema", product_name, 1)
309
+ if i >= 2 and "método" in lines[i].lower() and product_name.lower() not in lines[i].lower():
310
+ lines[i] = lines[i].replace("método", product_name, 1)
311
+
312
+ # Rejoin the lines
313
+ response_text = '\n'.join(lines)
314
+
315
+ st.session_state.offer_result = response_text
316
+ st.session_state.generated = True # Mark as generated
317
+
318
+ except Exception as e:
319
+ st.error(f'Ocurrió un error: {str(e)}')
320
+ st.session_state.submitted = False
321
+
322
+ # Display results if we have an offer result
323
+ if st.session_state.generated:
324
+ # Remove the visualization mode option
325
+
326
+ # Display the formatted result directly
327
+ st.markdown(get_response_html_wrapper(st.session_state.offer_result), unsafe_allow_html=True)
328
+
329
+ # Add a small space
330
+ st.markdown('<div style="height: 15px;"></div>', unsafe_allow_html=True)
331
+
332
+ # Apply the custom button style before rendering the download button
333
+ st.markdown('<style>div.stDownloadButton > button {your-custom-styles-here}</style>', unsafe_allow_html=True)
334
+ st.download_button(
335
+ label="Descargar Oferta",
336
+ data=st.session_state.offer_result,
337
+ file_name="oferta_generada.txt",
338
+ mime="text/plain"
339
+ )
340
 
341
+ # Footer
342
+ st.markdown('---')
343
+ st.markdown('Made with ❤️ by Jesús Cabrera')
344
 
345
+ # Remove the duplicate functions at the bottom