JeCabrera commited on
Commit
d01aa08
·
verified ·
1 Parent(s): 82a9601

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +250 -184
app.py CHANGED
@@ -1,184 +1,250 @@
1
- from dotenv import load_dotenv
2
- import streamlit as st
3
- import os
4
- import google.generativeai as genai
5
- from cta_formulas import cta_formulas
6
- from styles import apply_styles
7
- from tone_formulas import tone_settings
8
-
9
- # Cargar variables de entorno
10
- load_dotenv()
11
-
12
- # Configurar API de Google Gemini
13
- genai.configure(api_key=os.getenv("GOOGLE_API_KEY"))
14
-
15
- def get_gemini_response(product_service, target_audience, desired_action, formula_type, tone_type, temperature, postdata_theme=None, cta_count=5):
16
- if not product_service or not target_audience or not desired_action:
17
- return "Por favor, completa todos los campos requeridos."
18
-
19
- formula = cta_formulas[formula_type]
20
- tone = tone_settings[tone_type]
21
-
22
- # Lista de fórmulas que ya incluyen P.D. en su estructura
23
- formulas_with_pd = ["El Último Aviso (No tan Último)", "Cierra con Corazón", "Gancho Cachondo"]
24
-
25
- # Preparar información de postdata
26
- postdata_instruction = ""
27
- if postdata_theme:
28
- # Solo aplicar la instrucción de postdata si la fórmula tiene PD en su estructura
29
- if formula_type in formulas_with_pd:
30
- postdata_instruction = f"""
31
- POSTDATA THEME:
32
- Use the following theme for the P.S. section: {postdata_theme}
33
-
34
- IMPORTANT: For formulas with P.S.2 sections, use a DIFFERENT theme or approach than the one used in P.S.1.
35
- DO NOT repeat the same urgency factor, benefit, or discount information in both postdatas.
36
-
37
- Examples:
38
- - If P.S.1 mentions "only 2 days left", P.S.2 should NOT mention time limits again
39
- - If P.S.1 talks about "limited spots", P.S.2 should focus on a different benefit or feature
40
-
41
- Make sure each postdata adds unique value and persuasion elements.
42
- """
43
-
44
- model = genai.GenerativeModel('gemini-2.0-flash')
45
- full_prompt = f"""
46
- You are an expert copywriter specialized in creating persuasive Calls to Action (CTAs).
47
- Analyze (internally, don't include in output) the following information:
48
-
49
- BUSINESS INFORMATION:
50
- Product/Service: {product_service}
51
- Target Audience: {target_audience}
52
- Desired Action: {desired_action}
53
- CTA Type: {formula_type}
54
- Tone Style: {tone['style']}
55
- Keywords to consider: {', '.join(tone['keywords'])}
56
- {formula["description"]}
57
- {postdata_instruction}
58
-
59
- First, analyze (but don't show) these points:
60
- 1. TARGET AUDIENCE ANALYSIS:
61
- - What motivates them to take action?
62
- - What obstacles prevent them from acting?
63
- - What immediate benefits are they seeking?
64
- - What fears or doubts do they have?
65
- - What language and tone resonates with them?
66
-
67
- 2. PERSUASION ELEMENTS:
68
- - How to make the desired action more appealing?
69
- - What emotional triggers will resonate most?
70
- - How to create a sense of urgency naturally?
71
- - What unique value proposition to emphasize?
72
- - How to minimize perceived risk?
73
-
74
- Based on your internal analysis, create {cta_count} different CTAs following EXACTLY the formula structure:
75
- {formula["description"]}
76
-
77
- CRITICAL INSTRUCTIONS:
78
- - Follow the exact formula structure shown in the description above
79
- - Create {cta_count} different CTAs using the same formula pattern
80
- - ALL CTAs MUST BE IN SPANISH
81
- - DO NOT add postdata (P.S.) to formulas that don't include it in their structure
82
- - When a formula includes multiple postdatas (P.S.1 and P.S.2), make sure they focus on DIFFERENT themes and don't repeat the same urgency factors or benefits
83
-
84
- EXAMPLES TO FOLLOW:
85
- {formula["examples"]}
86
-
87
- Output EXACTLY in this format based on {formula_type}:
88
- 1. Follow format from {formula["examples"]}
89
-
90
- 2. Follow format from {formula["examples"]}
91
-
92
- 3. Follow format from {formula["examples"]}
93
- """
94
-
95
- response = model.generate_content([full_prompt], generation_config={"temperature": temperature})
96
- return response.parts[0].text if response and response.parts else "Error al generar contenido."
97
-
98
- # Configurar la aplicación Streamlit
99
- st.set_page_config(page_title="CTA Generator", page_icon="🎯", layout="wide")
100
-
101
- # Leer y mostrar el manual en el sidebar
102
- with open("manual.md", "r", encoding="utf-8") as file:
103
- manual_content = file.read()
104
- st.sidebar.markdown(manual_content)
105
-
106
- # Aplicar estilos
107
- st.markdown(apply_styles(), unsafe_allow_html=True)
108
-
109
- # Título de la app
110
- st.markdown("<h1>Generador de CTAs Persuasivos</h1>", unsafe_allow_html=True)
111
- st.markdown("<h3>Crea llamados a la acción que motiven a tu audiencia a dar el siguiente paso.</h3>", unsafe_allow_html=True)
112
-
113
- # Remove the duplicate manual expander from here
114
-
115
- # Crear dos columnas
116
- col1, col2 = st.columns([0.4, 0.6]) # 40% for left column, 60% for right column
117
-
118
- # Columna izquierda para inputs
119
- with col1:
120
- target_audience = st.text_area(
121
- "¿Cuál es tu público objetivo?",
122
- placeholder="Ejemplo: Emprendedores que buscan automatizar su negocio..."
123
- )
124
-
125
- product_service = st.text_area(
126
- "¿Cuál es tu producto o servicio?",
127
- placeholder="Ejemplo: Curso de automatización con IA, Software de gestión..."
128
- )
129
-
130
- desired_action = st.text_area(
131
- "¿Qué acción quieres que realicen?",
132
- placeholder="Ejemplo: Registrarse al webinar, Descargar la guía gratuita..."
133
- )
134
-
135
- # Mover el botón aquí, antes del acordeón
136
- generate_button = st.button("Generar CTAs")
137
-
138
- with st.expander("Opciones avanzadas"):
139
- formula_type = st.selectbox(
140
- "Tipo de CTA:",
141
- options=list(cta_formulas.keys())
142
- )
143
-
144
- tone_type = st.selectbox(
145
- "Tono del CTA:",
146
- options=list(tone_settings.keys()),
147
- )
148
-
149
- # Nuevos campos para postdata
150
- postdata_theme = st.text_input(
151
- "Tema o enfoque para la postdata",
152
- placeholder="Ejemplo: urgencia, beneficio, descuento"
153
- )
154
-
155
- cta_count = st.number_input(
156
- "Número de llamados a la acción",
157
- min_value=1,
158
- max_value=5,
159
- value=3
160
- )
161
-
162
- temperature = st.slider(
163
- "Nivel de creatividad:",
164
- min_value=0.0,
165
- max_value=2.0,
166
- value=1.0,
167
- step=0.1,
168
- help="Valores más altos generan CTAs más creativos pero menos predecibles."
169
- )
170
-
171
- # Columna derecha para resultados
172
- with col2:
173
- if generate_button and (response := get_gemini_response(
174
- product_service,
175
- target_audience,
176
- desired_action,
177
- formula_type,
178
- tone_type,
179
- temperature,
180
- postdata_theme,
181
- cta_count
182
- )):
183
- st.markdown("### Tus Llamados a la Acción")
184
- st.write(response)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from dotenv import load_dotenv
2
+ import streamlit as st
3
+ import os
4
+ import google.generativeai as genai
5
+ import random
6
+ from streamlit import session_state as state
7
+ from formulas import headline_formulas
8
+ from angles import angles
9
+
10
+ # Cargar las variables de entorno
11
+ load_dotenv()
12
+
13
+ # Configurar la API de Google
14
+ genai.configure(api_key=os.getenv("GOOGLE_API_KEY"))
15
+
16
+ # Fórmulas con ejemplos y explicaciones
17
+ # headline_formulas dictionary has been moved to formulas/headline_formulas.py
18
+
19
+ def generate_headlines(number_of_headlines, target_audience, product, temperature, selected_formula, selected_angle):
20
+ # Crear la configuración del modelo
21
+ generation_config = {
22
+ "temperature": temperature,
23
+ "top_p": 0.65,
24
+ "top_k": 360,
25
+ "max_output_tokens": 8196,
26
+ }
27
+
28
+ model = genai.GenerativeModel(
29
+ model_name="gemini-2.0-flash",
30
+ generation_config=generation_config,
31
+ )
32
+
33
+ # Angle dictionaries have been moved to angles/angle_data.py
34
+
35
+ # Incluir las instrucciones del sistema en el prompt principal
36
+ system_prompt = f"""You are a world-class copywriter, with expertise in crafting hooks, headlines, and subject lines that immediately capture the reader's attention, prompting them to open the email or continue reading.
37
+
38
+ FORMAT RULES:
39
+ - Each headline must start with number and period
40
+ - One headline per line
41
+ - No explanations or categories
42
+ - Add a line break between each headline
43
+ - Avoid unnecessary : symbols
44
+ - Each headline must be a complete and intriguing sentence
45
+
46
+ IMPORTANT ANGLE INSTRUCTIONS:
47
+ - The selected angle MUST be applied to EVERY headline
48
+ - The angle modifies HOW the formula is expressed, not its structure
49
+ - Think of the angle as a "tone overlay" on the formula
50
+ - The formula provides the structure, the angle provides the style
51
+ - Both must work together seamlessly
52
+
53
+ FORMAT EXAMPLE:
54
+ 1. Titular 1.
55
+
56
+ 2. Titular 2.
57
+
58
+ 3. Titular 3.
59
+
60
+ 4. Titular 4.
61
+
62
+ 5. Titular 5.
63
+
64
+ IMPORTANT:
65
+ - Each headline must be unique and memorable
66
+ - Avoid clichés and generalities
67
+ - Maintain an intriguing but credible tone
68
+ - Adapt speaking language from the audience
69
+ - Focus on transformative benefits
70
+ - Follow the selected angle style while maintaining formula structure"""
71
+
72
+ # Iniciar el prompt con las instrucciones del sistema
73
+ headlines_instruction = f"{system_prompt}\n\n"
74
+
75
+ # Añadir instrucciones de ángulo solo si no es "NINGUNO"
76
+ if selected_angle != "NINGUNO":
77
+ headlines_instruction += f"""
78
+ ÁNGULO PRINCIPAL: {selected_angle}
79
+ INSTRUCCIONES DE ÁNGULO ESPECÍFICAS:
80
+ {angles[selected_angle]["instruction"]}
81
+
82
+ IMPORTANTE: El ángulo {selected_angle} debe aplicarse como una "capa de estilo" sobre la estructura de la fórmula:
83
+ 1. Mantén la estructura base de la fórmula intacta
84
+ 2. Aplica el tono y estilo del ángulo {selected_angle}
85
+ 3. Asegura que cada elemento de la fórmula refleje el ángulo
86
+ 4. El ángulo afecta al "cómo" se dice, no al "qué" se dice
87
+
88
+ EJEMPLOS EXITOSOS DEL ÁNGULO {selected_angle}:
89
+ """
90
+ for example in angles[selected_angle]["examples"]:
91
+ headlines_instruction += f"- {example}\n"
92
+
93
+ headlines_instruction += (
94
+ f"\nTu tarea es crear {number_of_headlines} titulares irresistibles para {target_audience} "
95
+ f"que capturen la atención instantáneamente y generen curiosidad sobre {product}. "
96
+ )
97
+
98
+ if selected_angle != "NINGUNO":
99
+ headlines_instruction += f"IMPORTANTE: Cada titular DEBE seguir el ángulo {selected_angle} de manera clara y consistente.\n\n"
100
+
101
+ headlines_instruction += (
102
+ f"Evita menciones obvias de {product} y enfócate en despertar interés genuino"
103
+ )
104
+
105
+ if selected_angle != "NINGUNO":
106
+ headlines_instruction += f" usando el ángulo seleccionado"
107
+
108
+ headlines_instruction += ".\n\n"
109
+
110
+ headlines_instruction += (
111
+ f"IMPORTANTE: Estudia cuidadosamente estos ejemplos de la fórmula seleccionada. "
112
+ f"Cada ejemplo representa el estilo y estructura a seguir"
113
+ )
114
+
115
+ if selected_angle != "NINGUNO":
116
+ headlines_instruction += f", adaptados al ángulo {selected_angle}"
117
+
118
+ headlines_instruction += ":\n\n"
119
+
120
+ # Agregar 5 ejemplos aleatorios de la fórmula
121
+ random_examples = random.sample(selected_formula['examples'], min(5, len(selected_formula['examples'])))
122
+
123
+ headlines_instruction += "EJEMPLOS DE LA FÓRMULA A SEGUIR:\n"
124
+ for i, example in enumerate(random_examples, 1):
125
+ headlines_instruction += f"{i}. {example}\n"
126
+
127
+ headlines_instruction += "\nINSTRUCCIONES ESPECÍFICAS:\n"
128
+ headlines_instruction += "1. Mantén la misma estructura y longitud que los ejemplos anteriores\n"
129
+ headlines_instruction += "2. Usa el mismo tono y estilo de escritura\n"
130
+ headlines_instruction += "3. Replica los patrones de construcción de frases\n"
131
+ headlines_instruction += "4. Conserva el nivel de especificidad y detalle\n"
132
+ headlines_instruction += f"5. Adapta el contenido para {target_audience} manteniendo la esencia de los ejemplos\n\n"
133
+
134
+ headlines_instruction += f"FÓRMULA A SEGUIR:\n{selected_formula['description']}\n\n"
135
+
136
+ # CORRECTO (con indentación):
137
+ if selected_angle != "NINGUNO":
138
+ headlines_instruction += f"""
139
+ RECORDATORIO FINAL:
140
+ 1. Sigue la estructura de la fórmula seleccionada
141
+ 2. Aplica el ángulo como una "capa de estilo"
142
+ 3. Mantén la coherencia entre fórmula y ángulo
143
+ 4. Asegura que cada titular refleje ambos elementos
144
+
145
+ GENERA AHORA:
146
+ Crea {number_of_headlines} titulares que sigan fielmente el estilo y estructura de los ejemplos mostrados.
147
+ """
148
+ else:
149
+ headlines_instruction += f"""
150
+ GENERA AHORA:
151
+ Crea {number_of_headlines} titulares que sigan fielmente el estilo y estructura de los ejemplos mostrados.
152
+ """
153
+
154
+ # Enviar el mensaje al modelo (sin condiciones de imagen)
155
+ chat_session = model.start_chat(
156
+ history=[
157
+ {
158
+ "role": "user",
159
+ "parts": [headlines_instruction],
160
+ },
161
+ ]
162
+ )
163
+ response = chat_session.send_message("Genera los titulares siguiendo exactamente el estilo de los ejemplos mostrados.")
164
+
165
+ return response.text
166
+
167
+ # Configurar la interfaz de usuario con Streamlit
168
+ st.set_page_config(page_title="Enchanted Hooks", layout="wide")
169
+
170
+ # Leer el contenido del archivo manual.md
171
+ with open("manual.md", "r", encoding="utf-8") as file:
172
+ manual_content = file.read()
173
+
174
+ # Mostrar el contenido del manual en el sidebar
175
+ st.sidebar.markdown(manual_content)
176
+
177
+ # Load CSS from file
178
+ with open("styles/main.css", "r") as f:
179
+ css = f.read()
180
+
181
+ # Apply the CSS
182
+ st.markdown(f"<style>{css}</style>", unsafe_allow_html=True)
183
+
184
+ # Centrar el título y el subtítulo
185
+ st.markdown("<h1 style='text-align: center;'>Enchanted Hooks</h1>", unsafe_allow_html=True)
186
+ st.markdown("<h4 style='text-align: center;'>Imagina poder conjurar títulos que no solo informan, sino que encantan. Esta app es tu varita mágica en el mundo del copywriting, transformando cada concepto en un titular cautivador que deja a todos deseando más.</h4>", unsafe_allow_html=True)
187
+
188
+ # Crear columnas
189
+ col1, col2 = st.columns([1, 2])
190
+
191
+ # Columnas de entrada
192
+ with col1:
193
+ target_audience = st.text_input("¿Quién es tu público objetivo?", placeholder="Ejemplo: Estudiantes Universitarios")
194
+ product = st.text_input("¿Qué producto tienes en mente?", placeholder="Ejemplo: Curso de Inglés")
195
+ number_of_headlines = st.selectbox("Número de Titulares", options=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], index=4)
196
+
197
+ # Crear un único acordeón para fórmula, creatividad y ángulo
198
+ with st.expander("Personaliza tus titulares"):
199
+ temperature = st.slider("Creatividad", min_value=0.0, max_value=2.0, value=1.0, step=0.1)
200
+
201
+ selected_formula_key = st.selectbox(
202
+ "Selecciona una fórmula para tus titulares",
203
+ options=list(headline_formulas.keys())
204
+ )
205
+
206
+ # Automatically use the keys from the angles dictionary
207
+ # Make sure "NINGUNO" appears first, then the rest alphabetically
208
+ angle_keys = ["NINGUNO"] + sorted([key for key in angles.keys() if key != "NINGUNO"])
209
+ selected_angle = st.selectbox(
210
+ "Selecciona el ángulo para tus titulares",
211
+ options=angle_keys
212
+ )
213
+
214
+ selected_formula = headline_formulas[selected_formula_key]
215
+
216
+ # Botón de enviar
217
+ submit = st.button("Generar Titulares")
218
+
219
+ # Mostrar los titulares generados
220
+ if submit:
221
+ # Check if we have valid inputs
222
+ has_product = product.strip() != ""
223
+ has_audience = target_audience.strip() != ""
224
+
225
+ # Valid combination: Product + Audience
226
+ valid_inputs = has_product and has_audience
227
+
228
+ if valid_inputs and selected_formula:
229
+ try:
230
+ generated_headlines = generate_headlines(
231
+ number_of_headlines,
232
+ target_audience,
233
+ product,
234
+ temperature,
235
+ selected_formula,
236
+ selected_angle
237
+ )
238
+ col2.markdown(f"""
239
+ <div class="results-container">
240
+ <h4>Observa la magia en acción:</h4>
241
+ <p>{generated_headlines}</p>
242
+ </div>
243
+ """, unsafe_allow_html=True)
244
+ except ValueError as e:
245
+ col2.error(f"Error: {str(e)}")
246
+ else:
247
+ if not selected_formula:
248
+ col2.error("Por favor, selecciona una fórmula.")
249
+ else:
250
+ col2.error("Por favor, proporciona el público objetivo y el producto.")