Spaces:
Sleeping
Sleeping
| import streamlit as st | |
| import google.generativeai as genai | |
| import os | |
| import time | |
| from dotenv import load_dotenv | |
| from styles import get_custom_css, get_response_html_wrapper | |
| from formulas import offer_formulas | |
| from sophistication_levels import sophistication_levels # Import the sophistication levels | |
| import PyPDF2 | |
| import docx | |
| from PIL import Image | |
| import io | |
| # Import the bullet generator | |
| from bullets.generator import create_bullet_instruction | |
| # Import the bonus generator | |
| from bonuses.generator import create_bonus_instruction | |
| # Import the offer instruction generator | |
| from prompts import create_offer_instruction | |
| # Set page to wide mode to use full width | |
| st.set_page_config(layout="wide") | |
| # Load environment variables | |
| load_dotenv() | |
| # Configure Google Gemini API | |
| genai.configure(api_key=os.getenv('GOOGLE_API_KEY')) | |
| model = genai.GenerativeModel('gemini-2.0-flash') | |
| # Initialize session state variables if they don't exist | |
| if 'submitted' not in st.session_state: | |
| st.session_state.submitted = False | |
| if 'offer_result' not in st.session_state: | |
| st.session_state.offer_result = "" | |
| if 'generated' not in st.session_state: | |
| st.session_state.generated = False | |
| # Hide Streamlit menu and footer | |
| st.markdown(""" | |
| <style> | |
| #MainMenu {visibility: hidden;} | |
| footer {visibility: hidden;} | |
| header {visibility: hidden;} | |
| </style> | |
| """, unsafe_allow_html=True) | |
| # Custom CSS | |
| st.markdown(get_custom_css(), unsafe_allow_html=True) | |
| # App title and description | |
| st.markdown('<h1 style="text-align: center;">Great Offer Generator</h1>', unsafe_allow_html=True) | |
| st.markdown('<h3 style="text-align: center;">Transform your skills into compelling offers!</h3>', unsafe_allow_html=True) | |
| # Create two columns for layout - left column 40%, right column 60% | |
| col1, col2 = st.columns([4, 6]) | |
| # Main input section in left column | |
| with col1: | |
| # Define the generate_offer function first | |
| def handle_generate_button(): # Renamed to avoid conflict | |
| has_manual_input = bool(skills or product_service) | |
| has_file_input = bool(uploaded_file is not None and not is_image) | |
| has_image_input = bool(uploaded_file is not None and is_image) | |
| # Simple validation - check if we have at least one input type | |
| if not (has_manual_input or has_file_input or has_image_input): | |
| st.error('Por favor ingresa texto o sube un archivo/imagen') | |
| return | |
| st.session_state.submitted = True | |
| st.session_state.generated = False # Reset generated flag | |
| # Store inputs based on what's available | |
| if has_manual_input: | |
| st.session_state.skills = skills if skills else "" | |
| st.session_state.product_service = product_service if product_service else "" | |
| if has_file_input: | |
| st.session_state.file_content = file_content | |
| if has_image_input: | |
| st.session_state.image_parts = image_parts | |
| # Set input type based on what's available | |
| if has_image_input: | |
| if has_manual_input: | |
| st.session_state.input_type = "manual_image" | |
| else: | |
| st.session_state.input_type = "image" | |
| else: | |
| if has_manual_input and has_file_input: | |
| st.session_state.input_type = "both" | |
| elif has_file_input: | |
| st.session_state.input_type = "file" | |
| elif has_manual_input: | |
| st.session_state.input_type = "manual" | |
| # Store common settings | |
| st.session_state.target_audience = target_audience | |
| st.session_state.temperature = temperature | |
| st.session_state.formula_type = formula_type | |
| st.session_state.sophistication_level = sophistication_level # Store the sophistication level | |
| # Keep only the manual input tab | |
| with st.container(): | |
| skills = st.text_area('💪 Tus Habilidades', height=70, | |
| help='Lista tus habilidades y experiencia clave') | |
| product_service = st.text_area('🎯 Producto/Servicio', height=70, | |
| help='Describe tu producto o servicio') | |
| # Generate button moved here - right after product/service | |
| st.button('Generar Oferta 🎉', on_click=handle_generate_button) # Updated function name | |
| # Accordion for additional settings | |
| with st.expander('⚙️ Configuración Avanzada'): | |
| target_audience = st.text_area('👥 Público Objetivo', height=70, | |
| help='Describe tu cliente o público ideal') | |
| # Add file/image uploader here | |
| uploaded_file = st.file_uploader("📄 Sube un archivo o imagen", | |
| type=['txt', 'pdf', 'docx', 'jpg', 'jpeg', 'png']) | |
| if uploaded_file is not None: | |
| file_type = uploaded_file.name.split('.')[-1].lower() | |
| # Handle text files | |
| 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: | |
| import PyPDF2 | |
| 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: | |
| import docx | |
| 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 = "" | |
| # Remove success message - no notification shown | |
| # Set file type flag | |
| # Initialize is_image variable | |
| is_image = False | |
| # Handle image files | |
| elif file_type in ['jpg', 'jpeg', 'png']: | |
| try: | |
| image = Image.open(uploaded_file) | |
| st.image(image, caption="Imagen cargada", use_container_width=True) | |
| image_bytes = uploaded_file.getvalue() | |
| image_parts = [ | |
| { | |
| "mime_type": uploaded_file.type, | |
| "data": image_bytes | |
| } | |
| ] | |
| # Set file type flag | |
| is_image = True | |
| except Exception as e: | |
| st.error(f"Error al procesar la imagen: {str(e)}") | |
| is_image = False | |
| # Selector de fórmula | |
| formula_type = st.selectbox( | |
| '📋 Tipo de Fórmula', | |
| options=list(offer_formulas.keys()), | |
| help='Selecciona el tipo de fórmula para tu oferta' | |
| ) | |
| # Add the sophistication level selector | |
| sophistication_level = st.selectbox( | |
| '🎯 Nivel de Sofisticación del Mercado', | |
| options=list(sophistication_levels.keys()), | |
| help='Adapta tu mensaje según la madurez del mercado (Eugene Schwartz)' | |
| ) | |
| # Show example and strategy for the selected level | |
| if sophistication_level: | |
| st.info(f"**Estrategia:** {sophistication_levels[sophistication_level]['strategy']}\n\n**Ejemplo:** *{sophistication_levels[sophistication_level]['example']}*") | |
| temperature = st.slider('🌡️ Nivel de Creatividad', min_value=0.0, max_value=2.0, value=1.0, | |
| help='Valores más altos hacen que el resultado sea más creativo pero menos enfocado') | |
| # Results column | |
| # In the section where you're generating the offer | |
| with col2: | |
| if st.session_state.submitted and not st.session_state.generated: | |
| with st.spinner('Creando tu oferta perfecta...'): | |
| # Use the create_offer_instruction function to generate the prompt | |
| target_audience = st.session_state.target_audience if hasattr(st.session_state, 'target_audience') and st.session_state.target_audience else 'General audience' | |
| # Get product_service from session state or use the current value | |
| product_service_value = st.session_state.product_service if hasattr(st.session_state, 'product_service') and st.session_state.product_service else product_service | |
| # Get skills from session state or use empty string | |
| skills_value = st.session_state.skills if hasattr(st.session_state, 'skills') and st.session_state.skills else "" | |
| # Get sophistication level from session state or use default | |
| sophistication_level_value = st.session_state.sophistication_level if hasattr(st.session_state, 'sophistication_level') and st.session_state.sophistication_level else list(sophistication_levels.keys())[0] | |
| sophistication_data = sophistication_levels[sophistication_level_value] | |
| # Preparar el contenido del archivo si existe | |
| file_content = "" | |
| if hasattr(st.session_state, 'file_content') and st.session_state.input_type in ["file", "both"]: | |
| file_content = st.session_state.file_content | |
| # Get the instruction using the formula | |
| instruction = create_offer_instruction( | |
| target_audience=target_audience, | |
| product_name=product_service_value, | |
| selected_formula_name=st.session_state.formula_type, | |
| file_content=file_content, | |
| skills=skills_value, | |
| sophistication_level=sophistication_data # Pass the sophistication level data | |
| ) | |
| # Add instruction for generating benefit bullets based on the promise | |
| bullet_content = None | |
| if hasattr(st.session_state, 'file_content') and st.session_state.input_type in ["file", "both"]: | |
| bullet_content = st.session_state.file_content | |
| instruction += create_bullet_instruction( | |
| target_audience=target_audience, | |
| product_service=product_service_value, | |
| uploaded_content=bullet_content, | |
| skills=skills_value | |
| ) | |
| # Add instruction for generating bonuses that complement the offer | |
| instruction += create_bonus_instruction( | |
| target_audience=target_audience, | |
| product_service=product_service_value, # Changed from product_name to product_service | |
| selected_formula_name=st.session_state.formula_type, | |
| uploaded_content=bullet_content, | |
| skills=skills_value | |
| ) | |
| # We don't need this additional context anymore since we're passing skills directly to the generators | |
| # if st.session_state.input_type == "manual": | |
| # additional_context = f""" | |
| # Additional information: | |
| # Skills: {st.session_state.skills} | |
| # """ | |
| # instruction += additional_context | |
| try: | |
| generation_config = genai.GenerationConfig(temperature=st.session_state.temperature) | |
| if "image" in st.session_state.input_type: | |
| response = model.generate_content([instruction, st.session_state.image_parts[0]], generation_config=generation_config) | |
| else: | |
| response = model.generate_content(instruction, generation_config=generation_config) | |
| # Get the response text | |
| response_text = response.text | |
| # Cuando procesas la respuesta del modelo para la Oferta Dorada | |
| if st.session_state.formula_type == "Oferta Dorada": | |
| # Eliminar espacios al inicio de cada línea (indentación) | |
| lines = response_text.split('\n') | |
| lines = [line.lstrip() for line in lines] # Elimina espacios al inicio de cada línea | |
| # Asegurarse de que no haya líneas en blanco extras que puedan afectar el formato | |
| lines = [line for line in lines if line.strip()] | |
| response_text = '\n\n'.join(lines) | |
| # Natural integration of product name in the response | |
| if hasattr(st.session_state, 'product_service') and st.session_state.product_service: | |
| product_service_value = st.session_state.product_service | |
| # Split the text into lines to process each part of the formula | |
| lines = response_text.split('\n') | |
| # Process each line for proper product name integration | |
| for i in range(len(lines)): | |
| # For the second line (usually the promise in ALL CAPS), make product name uppercase | |
| if i == 1 and st.session_state.formula_type == "Oferta Dorada": | |
| lines[i] = lines[i].upper() | |
| # Remove quotes around product name in all lines | |
| lines[i] = lines[i].replace(f'"{product_service_value}"', product_service_value) | |
| lines[i] = lines[i].replace(f"'{product_service_value}'", product_service_value) | |
| # Ensure product name is properly capitalized in benefit descriptions | |
| if i >= 2 and "sistema" in lines[i].lower() and product_service_value.lower() not in lines[i].lower(): | |
| lines[i] = lines[i].replace("sistema", product_service_value, 1) | |
| if i >= 2 and "método" in lines[i].lower() and product_service_value.lower() not in lines[i].lower(): | |
| lines[i] = lines[i].replace("método", product_service_value, 1) | |
| # Rejoin the lines | |
| response_text = '\n'.join(lines) | |
| st.session_state.offer_result = response_text | |
| st.session_state.generated = True # Mark as generated | |
| except Exception as e: | |
| st.error(f'Ocurrió un error: {str(e)}') | |
| st.session_state.submitted = False | |
| # Display results if we have an offer result | |
| if st.session_state.generated: | |
| # Remove the visualization mode option | |
| # Display the formatted result directly | |
| st.markdown(get_response_html_wrapper(st.session_state.offer_result), unsafe_allow_html=True) | |
| # Add a small space | |
| st.markdown('<div style="height: 15px;"></div>', unsafe_allow_html=True) | |
| # Apply the custom button style before rendering the download button | |
| st.markdown('<style>div.stDownloadButton > button {your-custom-styles-here}</style>', unsafe_allow_html=True) | |
| st.download_button( | |
| label="Descargar Oferta", | |
| data=st.session_state.offer_result, | |
| file_name="oferta_generada.txt", | |
| mime="text/plain" | |
| ) | |
| # Footer | |
| st.markdown('---') | |
| st.markdown('Made with ❤️ by Jesús Cabrera') | |
| # Remove the duplicate functions at the bottom |