Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -1,470 +1,359 @@
|
|
1 |
-
|
2 |
-
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
24 |
|
25 |
-
|
26 |
-
|
27 |
-
"""
|
28 |
-
import re
|
29 |
-
|
30 |
-
# If input is empty or None, return empty string
|
31 |
-
if not product_name_input or product_name_input.strip() == "":
|
32 |
-
return ""
|
33 |
-
|
34 |
-
# Check if there's a name in quotes
|
35 |
-
quote_pattern = r'"([^"]+)"'
|
36 |
-
matches = re.findall(quote_pattern, product_name_input)
|
37 |
-
|
38 |
-
if matches:
|
39 |
-
# Return the first quoted string found
|
40 |
-
return matches[0]
|
41 |
-
|
42 |
-
# If no quotes but contains "llamado" or similar phrases, extract what follows
|
43 |
-
called_patterns = [
|
44 |
-
r'llamado\s+(.+?)(?:\s+que|\s+con|\s+para|\.$|$)',
|
45 |
-
r'titulado\s+(.+?)(?:\s+que|\s+con|\s+para|\.$|$)',
|
46 |
-
r'denominado\s+(.+?)(?:\s+que|\s+con|\s+para|\.$|$)',
|
47 |
-
r'nombrado\s+(.+?)(?:\s+que|\s+con|\s+para|\.$|$)'
|
48 |
-
]
|
49 |
-
|
50 |
-
for pattern in called_patterns:
|
51 |
-
matches = re.search(pattern, product_name_input, re.IGNORECASE)
|
52 |
-
if matches:
|
53 |
-
extracted = matches.group(1).strip()
|
54 |
-
# If the extracted text has quotes, remove them
|
55 |
-
if extracted.startswith('"') and extracted.endswith('"'):
|
56 |
-
extracted = extracted[1:-1]
|
57 |
-
return extracted
|
58 |
-
|
59 |
-
# Check if the input is generic (common course/product types without specific names)
|
60 |
-
generic_patterns = [
|
61 |
-
r'^(curso|taller|programa|webinar|entrenamiento|sistema|método|servicio|producto|aplicación|comunidad|masterclass)(\s+de\s+.+)?$',
|
62 |
-
r'^(un|el|mi|nuestro)\s+(curso|taller|programa|webinar|entrenamiento|sistema|método|servicio|producto|aplicación|comunidad|masterclass)(\s+de\s+.+)?$'
|
63 |
-
]
|
64 |
-
|
65 |
-
for pattern in generic_patterns:
|
66 |
-
if re.match(pattern, product_name_input.lower(), re.IGNORECASE):
|
67 |
-
# This is a generic description, return empty string to trigger creative name generation
|
68 |
-
return ""
|
69 |
-
|
70 |
-
# If no patterns match, return the original input
|
71 |
-
return product_name_input.strip()
|
72 |
-
|
73 |
-
|
74 |
-
def create_offer_instruction(avatar_description, product_name, selected_formula_name):
|
75 |
-
"""
|
76 |
-
Creates instructions for generating an offer based on the selected formula.
|
77 |
-
|
78 |
-
Args:
|
79 |
-
avatar_description: Description of the target audience
|
80 |
-
product_name: Name of the product or service
|
81 |
-
selected_formula_name: Name of the formula to use
|
82 |
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
extracted_name = extract_product_name(product_name)
|
88 |
-
|
89 |
-
# Get the selected formula
|
90 |
-
selected_formula = offer_formulas[selected_formula_name]
|
91 |
-
|
92 |
-
# Add specific instructions for each formula
|
93 |
-
additional_instructions = ""
|
94 |
-
if selected_formula_name == "Fórmula Sueño-Obstáculo":
|
95 |
-
additional_instructions = """
|
96 |
-
SPECIFIC INSTRUCTIONS FOR THIS FORMULA:
|
97 |
-
1. PRODUCT/SERVICE NAME HANDLING:
|
98 |
-
- CRITICAL: If a product name is provided in quotes or after words like "llamado", "titulado", etc.,
|
99 |
-
YOU MUST USE THAT EXACT NAME. This is non-negotiable.
|
100 |
-
- The extracted product name is: "{extracted_name}"
|
101 |
-
- If this extracted name is not empty, use it EXACTLY as provided with no modifications
|
102 |
-
- Only create a new name if the extracted name is empty or contains generic placeholders
|
103 |
-
|
104 |
-
2. Analyze ALL available information:
|
105 |
-
- Product/service name (use the extracted name provided above)
|
106 |
-
- Target audience description (avatar_description)
|
107 |
-
- Content from uploaded files (if any)
|
108 |
-
|
109 |
-
3. Determine the most appropriate type (curso, webinar, entrenamiento, etc.) based on:
|
110 |
-
- Any type mentioned in product_name
|
111 |
-
- The nature of the solution described in avatar_description
|
112 |
-
- The most suitable format for the target audience's needs
|
113 |
-
|
114 |
-
4. Create a comprehensive offer by combining:
|
115 |
-
- The appropriate type (determined in step 3)
|
116 |
-
- The EXACT product name as extracted (if provided)
|
117 |
-
- A compelling dream based on avatar_description
|
118 |
-
- A relevant obstacle based on avatar_description
|
119 |
-
|
120 |
-
5. The dream should be ambitious but believable, incorporating:
|
121 |
-
- Target audience desires from avatar_description
|
122 |
-
- Explicit goals mentioned in uploaded content (if any)
|
123 |
-
|
124 |
-
6. The obstacle should reflect:
|
125 |
-
- Real problems mentioned in avatar_description
|
126 |
-
- Challenges that would normally prevent achieving the dream
|
127 |
-
|
128 |
-
7. IMPORTANT: Vary the way you start the phrase. Instead of always using "Se trata de un...", use different openings such as:
|
129 |
-
- "Presentamos un..."
|
130 |
-
- "Te ofrecemos un..."
|
131 |
-
- "Descubre nuestro..."
|
132 |
-
- "Conoce el..."
|
133 |
-
- "Hemos creado un..."
|
134 |
-
- "Imagina tener acceso a un..."
|
135 |
-
- "Por fin existe un..."
|
136 |
-
- "Ahora puedes acceder a un..."
|
137 |
-
- "Tenemos para ti un..."
|
138 |
-
- "Disfruta de un..."
|
139 |
-
""".format(extracted_name=extracted_name)
|
140 |
-
|
141 |
-
elif selected_formula_name == "Oferta Dorada":
|
142 |
-
additional_instructions = """
|
143 |
-
SPECIFIC INSTRUCTIONS FOR THIS FORMULA:
|
144 |
-
1. PRODUCT/SERVICE NAME HANDLING:
|
145 |
-
- CRITICAL: If a product name is provided in quotes or after words like "llamado", "titulado", etc.,
|
146 |
-
YOU MUST USE THAT EXACT NAME in one of the three lines of the offer.
|
147 |
-
- The extracted product name is: "{extracted_name}"
|
148 |
-
- If this extracted name is not empty, use it EXACTLY as provided with no modifications
|
149 |
-
- If no product name is provided or extracted, DO NOT create or add ANY name
|
150 |
-
- DO NOT invent names like "ProExpert", "Sistema Profesional", "método XYZ", etc.
|
151 |
-
- DO NOT add product names like "curso", "programa", etc. unless they were explicitly provided
|
152 |
-
- For the third line, use generic terms like "Con nuestra metodología", "Mediante este sistema",
|
153 |
-
"Gracias a este método", etc. when no specific name is provided
|
154 |
-
|
155 |
-
2. FOLLOW THE 6-STEP PROCESS FOR CREATING AN IRRESISTIBLE OFFER:
|
156 |
-
|
157 |
-
STEP 1: IDENTIFY THE MAIN PROBLEM (ATTENTION HOOK)
|
158 |
-
- Analyze the avatar_description DEEPLY to understand their specific pain points
|
159 |
-
- Create a powerful hook that directly addresses their biggest frustration or anxiety
|
160 |
-
- Use statements that make them think "this person understands my situation exactly"
|
161 |
-
- Focus on the emotional impact of their problem, not just the practical aspects
|
162 |
-
- The hook must create an immediate emotional connection
|
163 |
-
- IMPORTANT: Be creative and varied in your approach
|
164 |
-
|
165 |
-
ATTENTION HOOK EXAMPLES FOR INSPIRATION:
|
166 |
-
- "El 83% de las personas..."
|
167 |
-
- "La mayoría de métodos para..."
|
168 |
-
- "Sé lo frustrante que..."
|
169 |
-
- "Imagina poder..."
|
170 |
-
- "Mientras otros..."
|
171 |
-
- "Lo que nadie te dice sobre..."
|
172 |
-
- "Hace unos años, yo también..."
|
173 |
-
- "¿Por qué sigues luchando con...?"
|
174 |
-
- "Si estás cansado de..."
|
175 |
-
- Use statistics, shocking revelations, direct statements or questions
|
176 |
-
- The hook must create an immediate emotional connection
|
177 |
-
- IMPORTANT: Vary your opening approach using different formats:
|
178 |
-
* Direct question formats (use variety, not just "¿Estás cansado de...?"):
|
179 |
-
- Problem-focused: "¿Por qué sigues luchando con...?"
|
180 |
-
- Contrast: "¿Qué pasaría si pudieras...?"
|
181 |
-
- Challenge: "¿Te has preguntado por qué no consigues...?"
|
182 |
-
- Future-oriented: "¿Imaginas cómo sería tu vida si...?"
|
183 |
-
- Reflection: "¿Cuántas veces has intentado...?"
|
184 |
-
- Provocative: "¿Y si te dijera que el problema no es...?"
|
185 |
-
- Empathetic: "¿También sientes que...?"
|
186 |
-
- Curiosity: "¿Sabías que el 78% de las personas...?"
|
187 |
-
* Shocking statistic: "El 83% de las personas..."
|
188 |
-
* Bold statement: "La mayoría de métodos para..."
|
189 |
-
* Empathetic observation: "Sé lo frustrante que..."
|
190 |
-
* Challenge: "Imagina poder..."
|
191 |
-
* Contrast: "Mientras otros..."
|
192 |
-
* Revelation: "Lo que nadie te dice sobre..."
|
193 |
-
* Story opener: "Hace unos años, yo también..."
|
194 |
-
* Avoid always starting with "Si [problema]..." - use this format only 20% of the time
|
195 |
-
|
196 |
-
STEP 2: CRAFT A QUANTIFIABLE VALUE PROMISE
|
197 |
-
- Write a promise COMPLETELY IN CAPITAL LETTERS that includes:
|
198 |
-
* Concrete numbers (money, time, results)
|
199 |
-
* Powerful action verbs (EARN, MULTIPLY, ACHIEVE, MASTER)
|
200 |
-
* Specific timeframes (EN 30 DÍAS, EN SOLO 2 SEMANAS)
|
201 |
-
* Clear effort indicators (CON SOLO 15 MINUTOS DIARIOS)
|
202 |
-
- Follow this structure: CÓMO [LOGRAR RESULTADO DESEADO] SIN [OBJECIÓN O CREENCIA LIMITANTE]
|
203 |
-
- NEVER use exclamation marks (!) in this section
|
204 |
-
- Make the promise both ambitious and believable
|
205 |
-
- AVOID GENERIC PHRASES like "al otro nivel", "en tiempo récord", "como nunca antes", "revolucionario"
|
206 |
-
- Instead, use SPECIFIC, MEASURABLE language that clearly states the exact benefit
|
207 |
-
|
208 |
-
STEP 3: DEMONSTRATE TRUST AND AUTHORITY
|
209 |
-
- Include elements that prove your system works:
|
210 |
-
* Personal results ("Así como he vendido más de $250,000 USD")
|
211 |
-
* Client results ("Mis clientes han vendido más de $5,000,000")
|
212 |
-
* Social proof (testimonials, case studies)
|
213 |
-
- Establish credibility through specific numbers and verifiable claims
|
214 |
-
- Connect your authority directly to the promised result
|
215 |
-
|
216 |
-
STEP 4: REDUCE DELIVERY TIME
|
217 |
-
- Clearly state how quickly they will see results
|
218 |
-
- Make the timeframe specific and believable
|
219 |
-
- Emphasize speed without sacrificing quality
|
220 |
-
- Use phrases like "en solo X días" or "desde la primera semana"
|
221 |
-
- IMPORTANT: This refers to when they'll see RESULTS, not how long they need to work
|
222 |
-
- Examples: "verás resultados en 30 días", "transformación completa en 8 semanas"
|
223 |
-
|
224 |
-
STEP 5: MINIMIZE CLIENT EFFORT
|
225 |
-
- Address the fear of complicated processes
|
226 |
-
- CLEARLY specify the exact effort required from the client:
|
227 |
-
* Time commitment: "con solo 15 minutos al día"
|
228 |
-
* Frequency: "practicando 3 veces por semana"
|
229 |
-
* Complexity: "siguiendo 5 pasos sencillos"
|
230 |
-
* Prerequisites: "sin necesidad de experiencia previa"
|
231 |
-
- Make the implementation process seem accessible and straightforward
|
232 |
-
- Emphasize the low effort-to-results ratio
|
233 |
-
- Examples: "dedicando solo 20 minutos diarios", "con 3 ejercicios simples"
|
234 |
-
|
235 |
-
STEP 6: COMBINE ALL ELEMENTS INTO A COHESIVE OFFER
|
236 |
-
- Ensure all elements address the SAME core problem
|
237 |
-
- Create a LOGICAL PROGRESSION from problem to solution to implementation
|
238 |
-
- Maintain thematic consistency throughout the entire offer
|
239 |
-
- Be SPECIFIC and CONCRETE - avoid generic superlatives and clichés
|
240 |
-
- Use PRECISE language that creates a clear mental image
|
241 |
-
- BANNED PHRASES: "al otro nivel", "en tiempo récord", "como nunca antes",
|
242 |
-
"revolucionario", "increíble", "asombroso", "el mejor", "único"
|
243 |
-
|
244 |
-
3. THIRD LINE EXAMPLES FOR INSPIRATION:
|
245 |
-
- "Usando nuestra metodología comprobada lograrás desenvolverte con confianza en reuniones y entrevistas. Cientos de profesionales han mejorado su comunicación en 8 semanas siguiendo este sistema."
|
246 |
-
- "Con nuestro sistema de optimización de negocios alcanzarás la rentabilidad que 327 emprendedores ya disfrutan en menos de un trimestre."
|
247 |
-
- "Gracias a nuestro método de reprogramación metabólica activarás tu quema natural de grasa, avalado por estudios clínicos con 94% de efectividad en 30 días."
|
248 |
-
- "Mediante nuestra estrategia de contenido de alto impacto transformarás tu presencia en redes en un canal de ventas automático en menos de 30 días."
|
249 |
-
- "El Sistema de activación bioenergética optimizará tu rendimiento físico y mental desde la primera semana, usado por los CEOs más exitosos del mundo."
|
250 |
-
- "Nuestro programa de ventas automatizadas te permitirá generar ingresos mientras duermes, como ya lo hacen más de 200 emprendedores, en menos de 60 días."
|
251 |
-
- "A través de nuestro método de copywriting persuasivo duplicarás tus conversiones, comprobado por más de 150 negocios online, dedicando solo 30 minutos al día."
|
252 |
-
""".format(extracted_name=extracted_name)
|
253 |
-
|
254 |
-
# Create the instruction using the system prompt at the beginning
|
255 |
-
instruction = f"""{offer_system_prompt}
|
256 |
-
|
257 |
-
FORMULA TO USE:
|
258 |
-
{selected_formula["description"]}
|
259 |
-
|
260 |
-
{additional_instructions}
|
261 |
-
|
262 |
-
PRODUCT/SERVICE:
|
263 |
-
{product_name}
|
264 |
-
|
265 |
-
TARGET AUDIENCE:
|
266 |
-
{avatar_description}
|
267 |
-
|
268 |
-
Create a compelling offer following the formula structure exactly.
|
269 |
-
"""
|
270 |
-
|
271 |
-
# Add examples if available
|
272 |
-
if selected_formula.get("examples") and len(selected_formula["examples"]) > 0:
|
273 |
-
examples_text = "\n\n".join([f"Example {i+1}:\n{example}" for i, example in enumerate(selected_formula["examples"][:3])])
|
274 |
-
instruction += f"\n\nGet inspired by these examples:\n{examples_text}"
|
275 |
-
|
276 |
-
return instruction
|
277 |
-
|
278 |
-
def generate_complete_offer(avatar_description, product_name, selected_formula_name, include_bonuses=True):
|
279 |
-
"""
|
280 |
-
Generates a complete offer including the main offer and optional bonuses.
|
281 |
-
|
282 |
-
Args:
|
283 |
-
avatar_description: Description of the target audience
|
284 |
-
product_name: Name of the product or service
|
285 |
-
selected_formula_name: Name of the formula to use
|
286 |
-
include_bonuses: Whether to include bonuses in the offer
|
287 |
|
288 |
-
|
289 |
-
|
290 |
-
"""
|
291 |
-
# Create main offer instruction
|
292 |
-
main_offer_instruction = create_offer_instruction(avatar_description, product_name, selected_formula_name)
|
293 |
-
|
294 |
-
# Create bonus instruction if requested
|
295 |
-
bonus_instruction = None
|
296 |
-
if include_bonuses:
|
297 |
-
# Import the bonus generator from the new module
|
298 |
-
from bonuses.generator import create_bonus_instruction
|
299 |
-
bonus_instruction = create_bonus_instruction(avatar_description, product_name, selected_formula_name)
|
300 |
-
|
301 |
-
return {
|
302 |
-
"main_offer_instruction": main_offer_instruction,
|
303 |
-
"bonus_instruction": bonus_instruction
|
304 |
-
}
|
305 |
-
|
306 |
-
# The rest of your offer_formulas dictionary remains unchanged
|
307 |
-
offer_formulas = {
|
308 |
-
"Oferta Dorada": {
|
309 |
-
"description": """
|
310 |
-
Formula: [Attention Hook + QUANTIFIABLE PROMISE IN ALL CAPS + Benefit + Authority + Time or Effort]
|
311 |
-
|
312 |
-
This formula is designed to speak directly to the avatar, capturing their attention immediately, reflecting their current situation, and showing the transformation they desire.
|
313 |
-
|
314 |
-
### **How to apply it?**
|
315 |
-
|
316 |
-
#### 1 **Attention Hook**
|
317 |
-
The first step is to capture the avatar's attention can be a shocking revelation, an unexpected question, or a dramatic fact. IMPORTANT: CUSTOMIZE THE HOOK BASED ON THE AVATAR AND THEIR SPECIFIC PROBLEMS. Don't use generic examples, but adapt them to the client's situation.
|
318 |
-
|
319 |
-
Analyze first:
|
320 |
-
- What is the avatar's biggest pain or frustration?
|
321 |
-
- What are they trying to achieve without success?
|
322 |
-
- What limiting beliefs do they have?
|
323 |
-
|
324 |
-
---
|
325 |
-
|
326 |
-
#### 2 **QUANTIFIABLE PROMISE IN ALL CAPS**
|
327 |
-
This is the most important part. You must create a specific, quantifiable promise written COMPLETELY IN CAPITAL LETTERS that immediately captures attention. It must include:
|
328 |
-
- Concrete numbers (money, time, results)
|
329 |
-
- Powerful action verbs (EARN, MULTIPLY, ACHIEVE, MASTER)
|
330 |
-
- The specific result they will obtain
|
331 |
-
- Optionally, the time or effort required
|
332 |
-
- IMPORTANT: DO NOT USE EXCLAMATION MARKS (!) IN THIS SECTION UNDER ANY CIRCUMSTANCES
|
333 |
-
|
334 |
-
**Incorrect example:**
|
335 |
-
"Improve your sales with our system." (Vague, no numbers, no impact).
|
336 |
-
"¡MULTIPLY YOUR SALES IN RECORD TIME!" (Uses exclamation marks, NEVER use them).
|
337 |
-
|
338 |
-
**Correct example:**
|
339 |
-
"FACTURA MAS DE $1.000 USD USANDO 15 EMAILS ESCRITOS EN 15 MINUTOS CADA UNO" (Specific, quantifiable, impactful).
|
340 |
-
"MULTIPLICA POR 3 TUS INTERACCIONES EN REDES SOCIALES EN SOLO 2 SEMANAS" (Clear, measurable, with defined time).
|
341 |
-
|
342 |
-
---
|
343 |
-
|
344 |
-
#### 3 **Benefit + Authority + Time or Effort**
|
345 |
-
In this part, we explain the result they will obtain, supported by an authority factor (proven method, studies, experience, validations) and establishing a time frame or necessary effort to achieve it.
|
346 |
-
|
347 |
-
**Incorrect example:**
|
348 |
-
"Grow your business with our strategy." (Doesn't say how long it will take or how reliable the strategy is).
|
349 |
-
|
350 |
-
**Correct examples:**
|
351 |
-
"El Sistema de emails persuasivos para que puedas convertir lectores en clientes con lo que multiplicarás tus ventas en solo 30 días."
|
352 |
-
"Con nuestra metodología de copywriting podrás crear ofertas irresistibles permitiéndote aumentar tu tasa de conversión en un 200% con solo 15 minutos al día."
|
353 |
-
"Gracias a nuestro framework de contenido lograrás posicionarte como autoridad haciendo que tu audiencia te busque a ti en lugar de a tu competencia."
|
354 |
-
"Mediante nuestro sistema de automatización conseguirás generar ventas mientras duermes lo que significa libertad financiera real en menos de 90 días."
|
355 |
-
"Usando nuestra estrategia de redes sociales alcanzarás 10.000 seguidores cualificados transformando tu presencia digital en una máquina de generación de leads."
|
356 |
-
|
357 |
-
---
|
358 |
-
|
359 |
-
### **Fixed structure:**
|
360 |
-
"[Varied Attention Hook]
|
361 |
-
|
362 |
-
[QUANTIFIABLE PROMISE IN ALL CAPS]
|
363 |
-
|
364 |
-
[Choose one of the 5 structure formats for Benefit + Authority + Time or Effort]"
|
365 |
-
""",
|
366 |
-
"examples": [
|
367 |
-
# Example 1 - Question format (updated with more variety)
|
368 |
-
"""¿Por qué sigues perdiendo oportunidades de venta a pesar de tener prospectos interesados?
|
369 |
-
|
370 |
-
DESCUBRE CÓMO VENDER CON UNA SOLA LLAMADA PRODUCTOS DE MÁS DE $1,000 USD SIN MANIPULAR, MENTIR O FORZAR LA VENTA.
|
371 |
-
|
372 |
-
Con nuestro proceso de venta persuasiva lograrás convertir objeciones en ventas, como ya han hecho más de 500 emprendedores en menos de 30 días.""",
|
373 |
-
|
374 |
-
# Example 2 - Statistic format
|
375 |
-
"""El 78% de los emprendedores luchan cada mes para llegar a fin de mes, ¿eres uno de ellos?
|
376 |
-
|
377 |
-
MULTIPLICA TUS INGRESOS POR 3 EN LOS PRÓXIMOS 90 DÍAS SIN TRABAJAR MÁS HORAS NI CONTRATAR PERSONAL ADICIONAL.
|
378 |
-
|
379 |
-
Mediante nuestro sistema de optimización de negocios alcanzarás la rentabilidad que 327 emprendedores ya disfrutan en menos de un trimestre.""",
|
380 |
-
|
381 |
-
# Example 3 - Bold statement format with clear time/effort distinction
|
382 |
-
"""La mayoría de dietas fallan porque atacan el síntoma y no la causa real del sobrepeso.
|
383 |
-
|
384 |
-
PIERDE ENTRE 5 Y 8 KILOS EN 30 DÍAS SIN DIETAS RESTRICTIVAS, SIN PASAR HAMBRE Y SIN EFECTO REBOTE.
|
385 |
-
|
386 |
-
Gracias a nuestro método de reprogramación metabólica activarás tu quema natural de grasa, avalado por estudios clínicos con 94% de efectividad. Verás los primeros resultados en solo 7 días, dedicando apenas 15 minutos diarios a los ejercicios metabólicos.""",
|
387 |
-
|
388 |
-
# Example 4 - Contrast format
|
389 |
-
"""Mientras otros influencers consiguen miles de seguidores cada semana, tú sigues creando contenido que nadie ve.
|
390 |
-
|
391 |
-
CONSIGUE 100 NUEVOS SEGUIDORES CUALIFICADOS POR SEMANA Y CONVIERTE EL 10% EN CLIENTES PAGANDO CON SOLO 3 PUBLICACIONES SEMANALES.
|
392 |
-
|
393 |
-
Usando nuestra estrategia de contenido de alto impacto transformarás tu presencia en redes en un canal de ventas automático en menos de 30 días.""",
|
394 |
-
|
395 |
-
# Example 5 - Empathetic format
|
396 |
-
"""Sé lo frustrante que es despertarse sin energía día tras día, sintiendo que nunca hay suficientes horas.
|
397 |
-
|
398 |
-
AUMENTA TU ENERGÍA UN 65% Y RECUPERA 2 HORAS PRODUCTIVAS AL DÍA CON SOLO 15 MINUTOS DE RUTINA MATUTINA.
|
399 |
-
|
400 |
-
El Sistema de activación bioenergética optimizará tu rendimiento físico y mental desde la primera semana, usado por los CEOs más exitosos del mundo."""
|
401 |
-
]
|
402 |
-
},
|
403 |
-
"Fórmula Sueño-Obstáculo": {
|
404 |
-
"description": """
|
405 |
-
Formula: [Type + Name + Dream + Obstacle]
|
406 |
-
|
407 |
-
This formula connects directly with the client's desires and concerns:
|
408 |
-
|
409 |
-
1. Type: The type of solution (training, product, or service)
|
410 |
-
2. Name: The name of your solution
|
411 |
-
3. Dream: The big dream or result that the client wants to achieve
|
412 |
-
4. Obstacle: The obstacle that would normally prevent achieving that dream
|
413 |
-
|
414 |
-
**Suggested solution types:**
|
415 |
-
- Online course
|
416 |
-
- Webinar
|
417 |
-
- Training
|
418 |
-
- Program
|
419 |
-
- Workshop
|
420 |
-
- Mentorship
|
421 |
-
- Consulting
|
422 |
-
- Membership
|
423 |
-
- System
|
424 |
-
- Method
|
425 |
-
- Service
|
426 |
-
- Product
|
427 |
-
- Application
|
428 |
-
- Community
|
429 |
-
- Masterclass
|
430 |
-
|
431 |
-
**Suggested opening variations:**
|
432 |
-
- "Se trata de un..."
|
433 |
-
- "Presentamos un..."
|
434 |
-
- "Te ofrecemos un..."
|
435 |
-
- "Descubre nuestro..."
|
436 |
-
- "Conoce el..."
|
437 |
-
- "Hemos creado un..."
|
438 |
-
- "Imagina tener acceso a un..."
|
439 |
-
- "Por fin existe un..."
|
440 |
-
- "Ahora puedes acceder a un..."
|
441 |
-
- "Tenemos para ti un..."
|
442 |
-
- "Disfruta de un..."
|
443 |
-
|
444 |
-
**Structure Format (Classic example):**
|
445 |
-
"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)"
|
446 |
-
""",
|
447 |
-
"examples": [
|
448 |
-
# Example 1 - Sales Training
|
449 |
-
"""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.""",
|
450 |
-
|
451 |
-
# Example 2 - Fitness Program
|
452 |
-
"""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.""",
|
453 |
|
454 |
-
|
455 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
456 |
|
457 |
-
#
|
458 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
459 |
|
460 |
-
#
|
461 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
462 |
|
463 |
-
#
|
464 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
465 |
|
466 |
-
#
|
467 |
-
|
468 |
-
|
469 |
-
|
470 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
+
|
18 |
+
# Set page to wide mode to use full width
|
19 |
+
st.set_page_config(layout="wide")
|
20 |
+
|
21 |
+
# Load environment variables
|
22 |
+
load_dotenv()
|
23 |
+
|
24 |
+
# Configure Google Gemini API
|
25 |
+
genai.configure(api_key=os.getenv('GOOGLE_API_KEY'))
|
26 |
+
model = genai.GenerativeModel('gemini-2.0-flash')
|
27 |
+
|
28 |
+
# Import the create_offer_instruction function from formulas
|
29 |
+
from formulas import create_offer_instruction, offer_formulas
|
30 |
+
|
31 |
+
# Initialize session state variables if they don't exist
|
32 |
+
if 'submitted' not in st.session_state:
|
33 |
+
st.session_state.submitted = False
|
34 |
+
if 'offer_result' not in st.session_state:
|
35 |
+
st.session_state.offer_result = ""
|
36 |
+
if 'generated' not in st.session_state:
|
37 |
+
st.session_state.generated = False
|
38 |
+
|
39 |
+
# Hide Streamlit menu and footer
|
40 |
+
st.markdown("""
|
41 |
+
<style>
|
42 |
+
#MainMenu {visibility: hidden;}
|
43 |
+
footer {visibility: hidden;}
|
44 |
+
header {visibility: hidden;}
|
45 |
+
</style>
|
46 |
+
""", unsafe_allow_html=True)
|
47 |
+
|
48 |
+
# Custom CSS
|
49 |
+
st.markdown(get_custom_css(), unsafe_allow_html=True)
|
50 |
+
|
51 |
+
# App title and description
|
52 |
+
st.markdown('<h1 style="text-align: center;">Great Offer Generator</h1>', unsafe_allow_html=True)
|
53 |
+
st.markdown('<h3 style="text-align: center;">Transform your skills into compelling offers!</h3>', unsafe_allow_html=True)
|
54 |
+
|
55 |
+
# Create two columns for layout - left column 40%, right column 60%
|
56 |
+
col1, col2 = st.columns([4, 6])
|
57 |
+
|
58 |
+
# Main input section in left column
|
59 |
+
with col1:
|
60 |
+
# Define the generate_offer function first
|
61 |
+
def handle_generate_button(): # Renamed to avoid conflict
|
62 |
+
has_manual_input = bool(skills or product_service)
|
63 |
+
has_file_input = bool(uploaded_file is not None and not is_image)
|
64 |
+
has_image_input = bool(uploaded_file is not None and is_image)
|
65 |
+
|
66 |
+
# Simple validation - check if we have at least one input type
|
67 |
+
if not (has_manual_input or has_file_input or has_image_input):
|
68 |
+
st.error('Por favor ingresa texto o sube un archivo/imagen')
|
69 |
+
return
|
70 |
|
71 |
+
st.session_state.submitted = True
|
72 |
+
st.session_state.generated = False # Reset generated flag
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
73 |
|
74 |
+
# Store inputs based on what's available
|
75 |
+
if has_manual_input:
|
76 |
+
st.session_state.skills = skills if skills else ""
|
77 |
+
st.session_state.product_service = product_service if product_service else ""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
78 |
|
79 |
+
if has_file_input:
|
80 |
+
st.session_state.file_content = file_content
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
81 |
|
82 |
+
if has_image_input:
|
83 |
+
st.session_state.image_parts = image_parts
|
84 |
+
|
85 |
+
# Set input type based on what's available
|
86 |
+
if has_image_input:
|
87 |
+
if has_manual_input:
|
88 |
+
st.session_state.input_type = "manual_image"
|
89 |
+
else:
|
90 |
+
st.session_state.input_type = "image"
|
91 |
+
else:
|
92 |
+
if has_manual_input and has_file_input:
|
93 |
+
st.session_state.input_type = "both"
|
94 |
+
elif has_file_input:
|
95 |
+
st.session_state.input_type = "file"
|
96 |
+
elif has_manual_input:
|
97 |
+
st.session_state.input_type = "manual"
|
98 |
+
|
99 |
+
# Store common settings
|
100 |
+
st.session_state.target_audience = target_audience
|
101 |
+
st.session_state.temperature = temperature
|
102 |
+
st.session_state.formula_type = formula_type
|
103 |
+
|
104 |
+
# Keep only the manual input tab
|
105 |
+
with st.container():
|
106 |
+
skills = st.text_area('💪 Tus Habilidades', height=70,
|
107 |
+
help='Lista tus habilidades y experiencia clave')
|
108 |
+
product_service = st.text_area('🎯 Producto/Servicio', height=70,
|
109 |
+
help='Describe tu producto o servicio')
|
110 |
+
|
111 |
+
# Generate button moved here - right after product/service
|
112 |
+
st.button('Generar Oferta 🎉', on_click=handle_generate_button) # Updated function name
|
113 |
+
|
114 |
+
# Accordion for additional settings
|
115 |
+
with st.expander('⚙️ Configuración Avanzada'):
|
116 |
+
target_audience = st.text_area('👥 Público Objetivo', height=70,
|
117 |
+
help='Describe tu cliente o público ideal')
|
118 |
+
|
119 |
+
# Add file/image uploader here
|
120 |
+
uploaded_file = st.file_uploader("📄 Sube un archivo o imagen",
|
121 |
+
type=['txt', 'pdf', 'docx', 'jpg', 'jpeg', 'png'])
|
122 |
+
|
123 |
+
if uploaded_file is not None:
|
124 |
+
file_type = uploaded_file.name.split('.')[-1].lower()
|
125 |
|
126 |
+
# Handle text files
|
127 |
+
if file_type in ['txt', 'pdf', 'docx']:
|
128 |
+
if file_type == 'txt':
|
129 |
+
try:
|
130 |
+
file_content = uploaded_file.read().decode('utf-8')
|
131 |
+
except Exception as e:
|
132 |
+
st.error(f"Error al leer el archivo TXT: {str(e)}")
|
133 |
+
file_content = ""
|
134 |
+
|
135 |
+
elif file_type == 'pdf':
|
136 |
+
try:
|
137 |
+
import PyPDF2
|
138 |
+
pdf_reader = PyPDF2.PdfReader(uploaded_file)
|
139 |
+
file_content = ""
|
140 |
+
for page in pdf_reader.pages:
|
141 |
+
file_content += page.extract_text() + "\n"
|
142 |
+
except Exception as e:
|
143 |
+
st.error(f"Error al leer el archivo PDF: {str(e)}")
|
144 |
+
file_content = ""
|
145 |
+
|
146 |
+
elif file_type == 'docx':
|
147 |
+
try:
|
148 |
+
import docx
|
149 |
+
doc = docx.Document(uploaded_file)
|
150 |
+
file_content = "\n".join([para.text for para in doc.paragraphs])
|
151 |
+
except Exception as e:
|
152 |
+
st.error(f"Error al leer el archivo DOCX: {str(e)}")
|
153 |
+
file_content = ""
|
154 |
+
|
155 |
+
# Remove success message - no notification shown
|
156 |
+
|
157 |
+
# Set file type flag
|
158 |
+
is_image = False
|
159 |
|
160 |
+
# Handle image files
|
161 |
+
elif file_type in ['jpg', 'jpeg', 'png']:
|
162 |
+
try:
|
163 |
+
image = Image.open(uploaded_file)
|
164 |
+
st.image(image, caption="Imagen cargada", use_container_width=True)
|
165 |
+
|
166 |
+
image_bytes = uploaded_file.getvalue()
|
167 |
+
image_parts = [
|
168 |
+
{
|
169 |
+
"mime_type": uploaded_file.type,
|
170 |
+
"data": image_bytes
|
171 |
+
}
|
172 |
+
]
|
173 |
+
|
174 |
+
# Set file type flag
|
175 |
+
is_image = True
|
176 |
+
except Exception as e:
|
177 |
+
st.error(f"Error al procesar la imagen: {str(e)}")
|
178 |
+
is_image = False
|
179 |
+
|
180 |
+
# Selector de fórmula
|
181 |
+
formula_type = st.selectbox(
|
182 |
+
'📋 Tipo de Fórmula',
|
183 |
+
options=list(offer_formulas.keys()),
|
184 |
+
help='Selecciona el tipo de fórmula para tu oferta'
|
185 |
+
)
|
186 |
+
|
187 |
+
temperature = st.slider('🌡️ Nivel de Creatividad', min_value=0.0, max_value=2.0, value=1.0,
|
188 |
+
help='Valores más altos hacen que el resultado sea más creativo pero menos enfocado')
|
189 |
+
|
190 |
+
# Results column
|
191 |
+
# In the section where you're generating the offer
|
192 |
+
with col2:
|
193 |
+
if st.session_state.submitted and not st.session_state.generated:
|
194 |
+
with st.spinner('Creando tu oferta perfecta...'):
|
195 |
+
# Use the create_offer_instruction function to generate the prompt
|
196 |
+
avatar_description = st.session_state.target_audience if hasattr(st.session_state, 'target_audience') and st.session_state.target_audience else 'General audience'
|
197 |
|
198 |
+
# Determine product name based on input type
|
199 |
+
if hasattr(st.session_state, 'product_service') and st.session_state.product_service:
|
200 |
+
product_name = st.session_state.product_service
|
201 |
+
else:
|
202 |
+
# Generar un nombre creativo en lugar de usar "Producto/Servicio"
|
203 |
+
try:
|
204 |
+
creative_name_prompt = f"""Crea un nombre creativo y atractivo para un producto o servicio
|
205 |
+
basado en estas habilidades: {st.session_state.skills if hasattr(st.session_state, 'skills') else 'profesional'}.
|
206 |
+
El público objetivo es: {avatar_description}.
|
207 |
+
Responde SOLO con el nombre, sin explicaciones ni comillas."""
|
208 |
+
|
209 |
+
creative_name_response = model.generate_content(
|
210 |
+
creative_name_prompt,
|
211 |
+
generation_config=genai.GenerationConfig(temperature=0.8, max_output_tokens=30)
|
212 |
+
)
|
213 |
+
product_name = creative_name_response.text.strip().replace('"', '').replace("'", "")
|
214 |
+
|
215 |
+
# Si el nombre generado está vacío o es demasiado largo, usar un valor predeterminado
|
216 |
+
if not product_name or len(product_name) > 50:
|
217 |
+
product_name = "Sistema Profesional"
|
218 |
+
except Exception:
|
219 |
+
product_name = "Sistema Profesional"
|
220 |
+
|
221 |
+
# Get the instruction using the formula
|
222 |
+
instruction = create_offer_instruction(
|
223 |
+
avatar_description=avatar_description,
|
224 |
+
product_name=product_name,
|
225 |
+
selected_formula_name=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 |
+
# Eliminar cualquier formato que pueda causar recuadros
|
281 |
+
response_text = response_text.replace("```", "").replace("`", "")
|
282 |
+
|
283 |
+
# Eliminar posibles etiquetas HTML
|
284 |
+
response_text = response_text.replace("<div>", "").replace("</div>", "")
|
285 |
+
response_text = response_text.replace("<p>", "").replace("</p>", "")
|
286 |
+
response_text = response_text.replace("<h1>", "").replace("</h1>", "")
|
287 |
+
response_text = response_text.replace("<h2>", "").replace("</h2>", "")
|
288 |
+
response_text = response_text.replace("<h3>", "").replace("</h3>", "")
|
289 |
+
response_text = response_text.replace("<strong>", "").replace("</strong>", "")
|
290 |
+
|
291 |
+
# Eliminar caracteres que puedan causar problemas de formato
|
292 |
+
response_text = response_text.replace("#", "")
|
293 |
+
response_text = response_text.replace("*", "")
|
294 |
+
|
295 |
+
# Eliminar espacios al inicio de cada línea (indentación)
|
296 |
+
lines = response_text.split('\n')
|
297 |
+
lines = [line.lstrip() for line in lines] # Elimina espacios al inicio de cada línea
|
298 |
+
|
299 |
+
# Asegurarse de que no haya líneas en blanco extras que puedan afectar el formato
|
300 |
+
lines = [line for line in lines if line.strip()]
|
301 |
+
response_text = '\n\n'.join(lines)
|
302 |
+
|
303 |
+
# Natural integration of product name in the response
|
304 |
+
if hasattr(st.session_state, 'product_service') and st.session_state.product_service:
|
305 |
+
product_name = st.session_state.product_service
|
306 |
+
|
307 |
+
# Split the text into lines to process each part of the formula
|
308 |
+
lines = response_text.split('\n')
|
309 |
+
|
310 |
+
# Process each line for proper product name integration
|
311 |
+
for i in range(len(lines)):
|
312 |
+
# For the second line (usually the promise in ALL CAPS), make product name uppercase
|
313 |
+
if i == 1 and st.session_state.formula_type == "Oferta Dorada":
|
314 |
+
lines[i] = lines[i].upper()
|
315 |
+
|
316 |
+
# Remove quotes around product name in all lines
|
317 |
+
lines[i] = lines[i].replace(f'"{product_name}"', product_name)
|
318 |
+
lines[i] = lines[i].replace(f"'{product_name}'", product_name)
|
319 |
+
|
320 |
+
# Ensure product name is properly capitalized in benefit descriptions
|
321 |
+
if i >= 2 and "sistema" in lines[i].lower() and product_name.lower() not in lines[i].lower():
|
322 |
+
lines[i] = lines[i].replace("sistema", product_name, 1)
|
323 |
+
if i >= 2 and "método" in lines[i].lower() and product_name.lower() not in lines[i].lower():
|
324 |
+
lines[i] = lines[i].replace("método", product_name, 1)
|
325 |
+
|
326 |
+
# Rejoin the lines
|
327 |
+
response_text = '\n'.join(lines)
|
328 |
+
|
329 |
+
st.session_state.offer_result = response_text
|
330 |
+
st.session_state.generated = True # Mark as generated
|
331 |
+
|
332 |
+
except Exception as e:
|
333 |
+
st.error(f'Ocurrió un error: {str(e)}')
|
334 |
+
st.session_state.submitted = False
|
335 |
+
|
336 |
+
# Display results if we have an offer result
|
337 |
+
if st.session_state.generated:
|
338 |
+
# Remove the visualization mode option
|
339 |
+
|
340 |
+
# Display the formatted result directly
|
341 |
+
st.markdown(get_response_html_wrapper(st.session_state.offer_result), unsafe_allow_html=True)
|
342 |
+
|
343 |
+
# Add a small space
|
344 |
+
st.markdown('<div style="height: 15px;"></div>', unsafe_allow_html=True)
|
345 |
+
|
346 |
+
# Apply the custom button style before rendering the download button
|
347 |
+
st.markdown('<style>div.stDownloadButton > button {your-custom-styles-here}</style>', unsafe_allow_html=True)
|
348 |
+
st.download_button(
|
349 |
+
label="Descargar Oferta",
|
350 |
+
data=st.session_state.offer_result,
|
351 |
+
file_name="oferta_generada.txt",
|
352 |
+
mime="text/plain"
|
353 |
+
)
|
354 |
+
|
355 |
+
# Footer
|
356 |
+
st.markdown('---')
|
357 |
+
st.markdown('Made with ❤️ by Jesús Cabrera')
|
358 |
+
|
359 |
+
# Remove the duplicate functions at the bottom
|