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" | |
| ) |