Spaces:
Sleeping
Sleeping
from dotenv import load_dotenv | |
import streamlit as st | |
import os | |
import google.generativeai as genai | |
import random | |
from streamlit import session_state as state | |
from formulas import headline_formulas | |
from angles import angles | |
# Cargar las variables de entorno | |
load_dotenv() | |
# Configurar la API de Google | |
genai.configure(api_key=os.getenv("GOOGLE_API_KEY")) | |
# Fórmulas con ejemplos y explicaciones | |
# headline_formulas dictionary has been moved to formulas/headline_formulas.py | |
def generate_headlines(number_of_headlines, target_audience, product, temperature, selected_formula, selected_angle, base_copy=None): | |
# Crear la configuración del modelo | |
generation_config = { | |
"temperature": temperature, | |
"top_p": 0.65, | |
"top_k": 360, | |
"max_output_tokens": 8196, | |
} | |
model = genai.GenerativeModel( | |
model_name="gemini-2.0-flash", | |
generation_config=generation_config, | |
) | |
# Angle dictionaries have been moved to angles/angle_data.py | |
# Incluir las instrucciones del sistema en el prompt principal | |
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. | |
FORMAT RULES: | |
- Each headline must start with number and period | |
- One headline per line | |
- No explanations or categories | |
- Add a line break between each headline | |
- Avoid unnecessary : symbols | |
- Each headline must be a complete and intriguing sentence | |
IMPORTANT ANGLE INSTRUCTIONS: | |
- The selected angle MUST be applied to EVERY headline | |
- The angle modifies HOW the formula is expressed, not its structure | |
- Think of the angle as a "tone overlay" on the formula | |
- The formula provides the structure, the angle provides the style | |
- Both must work together seamlessly | |
FORMAT EXAMPLE: | |
1. Titular 1. | |
2. Titular 2. | |
3. Titular 3. | |
4. Titular 4. | |
5. Titular 5. | |
IMPORTANT: | |
- Each headline must be unique and memorable | |
- Avoid clichés and generalities | |
- Maintain an intriguing but credible tone | |
- Adapt speaking language from the audience | |
- Focus on transformative benefits | |
- Follow the selected angle style while maintaining formula structure""" | |
# Iniciar el prompt con las instrucciones del sistema | |
headlines_instruction = f"{system_prompt}\n\n" | |
# Add base copy instructions if provided | |
if base_copy and base_copy.strip(): | |
headlines_instruction += f""" | |
BASE COPY TO USE: | |
The following text should be used as the primary source of information and inspiration for generating headlines: | |
{base_copy} | |
IMPORTANT: Extract key concepts, benefits, and language from this base copy to create your headlines. | |
Use the tone, style, and specific terminology from this text whenever possible. | |
""" | |
# Añadir instrucciones de ángulo solo si no es "NINGUNO" | |
if selected_angle != "NINGUNO": | |
headlines_instruction += f""" | |
ÁNGULO PRINCIPAL: {selected_angle} | |
INSTRUCCIONES DE ÁNGULO ESPECÍFICAS: | |
{angles[selected_angle]["instruction"]} | |
IMPORTANT: The angle {selected_angle} must be applied as a "style layer" over the formula structure: | |
1. Keep the base structure of the formula intact | |
2. Apply the tone and style of the angle {selected_angle} | |
3. Ensure that each element of the formula reflects the angle | |
4. The angle affects "how" it is said, not "what" is said | |
SUCCESSFUL EXAMPLES OF THE ANGLE {selected_angle}: | |
""" | |
for example in angles[selected_angle]["examples"]: | |
headlines_instruction += f"- {example}\n" | |
headlines_instruction += ( | |
f"\nYour task is to create {number_of_headlines} irresistible headlines for {target_audience} " | |
f"that instantly capture attention and generate curiosity about {product}. " | |
) | |
if selected_angle != "NINGUNO": | |
headlines_instruction += f"IMPORTANT: Each headline MUST follow the {selected_angle} angle clearly and consistently.\n\n" | |
headlines_instruction += ( | |
f"Avoid obvious mentions of {product} and focus on awakening genuine interest" | |
) | |
if selected_angle != "NINGUNO": | |
headlines_instruction += f" using the selected angle" | |
headlines_instruction += ".\n\n" | |
headlines_instruction += ( | |
f"IMPORTANT: Carefully study these examples of the selected formula. " | |
f"Each example represents the style and structure to follow" | |
) | |
if selected_angle != "NINGUNO": | |
headlines_instruction += f", adapted to the {selected_angle} angle" | |
headlines_instruction += ":\n\n" | |
# Add 5 random examples of the formula | |
random_examples = random.sample(selected_formula['examples'], min(5, len(selected_formula['examples']))) | |
headlines_instruction += "FORMULA EXAMPLES TO FOLLOW:\n" | |
for i, example in enumerate(random_examples, 1): | |
headlines_instruction += f"{i}. {example}\n" | |
headlines_instruction += "\nSPECIFIC INSTRUCTIONS:\n" | |
headlines_instruction += "1. Maintain the same structure and length as the previous examples\n" | |
headlines_instruction += "2. Use the same tone and writing style\n" | |
headlines_instruction += "3. Replicate the patterns of phrase construction\n" | |
headlines_instruction += "4. Preserve the level of specificity and detail\n" | |
headlines_instruction += f"5. Adapt the content for {target_audience} while maintaining the essence of the examples\n\n" | |
headlines_instruction += f"FORMULA TO FOLLOW:\n{selected_formula['description']}\n\n" | |
# CORRECT (with indentation): | |
if selected_angle != "NINGUNO": | |
headlines_instruction += f""" | |
FINAL REMINDER: | |
1. Follow the structure of the selected formula | |
2. Apply the angle as a "style layer" | |
3. Maintain coherence between formula and angle | |
4. Ensure each headline reflects both elements | |
GENERATE NOW: | |
Create {number_of_headlines} headlines that faithfully follow the style and structure of the examples shown. | |
""" | |
else: | |
headlines_instruction += f""" | |
GENERATE NOW: | |
Create {number_of_headlines} headlines that faithfully follow the style and structure of the examples shown. | |
""" | |
# Send the message to the model (without image conditions) | |
chat_session = model.start_chat( | |
history=[ | |
{ | |
"role": "user", | |
"parts": [headlines_instruction], | |
}, | |
] | |
) | |
response = chat_session.send_message("Genera los titulares siguiendo exactamente el estilo de los ejemplos mostrados.") | |
return response.text | |
# Configurar la interfaz de usuario con Streamlit | |
st.set_page_config(page_title="Enchanted Hooks", layout="wide") | |
# Leer el contenido del archivo manual.md | |
with open("manual.md", "r", encoding="utf-8") as file: | |
manual_content = file.read() | |
# Mostrar el contenido del manual en el sidebar | |
st.sidebar.markdown(manual_content) | |
# Load CSS from file | |
with open("styles/main.css", "r") as f: | |
css = f.read() | |
# Apply the CSS | |
st.markdown(f"<style>{css}</style>", unsafe_allow_html=True) | |
# Centrar el título y el subtítulo | |
st.markdown("<h1 style='text-align: center;'>Enchanted Hooks</h1>", unsafe_allow_html=True) | |
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) | |
# Crear columnas | |
col1, col2 = st.columns([1, 2]) | |
# Columnas de entrada | |
with col1: | |
target_audience = st.text_input("¿Quién es tu público objetivo?", placeholder="Ejemplo: Estudiantes Universitarios") | |
product = st.text_input("¿Qué producto tienes en mente?", placeholder="Ejemplo: Curso de Inglés") | |
# Botón de enviar con estilo personalizado (moved right after product input) | |
submit = st.button("Generar Titulares", key="submit_button", use_container_width=True) | |
# Crear un único acordeón para fórmula, creatividad y ángulo | |
with st.expander("Personaliza tus titulares"): | |
# Moved text area for base copy above formula selection | |
base_copy = st.text_area( | |
"Correo o copy para inspiración de titulares", | |
placeholder="Agrega aquí un texto que sirva como base para generar los titulares. Si lo dejas vacío, se generarán basados en el producto y público objetivo.", | |
height=150 | |
) | |
# Formula selection after the text area | |
selected_formula_key = st.selectbox( | |
"Selecciona una fórmula para tus titulares", | |
options=list(headline_formulas.keys()) | |
) | |
# Angle selection moved before number of headlines | |
angle_keys = ["NINGUNO"] + sorted([key for key in angles.keys() if key != "NINGUNO"]) | |
selected_angle = st.selectbox( | |
"Selecciona el ángulo para tus titulares", | |
options=angle_keys | |
) | |
# Number of headlines moved after angle selection | |
number_of_headlines = st.selectbox("Número de Titulares", options=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], index=4) | |
# Temperature slider remains at the bottom | |
temperature = st.slider("Creatividad", min_value=0.0, max_value=2.0, value=1.0, step=0.1) | |
selected_formula = headline_formulas[selected_formula_key] | |
# Mostrar los titulares generados | |
if submit: | |
# Check if we have valid inputs | |
has_product = product.strip() != "" | |
has_audience = target_audience.strip() != "" | |
has_base_copy = base_copy and base_copy.strip() != "" | |
# Valid combination: Either (Product + Audience) or Base Copy | |
valid_inputs = (has_product and has_audience) or has_base_copy | |
if valid_inputs and selected_formula: | |
try: | |
generated_headlines = generate_headlines( | |
number_of_headlines, | |
target_audience, | |
product, | |
temperature, | |
selected_formula, | |
selected_angle, | |
base_copy if has_base_copy else None | |
) | |
col2.markdown(f""" | |
<div class="results-container"> | |
<h4>Observa la magia en acción:</h4> | |
<p>{generated_headlines}</p> | |
</div> | |
""", unsafe_allow_html=True) | |
except ValueError as e: | |
col2.error(f"Error: {str(e)}") | |
else: | |
if not selected_formula: | |
col2.error("Por favor, selecciona una fórmula.") | |
elif not has_base_copy: | |
col2.error("Por favor, proporciona el público objetivo y el producto, o un texto base.") | |