Spaces:
Sleeping
Sleeping
from dotenv import load_dotenv | |
import streamlit as st | |
import os | |
import google.generativeai as genai | |
from puv_formulas import puv_formulas | |
from styles import apply_styles, format_creative_response | |
from options import tone_options, creative_approaches | |
import PyPDF2 | |
import docx | |
from PIL import Image | |
import datetime # Add this import for timestamp | |
# Cargar variables de entorno | |
load_dotenv() | |
# Configurar API de Google Gemini | |
genai.configure(api_key=os.getenv("GOOGLE_API_KEY")) | |
# Función para obtener la respuesta del modelo Gemini | |
def get_gemini_response(product_service, target_audience, main_benefit, tone_of_voice, temperature, file_content="", image_parts=None, creative_approach=""): | |
# Adjust prompt based on what's provided | |
business_info = f"Target Audience: {target_audience}\n" | |
business_info += f"Product/Service: {product_service}\n" | |
business_info += f"Main Benefit: {main_benefit}\n" | |
if tone_of_voice: | |
business_info += f"Brand Tone of Voice: {tone_of_voice}\n" | |
# Add creative approach to business info if provided | |
if creative_approach: | |
# Get the description from the dictionary | |
from options import creative_approaches | |
approach_description = creative_approaches.get(creative_approach, "") | |
business_info += f"\nCREATIVE APPROACH: {creative_approach}\n" | |
business_info += f"Description: {approach_description}\n" | |
business_info += f"IMPORTANT: Please follow this creative approach strictly when generating concepts.\n" | |
# Add file content if available | |
reference_info = "" | |
if file_content: | |
reference_info = f"\nREFERENCE MATERIAL:\n{file_content}\n" | |
model = genai.GenerativeModel('gemini-2.0-flash') | |
full_prompt = f""" | |
You are a Creative Concept expert. Analyze (internally only, do not output the analysis) the following information: | |
BUSINESS INFORMATION: | |
{business_info} | |
{reference_info} | |
A Creative Idea is a set of pieces created to sell a brand, product, or service, united by the same idea that is transmitted through a creative concept. | |
First, analyze (but don't output) these points: | |
1. TARGET AUDIENCE ANALYSIS: | |
- What everyday concepts are they familiar with? | |
- What TV shows, movies, or cultural references resonate with them? | |
- What emotions and experiences are meaningful to them? | |
- What mental images would be easy for them to recall? | |
2. PRODUCT/SERVICE ANALYSIS: | |
- What is the main benefit or promise? | |
- What makes it unique or different? | |
- What transformation does it offer? | |
- What process or journey does the customer go through? | |
Based on your internal analysis, create THREE different Creative Concepts in Spanish language. | |
Each concept should be a DIRECT ANALOGY or METAPHOR that connects your product/service to something completely different but familiar. | |
Examples of good creative concepts: | |
- "Escribir copy es como cocinar tu plato favorito porque necesitas los ingredientes correctos para que todos quieran probarlo" | |
- "Tu negocio es como un equipo de fútbol: necesita buenos jugadores (productos) y una estrategia clara para ganar clientes" | |
- "Tu curso es como Netflix: ofrece contenido que engancha y soluciones que la gente realmente quiere ver" | |
For each concept, include: | |
CONCEPT: A clear statement of the main benefit | |
CREATIVITY: A direct analogy or metaphor connecting your product to something completely different but familiar | |
CRITICAL INSTRUCTIONS: | |
- Each concept MUST use a direct "X es como Y porque Z" structure | |
- Use SIMPLE, EVERYDAY language that anyone can understand | |
- Avoid technical jargon, complex words, or business terminology | |
- Write as if you're explaining to a friend in a casual conversation | |
- Use everyday objects, activities, movies, TV shows or cultural references everyone knows | |
- Make the connection SURPRISING and UNEXPECTED - connect things that normally wouldn't be connected | |
- Challenge conventional thinking by finding parallels between your product and something completely different | |
- Create analogies that make people say "I never thought of it that way!" | |
- Focus on the main benefit | |
- Create clear mental images | |
- Be easy to remember | |
- Use the brand's tone of voice if provided | |
- Format with proper spacing between sections | |
Output EXACTLY in this format (no additional text) in Spanish language: | |
CONCEPTO CREATIVO 1: | |
Concepto: | |
[Main message/benefit in simple, conversational language] | |
Creatividad: | |
[Direct analogy using everyday language: "X es como Y porque Z"] | |
CONCEPTO CREATIVO 2: | |
Concepto: | |
[Main message/benefit] | |
Creatividad: | |
[Direct analogy: "X es como Y porque Z"] | |
CONCEPTO CREATIVO 3: | |
Concepto: | |
[Main message/benefit] | |
Creatividad: | |
[Direct analogy: "X es como Y porque Z"] | |
""" | |
# Handle text-only or text+image requests | |
if image_parts: | |
response = model.generate_content([full_prompt, image_parts], generation_config={"temperature": temperature}) | |
else: | |
response = model.generate_content([full_prompt], generation_config={"temperature": temperature}) | |
return response.parts[0].text if response and response.parts else "Error generating content." | |
# Configurar la aplicación Streamlit | |
st.set_page_config(page_title="Generador de Ideas Creativas", page_icon="💡", layout="wide") | |
# Aplicar estilos | |
st.markdown(apply_styles(), unsafe_allow_html=True) | |
# Título de la app | |
st.markdown("<h1>Generador de Ideas Creativas</h1>", unsafe_allow_html=True) | |
st.markdown("<h3>Crea conceptos creativos poderosos que conecten con tu audiencia y transmitan el valor de tu marca de manera memorable.</h3>", unsafe_allow_html=True) | |
# Sidebar manual | |
with open("manual.md", "r", encoding="utf-8") as file: | |
manual_content = file.read() | |
st.sidebar.markdown(manual_content) | |
# Crear dos columnas | |
col1, col2 = st.columns([1, 1]) | |
# Columna izquierda para inputs | |
# In the app.py file, update the main_benefit field label and create an accordion for tone options | |
with col1: | |
product_service = st.text_area( | |
"¿Cuál es tu producto o servicio?", | |
placeholder="Ejemplo: Curso de copywriting con IA, Programa de coaching..." | |
) | |
main_benefit = st.text_area( | |
"¿Cuál es tu Oferta Dorada/PUV?", | |
placeholder="Ejemplo: Aprender copywriting a través de transformaciones reales de textos..." | |
) | |
target_audience = st.text_area( | |
"¿Cuál es tu público objetivo?", | |
placeholder="Ejemplo: Emprendedores que quieren mejorar sus textos comerciales..." | |
) | |
# Generate button after main inputs | |
generate_button = st.button("Generar Ideas Creativas") | |
with st.expander("Opciones avanzadas"): | |
# Replace nested expanders with a selectbox for tone selection | |
st.write("Tono de voz de la marca (opcional)") | |
# Use selectbox for tone selection | |
selected_tone = st.selectbox( | |
"Selecciona un tono:", | |
options=list(tone_options.keys()), | |
index=0, | |
key="tone_selectbox" | |
) | |
# Display the description of the selected tone | |
if selected_tone != "Ninguno": | |
st.info(tone_options[selected_tone]) | |
# Store the selected tone | |
st.session_state.selected_tone = selected_tone | |
else: | |
# Clear any previously selected tone | |
if "selected_tone" in st.session_state: | |
del st.session_state.selected_tone | |
# Use the stored tone or empty string | |
tone_of_voice = st.session_state.get("selected_tone", "") | |
# Añadir cargador de archivos | |
uploaded_file = st.file_uploader("📄 Archivo o imagen de referencia", | |
type=['txt', 'pdf', 'docx', 'jpg', 'jpeg', 'png']) | |
file_content = "" | |
is_image = False | |
image_parts = None | |
if uploaded_file is not None: | |
file_type = uploaded_file.name.split('.')[-1].lower() | |
# Manejar archivos de texto | |
if file_type in ['txt', 'pdf', 'docx']: | |
if file_type == 'txt': | |
try: | |
file_content = uploaded_file.read().decode('utf-8') | |
except Exception as e: | |
st.error(f"Error al leer el archivo TXT: {str(e)}") | |
file_content = "" | |
elif file_type == 'pdf': | |
try: | |
pdf_reader = PyPDF2.PdfReader(uploaded_file) | |
file_content = "" | |
for page in pdf_reader.pages: | |
file_content += page.extract_text() + "\n" | |
except Exception as e: | |
st.error(f"Error al leer el archivo PDF: {str(e)}") | |
file_content = "" | |
elif file_type == 'docx': | |
try: | |
doc = docx.Document(uploaded_file) | |
file_content = "\n".join([para.text for para in doc.paragraphs]) | |
except Exception as e: | |
st.error(f"Error al leer el archivo DOCX: {str(e)}") | |
file_content = "" | |
# Manejar archivos de imagen | |
elif file_type in ['jpg', 'jpeg', 'png']: | |
try: | |
image = Image.open(uploaded_file) | |
image_bytes = uploaded_file.getvalue() | |
image_parts = { | |
"mime_type": uploaded_file.type, | |
"data": image_bytes | |
} | |
is_image = True | |
except Exception as e: | |
st.error(f"Error al procesar la imagen: {str(e)}") | |
is_image = False | |
# Add creative approach selector (moved outside nested expander) | |
selected_approach = st.selectbox( | |
"Enfoque creativo:", | |
options=list(creative_approaches.keys()), | |
index=0, | |
key="approach_selectbox" | |
) | |
# Display the description of the selected approach | |
st.info(creative_approaches[selected_approach]) | |
# Store the selected approach | |
st.session_state.selected_approach = selected_approach | |
# Temperature slider | |
temperature = st.slider( | |
"Nivel de creatividad:", | |
min_value=0.0, | |
max_value=2.0, | |
value=1.0, | |
step=0.1, | |
help="Valores más altos generan ideas más creativas pero menos predecibles." | |
) | |
with col2: | |
if generate_button: | |
# Store the response in session state so it persists across reruns | |
with st.spinner("Creando tus ideas creativas..."): | |
st.session_state.creative_response = get_gemini_response( | |
product_service, | |
target_audience, | |
main_benefit, | |
tone_of_voice, | |
temperature, | |
file_content, | |
image_parts, | |
st.session_state.get("selected_approach", "") | |
# Removed contrast_level parameter | |
) | |
# Display the response if it exists in session state | |
if 'creative_response' in st.session_state: | |
st.write("### Conceptos Creativos") | |
# Format the response with custom styling | |
formatted_response = format_creative_response(st.session_state.creative_response) | |
# Use markdown with HTML to display the formatted response | |
st.markdown(formatted_response, unsafe_allow_html=True) | |
# Add download button if we have a valid response | |
if st.session_state.creative_response and not st.session_state.creative_response.startswith("Error") and not st.session_state.creative_response.startswith("Debes"): | |
# Get current timestamp for the filename | |
timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S") | |
# Prepare content for download (use the original unformatted response) | |
download_content = st.session_state.creative_response | |
# Download button | |
st.download_button( | |
label="DESCARGAR IDEAS CREATIVAS", | |
data=download_content, | |
file_name=f"conceptos_creativos_{timestamp}.txt", | |
mime="text/plain" | |
) |