Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -1,349 +1,349 @@
|
|
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, file_content="", image_parts=None, is_image=False):
|
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 contenido del archivo si existe
|
76 |
-
if file_content:
|
77 |
-
headlines_instruction += f"""
|
78 |
-
REFERENCE CONTENT:
|
79 |
-
Carefully analyze the following content as a reference for generating headlines:
|
80 |
-
{file_content[:3000]}
|
81 |
-
|
82 |
-
ANALYSIS INSTRUCTIONS:
|
83 |
-
1. Extract key information about the product or service mentioned
|
84 |
-
2. Identify the tone, style, and language used
|
85 |
-
3. Detect any data about the target audience or customer avatar
|
86 |
-
4. Look for benefits, features, or pain points mentioned
|
87 |
-
5. Use relevant terms, phrases, or concepts from the content
|
88 |
-
6. Maintain consistency with the brand identity or main message
|
89 |
-
7. Adapt the headlines to resonate with the provided content
|
90 |
-
|
91 |
-
IMPORTANT COMBINATIONS:
|
92 |
-
"""
|
93 |
-
#
|
94 |
-
if product and target_audience:
|
95 |
-
headlines_instruction += f"""- You have
|
96 |
-
"""
|
97 |
-
elif product:
|
98 |
-
headlines_instruction += f"""- You have
|
99 |
-
"""
|
100 |
-
elif target_audience:
|
101 |
-
headlines_instruction += f"""- You have target audience ({target_audience})
|
102 |
-
"""
|
103 |
-
|
104 |
-
headlines_instruction += """
|
105 |
-
IMPORTANT: Naturally integrate the elements found in the content with the selected formula and angle.
|
106 |
-
"""
|
107 |
-
|
108 |
-
# Añadir instrucciones de ángulo solo si no es "NINGUNO"
|
109 |
-
if selected_angle != "NINGUNO":
|
110 |
-
headlines_instruction += f"""
|
111 |
-
ÁNGULO PRINCIPAL: {selected_angle}
|
112 |
-
INSTRUCCIONES DE ÁNGULO ESPECÍFICAS:
|
113 |
-
{angles[selected_angle]["instruction"]}
|
114 |
-
|
115 |
-
IMPORTANTE: El ángulo {selected_angle} debe aplicarse como una "capa de estilo" sobre la estructura de la fórmula:
|
116 |
-
1. Mantén la estructura base de la fórmula intacta
|
117 |
-
2. Aplica el tono y estilo del ángulo {selected_angle}
|
118 |
-
3. Asegura que cada elemento de la fórmula refleje el ángulo
|
119 |
-
4. El ángulo afecta al "cómo" se dice, no al "qué" se dice
|
120 |
-
|
121 |
-
EJEMPLOS EXITOSOS DEL ÁNGULO {selected_angle}:
|
122 |
-
"""
|
123 |
-
for example in angles[selected_angle]["examples"]:
|
124 |
-
headlines_instruction += f"- {example}\n"
|
125 |
-
|
126 |
-
headlines_instruction += (
|
127 |
-
f"\nTu tarea es crear {number_of_headlines} titulares irresistibles para {target_audience} "
|
128 |
-
f"que capturen la atención instantáneamente y generen curiosidad sobre {product}. "
|
129 |
-
)
|
130 |
-
|
131 |
-
if selected_angle != "NINGUNO":
|
132 |
-
headlines_instruction += f"IMPORTANTE: Cada titular DEBE seguir el ángulo {selected_angle} de manera clara y consistente.\n\n"
|
133 |
-
|
134 |
-
headlines_instruction += (
|
135 |
-
f"Evita menciones obvias de {product} y enfócate en despertar interés genuino"
|
136 |
-
)
|
137 |
-
|
138 |
-
if selected_angle != "NINGUNO":
|
139 |
-
headlines_instruction += f" usando el ángulo seleccionado"
|
140 |
-
|
141 |
-
headlines_instruction += ".\n\n"
|
142 |
-
|
143 |
-
headlines_instruction += (
|
144 |
-
f"IMPORTANTE: Estudia cuidadosamente estos ejemplos de la fórmula seleccionada. "
|
145 |
-
f"Cada ejemplo representa el estilo y estructura a seguir"
|
146 |
-
)
|
147 |
-
|
148 |
-
if selected_angle != "NINGUNO":
|
149 |
-
headlines_instruction += f", adaptados al ángulo {selected_angle}"
|
150 |
-
|
151 |
-
headlines_instruction += ":\n\n"
|
152 |
-
|
153 |
-
# Agregar 5 ejemplos aleatorios de la fórmula
|
154 |
-
random_examples = random.sample(selected_formula['examples'], min(5, len(selected_formula['examples'])))
|
155 |
-
|
156 |
-
headlines_instruction += "EJEMPLOS DE LA FÓRMULA A SEGUIR:\n"
|
157 |
-
for i, example in enumerate(random_examples, 1):
|
158 |
-
headlines_instruction += f"{i}. {example}\n"
|
159 |
-
|
160 |
-
headlines_instruction += "\nINSTRUCCIONES ESPECÍFICAS:\n"
|
161 |
-
headlines_instruction += "1. Mantén la misma estructura y longitud que los ejemplos anteriores\n"
|
162 |
-
headlines_instruction += "2. Usa el mismo tono y estilo de escritura\n"
|
163 |
-
headlines_instruction += "3. Replica los patrones de construcción de frases\n"
|
164 |
-
headlines_instruction += "4. Conserva el nivel de especificidad y detalle\n"
|
165 |
-
headlines_instruction += f"5. Adapta el contenido para {target_audience} manteniendo la esencia de los ejemplos\n\n"
|
166 |
-
|
167 |
-
headlines_instruction += f"FÓRMULA A SEGUIR:\n{selected_formula['description']}\n\n"
|
168 |
-
|
169 |
-
# CORRECTO (con indentación):
|
170 |
-
if selected_angle != "NINGUNO":
|
171 |
-
headlines_instruction += f"""
|
172 |
-
RECORDATORIO FINAL:
|
173 |
-
1. Sigue la estructura de la fórmula seleccionada
|
174 |
-
2. Aplica el ángulo como una "capa de estilo"
|
175 |
-
3. Mantén la coherencia entre fórmula y ángulo
|
176 |
-
4. Asegura que cada titular refleje ambos elementos
|
177 |
-
|
178 |
-
GENERA AHORA:
|
179 |
-
Crea {number_of_headlines} titulares que sigan fielmente el estilo y estructura de los ejemplos mostrados.
|
180 |
-
"""
|
181 |
-
else:
|
182 |
-
headlines_instruction += f"""
|
183 |
-
GENERA AHORA:
|
184 |
-
Crea {number_of_headlines} titulares que sigan fielmente el estilo y estructura de los ejemplos mostrados.
|
185 |
-
"""
|
186 |
-
|
187 |
-
# Modificar la forma de enviar el mensaje según si hay imagen o no
|
188 |
-
if is_image and image_parts:
|
189 |
-
chat_session = model.start_chat(
|
190 |
-
history=[
|
191 |
-
{
|
192 |
-
"role": "user",
|
193 |
-
"parts": [
|
194 |
-
headlines_instruction,
|
195 |
-
image_parts
|
196 |
-
],
|
197 |
-
},
|
198 |
-
]
|
199 |
-
)
|
200 |
-
response = chat_session.send_message("Genera los titulares siguiendo exactamente el estilo de los ejemplos mostrados, inspirándote en la imagen proporcionada.")
|
201 |
-
else:
|
202 |
-
chat_session = model.start_chat(
|
203 |
-
history=[
|
204 |
-
{
|
205 |
-
"role": "user",
|
206 |
-
"parts": [headlines_instruction],
|
207 |
-
},
|
208 |
-
]
|
209 |
-
)
|
210 |
-
response = chat_session.send_message("Genera los titulares siguiendo exactamente el estilo de los ejemplos mostrados.")
|
211 |
-
|
212 |
-
return response.text
|
213 |
-
|
214 |
-
# Configurar la interfaz de usuario con Streamlit
|
215 |
-
st.set_page_config(page_title="Enchanted Hooks", layout="wide")
|
216 |
-
|
217 |
-
# Leer el contenido del archivo manual.md
|
218 |
-
with open("manual.md", "r", encoding="utf-8") as file:
|
219 |
-
manual_content = file.read()
|
220 |
-
|
221 |
-
# Mostrar el contenido del manual en el sidebar
|
222 |
-
st.sidebar.markdown(manual_content)
|
223 |
-
|
224 |
-
# Load CSS from file
|
225 |
-
with open("styles/main.css", "r") as f:
|
226 |
-
css = f.read()
|
227 |
-
|
228 |
-
# Apply the CSS
|
229 |
-
st.markdown(f"<style>{css}</style>", unsafe_allow_html=True)
|
230 |
-
|
231 |
-
# Centrar el título y el subtítulo
|
232 |
-
st.markdown("<h1 style='text-align: center;'>Enchanted Hooks</h1>", unsafe_allow_html=True)
|
233 |
-
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)
|
234 |
-
|
235 |
-
# Crear columnas
|
236 |
-
col1, col2 = st.columns([1, 2])
|
237 |
-
|
238 |
-
# Columnas de entrada
|
239 |
-
with col1:
|
240 |
-
target_audience = st.text_input("¿Quién es tu público objetivo?", placeholder="Ejemplo: Estudiantes Universitarios")
|
241 |
-
product = st.text_input("¿Qué producto tienes en mente?", placeholder="Ejemplo: Curso de Inglés")
|
242 |
-
number_of_headlines = st.selectbox("Número de Titulares", options=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], index=4)
|
243 |
-
|
244 |
-
# Crear un único acordeón para fórmula, creatividad y ángulo
|
245 |
-
with st.expander("Personaliza tus titulares"):
|
246 |
-
temperature = st.slider("Creatividad", min_value=0.0, max_value=2.0, value=1.0, step=0.1)
|
247 |
-
|
248 |
-
selected_formula_key = st.selectbox(
|
249 |
-
"Selecciona una fórmula para tus titulares",
|
250 |
-
options=list(headline_formulas.keys())
|
251 |
-
)
|
252 |
-
|
253 |
-
# Automatically use the keys from the angles dictionary
|
254 |
-
# Make sure "NINGUNO" appears first, then the rest alphabetically
|
255 |
-
angle_keys = ["NINGUNO"] + sorted([key for key in angles.keys() if key != "NINGUNO"])
|
256 |
-
selected_angle = st.selectbox(
|
257 |
-
"Selecciona el ángulo para tus titulares",
|
258 |
-
options=angle_keys
|
259 |
-
)
|
260 |
-
|
261 |
-
# Añadir cargador de archivos dentro del acordeón
|
262 |
-
uploaded_file = st.file_uploader("📄 Archivo o imagen de referencia",
|
263 |
-
type=['txt', 'pdf', 'docx', 'jpg', 'jpeg', 'png'])
|
264 |
-
|
265 |
-
file_content = ""
|
266 |
-
is_image = False
|
267 |
-
image_parts = None
|
268 |
-
|
269 |
-
if uploaded_file is not None:
|
270 |
-
file_type = uploaded_file.name.split('.')[-1].lower()
|
271 |
-
|
272 |
-
# Manejar archivos de texto
|
273 |
-
if file_type in ['txt', 'pdf', 'docx']:
|
274 |
-
if file_type == 'txt':
|
275 |
-
try:
|
276 |
-
file_content = uploaded_file.read().decode('utf-8')
|
277 |
-
st.success(f"Archivo TXT cargado correctamente: {uploaded_file.name}")
|
278 |
-
except Exception as e:
|
279 |
-
st.error(f"Error al leer el archivo TXT: {str(e)}")
|
280 |
-
file_content = ""
|
281 |
-
|
282 |
-
elif file_type == 'pdf':
|
283 |
-
try:
|
284 |
-
import PyPDF2
|
285 |
-
pdf_reader = PyPDF2.PdfReader(uploaded_file)
|
286 |
-
file_content = ""
|
287 |
-
for page in pdf_reader.pages:
|
288 |
-
file_content += page.extract_text() + "\n"
|
289 |
-
st.success(f"Archivo PDF cargado correctamente: {uploaded_file.name}")
|
290 |
-
except Exception as e:
|
291 |
-
st.error(f"Error al leer el archivo PDF: {str(e)}")
|
292 |
-
file_content = ""
|
293 |
-
|
294 |
-
elif file_type == 'docx':
|
295 |
-
try:
|
296 |
-
import docx
|
297 |
-
doc = docx.Document(uploaded_file)
|
298 |
-
file_content = "\n".join([para.text for para in doc.paragraphs])
|
299 |
-
st.success(f"Archivo DOCX cargado correctamente: {uploaded_file.name}")
|
300 |
-
except Exception as e:
|
301 |
-
st.error(f"Error al leer el archivo DOCX: {str(e)}")
|
302 |
-
file_content = ""
|
303 |
-
|
304 |
-
# Manejar archivos de imagen
|
305 |
-
elif file_type in ['jpg', 'jpeg', 'png']:
|
306 |
-
try:
|
307 |
-
from PIL import Image
|
308 |
-
image = Image.open(uploaded_file)
|
309 |
-
image_bytes = uploaded_file.getvalue()
|
310 |
-
image_parts = {
|
311 |
-
"mime_type": uploaded_file.type,
|
312 |
-
"data": image_bytes
|
313 |
-
}
|
314 |
-
is_image = True
|
315 |
-
st.image(image, caption="Imagen cargada", use_column_width=True)
|
316 |
-
except Exception as e:
|
317 |
-
st.error(f"Error al procesar la imagen: {str(e)}")
|
318 |
-
is_image = False
|
319 |
-
|
320 |
-
selected_formula = headline_formulas[selected_formula_key]
|
321 |
-
|
322 |
-
# Botón de enviar
|
323 |
-
submit = st.button("Generar Titulares")
|
324 |
-
|
325 |
-
# Mostrar los titulares generados
|
326 |
-
if submit:
|
327 |
-
if target_audience and product and selected_formula:
|
328 |
-
try:
|
329 |
-
generated_headlines = generate_headlines(
|
330 |
-
number_of_headlines,
|
331 |
-
target_audience,
|
332 |
-
product,
|
333 |
-
temperature,
|
334 |
-
selected_formula,
|
335 |
-
selected_angle,
|
336 |
-
file_content if 'file_content' in locals() else "",
|
337 |
-
image_parts if 'image_parts' in locals() else None,
|
338 |
-
is_image if 'is_image' in locals() else False
|
339 |
-
)
|
340 |
-
col2.markdown(f"""
|
341 |
-
<div class="results-container">
|
342 |
-
<h4>Observa la magia en acción:</h4>
|
343 |
-
<p>{generated_headlines}</p>
|
344 |
-
</div>
|
345 |
-
""", unsafe_allow_html=True)
|
346 |
-
except ValueError as e:
|
347 |
-
col2.error(f"Error: {str(e)}")
|
348 |
-
else:
|
349 |
-
col2.error("Por favor, proporciona el público objetivo, el producto y selecciona una fórmula.")
|
|
|
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, file_content="", image_parts=None, is_image=False):
|
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 contenido del archivo si existe
|
76 |
+
if file_content:
|
77 |
+
headlines_instruction += f"""
|
78 |
+
REFERENCE CONTENT:
|
79 |
+
Carefully analyze the following content as a reference for generating headlines:
|
80 |
+
{file_content[:3000]}
|
81 |
+
|
82 |
+
ANALYSIS INSTRUCTIONS:
|
83 |
+
1. Extract key information about the product or service mentioned
|
84 |
+
2. Identify the tone, style, and language used
|
85 |
+
3. Detect any data about the target audience or customer avatar
|
86 |
+
4. Look for benefits, features, or pain points mentioned
|
87 |
+
5. Use relevant terms, phrases, or concepts from the content
|
88 |
+
6. Maintain consistency with the brand identity or main message
|
89 |
+
7. Adapt the headlines to resonate with the provided content
|
90 |
+
|
91 |
+
IMPORTANT COMBINATIONS:
|
92 |
+
"""
|
93 |
+
# Updated conditions for specific input combinations
|
94 |
+
if product and not target_audience:
|
95 |
+
headlines_instruction += f"""- FILE + PRODUCT: You have a reference document and product ({product}). Create headlines that highlight this specific product's benefits and features using insights from the document. Extract audience information from the document to better target the headlines.
|
96 |
+
"""
|
97 |
+
elif target_audience and not product:
|
98 |
+
headlines_instruction += f"""- FILE + TARGET AUDIENCE: You have a reference document and target audience ({target_audience}). Create headlines tailored to this specific audience using language and concepts from the document. Identify products or services from the document that would appeal to this audience.
|
99 |
+
"""
|
100 |
+
elif product and target_audience:
|
101 |
+
headlines_instruction += f"""- PRODUCT + TARGET AUDIENCE: You have both product ({product}) and target audience ({target_audience}). Create headlines that connect this specific product with this specific audience, using insights from the document to strengthen the connection.
|
102 |
+
"""
|
103 |
+
|
104 |
+
headlines_instruction += """
|
105 |
+
IMPORTANT: Naturally integrate the elements found in the content with the selected formula and angle.
|
106 |
+
"""
|
107 |
+
|
108 |
+
# Añadir instrucciones de ángulo solo si no es "NINGUNO"
|
109 |
+
if selected_angle != "NINGUNO":
|
110 |
+
headlines_instruction += f"""
|
111 |
+
ÁNGULO PRINCIPAL: {selected_angle}
|
112 |
+
INSTRUCCIONES DE ÁNGULO ESPECÍFICAS:
|
113 |
+
{angles[selected_angle]["instruction"]}
|
114 |
+
|
115 |
+
IMPORTANTE: El ángulo {selected_angle} debe aplicarse como una "capa de estilo" sobre la estructura de la fórmula:
|
116 |
+
1. Mantén la estructura base de la fórmula intacta
|
117 |
+
2. Aplica el tono y estilo del ángulo {selected_angle}
|
118 |
+
3. Asegura que cada elemento de la fórmula refleje el ángulo
|
119 |
+
4. El ángulo afecta al "cómo" se dice, no al "qué" se dice
|
120 |
+
|
121 |
+
EJEMPLOS EXITOSOS DEL ÁNGULO {selected_angle}:
|
122 |
+
"""
|
123 |
+
for example in angles[selected_angle]["examples"]:
|
124 |
+
headlines_instruction += f"- {example}\n"
|
125 |
+
|
126 |
+
headlines_instruction += (
|
127 |
+
f"\nTu tarea es crear {number_of_headlines} titulares irresistibles para {target_audience} "
|
128 |
+
f"que capturen la atención instantáneamente y generen curiosidad sobre {product}. "
|
129 |
+
)
|
130 |
+
|
131 |
+
if selected_angle != "NINGUNO":
|
132 |
+
headlines_instruction += f"IMPORTANTE: Cada titular DEBE seguir el ángulo {selected_angle} de manera clara y consistente.\n\n"
|
133 |
+
|
134 |
+
headlines_instruction += (
|
135 |
+
f"Evita menciones obvias de {product} y enfócate en despertar interés genuino"
|
136 |
+
)
|
137 |
+
|
138 |
+
if selected_angle != "NINGUNO":
|
139 |
+
headlines_instruction += f" usando el ángulo seleccionado"
|
140 |
+
|
141 |
+
headlines_instruction += ".\n\n"
|
142 |
+
|
143 |
+
headlines_instruction += (
|
144 |
+
f"IMPORTANTE: Estudia cuidadosamente estos ejemplos de la fórmula seleccionada. "
|
145 |
+
f"Cada ejemplo representa el estilo y estructura a seguir"
|
146 |
+
)
|
147 |
+
|
148 |
+
if selected_angle != "NINGUNO":
|
149 |
+
headlines_instruction += f", adaptados al ángulo {selected_angle}"
|
150 |
+
|
151 |
+
headlines_instruction += ":\n\n"
|
152 |
+
|
153 |
+
# Agregar 5 ejemplos aleatorios de la fórmula
|
154 |
+
random_examples = random.sample(selected_formula['examples'], min(5, len(selected_formula['examples'])))
|
155 |
+
|
156 |
+
headlines_instruction += "EJEMPLOS DE LA FÓRMULA A SEGUIR:\n"
|
157 |
+
for i, example in enumerate(random_examples, 1):
|
158 |
+
headlines_instruction += f"{i}. {example}\n"
|
159 |
+
|
160 |
+
headlines_instruction += "\nINSTRUCCIONES ESPECÍFICAS:\n"
|
161 |
+
headlines_instruction += "1. Mantén la misma estructura y longitud que los ejemplos anteriores\n"
|
162 |
+
headlines_instruction += "2. Usa el mismo tono y estilo de escritura\n"
|
163 |
+
headlines_instruction += "3. Replica los patrones de construcción de frases\n"
|
164 |
+
headlines_instruction += "4. Conserva el nivel de especificidad y detalle\n"
|
165 |
+
headlines_instruction += f"5. Adapta el contenido para {target_audience} manteniendo la esencia de los ejemplos\n\n"
|
166 |
+
|
167 |
+
headlines_instruction += f"FÓRMULA A SEGUIR:\n{selected_formula['description']}\n\n"
|
168 |
+
|
169 |
+
# CORRECTO (con indentación):
|
170 |
+
if selected_angle != "NINGUNO":
|
171 |
+
headlines_instruction += f"""
|
172 |
+
RECORDATORIO FINAL:
|
173 |
+
1. Sigue la estructura de la fórmula seleccionada
|
174 |
+
2. Aplica el ángulo como una "capa de estilo"
|
175 |
+
3. Mantén la coherencia entre fórmula y ángulo
|
176 |
+
4. Asegura que cada titular refleje ambos elementos
|
177 |
+
|
178 |
+
GENERA AHORA:
|
179 |
+
Crea {number_of_headlines} titulares que sigan fielmente el estilo y estructura de los ejemplos mostrados.
|
180 |
+
"""
|
181 |
+
else:
|
182 |
+
headlines_instruction += f"""
|
183 |
+
GENERA AHORA:
|
184 |
+
Crea {number_of_headlines} titulares que sigan fielmente el estilo y estructura de los ejemplos mostrados.
|
185 |
+
"""
|
186 |
+
|
187 |
+
# Modificar la forma de enviar el mensaje según si hay imagen o no
|
188 |
+
if is_image and image_parts:
|
189 |
+
chat_session = model.start_chat(
|
190 |
+
history=[
|
191 |
+
{
|
192 |
+
"role": "user",
|
193 |
+
"parts": [
|
194 |
+
headlines_instruction,
|
195 |
+
image_parts
|
196 |
+
],
|
197 |
+
},
|
198 |
+
]
|
199 |
+
)
|
200 |
+
response = chat_session.send_message("Genera los titulares siguiendo exactamente el estilo de los ejemplos mostrados, inspirándote en la imagen proporcionada.")
|
201 |
+
else:
|
202 |
+
chat_session = model.start_chat(
|
203 |
+
history=[
|
204 |
+
{
|
205 |
+
"role": "user",
|
206 |
+
"parts": [headlines_instruction],
|
207 |
+
},
|
208 |
+
]
|
209 |
+
)
|
210 |
+
response = chat_session.send_message("Genera los titulares siguiendo exactamente el estilo de los ejemplos mostrados.")
|
211 |
+
|
212 |
+
return response.text
|
213 |
+
|
214 |
+
# Configurar la interfaz de usuario con Streamlit
|
215 |
+
st.set_page_config(page_title="Enchanted Hooks", layout="wide")
|
216 |
+
|
217 |
+
# Leer el contenido del archivo manual.md
|
218 |
+
with open("manual.md", "r", encoding="utf-8") as file:
|
219 |
+
manual_content = file.read()
|
220 |
+
|
221 |
+
# Mostrar el contenido del manual en el sidebar
|
222 |
+
st.sidebar.markdown(manual_content)
|
223 |
+
|
224 |
+
# Load CSS from file
|
225 |
+
with open("styles/main.css", "r") as f:
|
226 |
+
css = f.read()
|
227 |
+
|
228 |
+
# Apply the CSS
|
229 |
+
st.markdown(f"<style>{css}</style>", unsafe_allow_html=True)
|
230 |
+
|
231 |
+
# Centrar el título y el subtítulo
|
232 |
+
st.markdown("<h1 style='text-align: center;'>Enchanted Hooks</h1>", unsafe_allow_html=True)
|
233 |
+
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)
|
234 |
+
|
235 |
+
# Crear columnas
|
236 |
+
col1, col2 = st.columns([1, 2])
|
237 |
+
|
238 |
+
# Columnas de entrada
|
239 |
+
with col1:
|
240 |
+
target_audience = st.text_input("¿Quién es tu público objetivo?", placeholder="Ejemplo: Estudiantes Universitarios")
|
241 |
+
product = st.text_input("¿Qué producto tienes en mente?", placeholder="Ejemplo: Curso de Inglés")
|
242 |
+
number_of_headlines = st.selectbox("Número de Titulares", options=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], index=4)
|
243 |
+
|
244 |
+
# Crear un único acordeón para fórmula, creatividad y ángulo
|
245 |
+
with st.expander("Personaliza tus titulares"):
|
246 |
+
temperature = st.slider("Creatividad", min_value=0.0, max_value=2.0, value=1.0, step=0.1)
|
247 |
+
|
248 |
+
selected_formula_key = st.selectbox(
|
249 |
+
"Selecciona una fórmula para tus titulares",
|
250 |
+
options=list(headline_formulas.keys())
|
251 |
+
)
|
252 |
+
|
253 |
+
# Automatically use the keys from the angles dictionary
|
254 |
+
# Make sure "NINGUNO" appears first, then the rest alphabetically
|
255 |
+
angle_keys = ["NINGUNO"] + sorted([key for key in angles.keys() if key != "NINGUNO"])
|
256 |
+
selected_angle = st.selectbox(
|
257 |
+
"Selecciona el ángulo para tus titulares",
|
258 |
+
options=angle_keys
|
259 |
+
)
|
260 |
+
|
261 |
+
# Añadir cargador de archivos dentro del acordeón
|
262 |
+
uploaded_file = st.file_uploader("📄 Archivo o imagen de referencia",
|
263 |
+
type=['txt', 'pdf', 'docx', 'jpg', 'jpeg', 'png'])
|
264 |
+
|
265 |
+
file_content = ""
|
266 |
+
is_image = False
|
267 |
+
image_parts = None
|
268 |
+
|
269 |
+
if uploaded_file is not None:
|
270 |
+
file_type = uploaded_file.name.split('.')[-1].lower()
|
271 |
+
|
272 |
+
# Manejar archivos de texto
|
273 |
+
if file_type in ['txt', 'pdf', 'docx']:
|
274 |
+
if file_type == 'txt':
|
275 |
+
try:
|
276 |
+
file_content = uploaded_file.read().decode('utf-8')
|
277 |
+
st.success(f"Archivo TXT cargado correctamente: {uploaded_file.name}")
|
278 |
+
except Exception as e:
|
279 |
+
st.error(f"Error al leer el archivo TXT: {str(e)}")
|
280 |
+
file_content = ""
|
281 |
+
|
282 |
+
elif file_type == 'pdf':
|
283 |
+
try:
|
284 |
+
import PyPDF2
|
285 |
+
pdf_reader = PyPDF2.PdfReader(uploaded_file)
|
286 |
+
file_content = ""
|
287 |
+
for page in pdf_reader.pages:
|
288 |
+
file_content += page.extract_text() + "\n"
|
289 |
+
st.success(f"Archivo PDF cargado correctamente: {uploaded_file.name}")
|
290 |
+
except Exception as e:
|
291 |
+
st.error(f"Error al leer el archivo PDF: {str(e)}")
|
292 |
+
file_content = ""
|
293 |
+
|
294 |
+
elif file_type == 'docx':
|
295 |
+
try:
|
296 |
+
import docx
|
297 |
+
doc = docx.Document(uploaded_file)
|
298 |
+
file_content = "\n".join([para.text for para in doc.paragraphs])
|
299 |
+
st.success(f"Archivo DOCX cargado correctamente: {uploaded_file.name}")
|
300 |
+
except Exception as e:
|
301 |
+
st.error(f"Error al leer el archivo DOCX: {str(e)}")
|
302 |
+
file_content = ""
|
303 |
+
|
304 |
+
# Manejar archivos de imagen
|
305 |
+
elif file_type in ['jpg', 'jpeg', 'png']:
|
306 |
+
try:
|
307 |
+
from PIL import Image
|
308 |
+
image = Image.open(uploaded_file)
|
309 |
+
image_bytes = uploaded_file.getvalue()
|
310 |
+
image_parts = {
|
311 |
+
"mime_type": uploaded_file.type,
|
312 |
+
"data": image_bytes
|
313 |
+
}
|
314 |
+
is_image = True
|
315 |
+
st.image(image, caption="Imagen cargada", use_column_width=True)
|
316 |
+
except Exception as e:
|
317 |
+
st.error(f"Error al procesar la imagen: {str(e)}")
|
318 |
+
is_image = False
|
319 |
+
|
320 |
+
selected_formula = headline_formulas[selected_formula_key]
|
321 |
+
|
322 |
+
# Botón de enviar
|
323 |
+
submit = st.button("Generar Titulares")
|
324 |
+
|
325 |
+
# Mostrar los titulares generados
|
326 |
+
if submit:
|
327 |
+
if target_audience and product and selected_formula:
|
328 |
+
try:
|
329 |
+
generated_headlines = generate_headlines(
|
330 |
+
number_of_headlines,
|
331 |
+
target_audience,
|
332 |
+
product,
|
333 |
+
temperature,
|
334 |
+
selected_formula,
|
335 |
+
selected_angle,
|
336 |
+
file_content if 'file_content' in locals() else "",
|
337 |
+
image_parts if 'image_parts' in locals() else None,
|
338 |
+
is_image if 'is_image' in locals() else False
|
339 |
+
)
|
340 |
+
col2.markdown(f"""
|
341 |
+
<div class="results-container">
|
342 |
+
<h4>Observa la magia en acción:</h4>
|
343 |
+
<p>{generated_headlines}</p>
|
344 |
+
</div>
|
345 |
+
""", unsafe_allow_html=True)
|
346 |
+
except ValueError as e:
|
347 |
+
col2.error(f"Error: {str(e)}")
|
348 |
+
else:
|
349 |
+
col2.error("Por favor, proporciona el público objetivo, el producto y selecciona una fórmula.")
|