import streamlit as st from dotenv import load_dotenv import os import google.generativeai as genai from style import styles from prompts import create_instruction from consciousness_levels import CONSCIOUSNESS_LEVELS st.set_page_config(page_title="Generador de Cliente Ideal", page_icon="馃懁", layout="wide") # Cargar las variables de entorno load_dotenv() # Configurar la API de Google genai.configure(api_key=os.getenv("GOOGLE_API_KEY")) # Inicializar variables de estado en session_state si no existen if 'perfil_cliente' not in st.session_state: st.session_state.perfil_cliente = None if 'producto' not in st.session_state: st.session_state.producto = "" if 'habilidades' not in st.session_state: st.session_state.habilidades = "" if 'creatividad' not in st.session_state: st.session_state.creatividad = 1.0 # Update the default format in session state if 'formato' not in st.session_state: st.session_state.formato = "Jung's_Avatar" # Changed from previous format name if 'nivel_conciencia' not in st.session_state: # Usar el primer nivel del diccionario como valor predeterminado first_key = list(CONSCIOUSNESS_LEVELS.keys())[0] st.session_state.nivel_conciencia = first_key.replace("_", " ") # Funci贸n para generar el perfil de cliente ideal @st.cache_resource def get_model(temperature): generation_config = { "temperature": temperature, } return genai.GenerativeModel('gemini-2.0-flash', generation_config=generation_config) # REMOVED: The format selection dropdown that was here def generate_buyer_persona(product, skills, target_audience, temperature, consciousness_level="Ninguno", format_type="Jung's_Avatar"): if not product or not skills: return "Por favor, completa los campos de producto y habilidades." try: model = get_model(temperature) instruction = create_instruction( format_type=format_type, product_service=product, skills=skills, consciousness_level=consciousness_level, target_audience=target_audience, gender=None # Adding gender parameter with None as default ) # A帽adir instrucci贸n expl铆cita para respuesta en espa帽ol instruction += "\n\nIMPORTANTE: La respuesta debe estar completamente en espa帽ol." response = model.generate_content([instruction], generation_config={"temperature": temperature}) return response.parts[0].text if response and response.parts else "Error generando el perfil de cliente ideal." except Exception as e: return f"Error al generar el perfil: {str(e)}" # Modificar la funci贸n update_profile para que no use spinner def update_profile(): # Solo actualizar la variable de sesi贸n st.session_state.submitted = True # Leer el contenido del archivo manual.md si existe try: 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) except FileNotFoundError: st.sidebar.warning("Manual not found. Please create a manual.md file.") except Exception as e: st.sidebar.error(f"Error loading manual: {str(e)}") # Ocultar elementos de la interfaz st.markdown(styles["main_layout"], unsafe_allow_html=True) # Centrar el t铆tulo y el subt铆tulo st.markdown("

Generador de Perfil de Cliente Ideal

", unsafe_allow_html=True) st.markdown("

Crea un perfil detallado de tu cliente ideal basado en tu producto y habilidades.

", unsafe_allow_html=True) # A帽adir CSS personalizado para el bot贸n st.markdown(styles["button"], unsafe_allow_html=True) # A帽adir CSS personalizado para el bot贸n de descarga st.markdown(styles["download_button"], unsafe_allow_html=True) # Crear columnas col1, col2 = st.columns([1, 2]) # Columna de entrada with col1: product = st.text_area("驴Qu茅 producto o servicio ofreces?", value=st.session_state.producto, placeholder="Ejemplo: Curso de Ingl茅s", key="producto_input", height=70) st.session_state.producto = product skills = st.text_area("驴Cu谩les son tus habilidades principales?", value=st.session_state.habilidades, placeholder="Ejemplo: Ense帽anza, comunicaci贸n, dise帽o de contenidos", key="habilidades_input", height=70) st.session_state.habilidades = skills # Bot贸n para generar - Movido arriba del acorde贸n submit = st.button("CREAR MI CLIENTE IDEAL SO脩ADO 鉃も灓", on_click=update_profile) # Crear un acorde贸n para las opciones de personalizaci贸n with st.expander("Personaliza Tu Cliente Ideal So帽ado"): # Nuevo campo para p煤blico objetivo if 'publico_objetivo' not in st.session_state: st.session_state.publico_objetivo = "" target_audience = st.text_area("驴Cu谩l es tu p煤blico objetivo? (opcional)", value=st.session_state.publico_objetivo, placeholder="Ejemplo: Profesionales entre 25-40 a帽os interesados en desarrollo personal", key="publico_objetivo_input", height=70) st.session_state.publico_objetivo = target_audience # Selector de formato from format.format import buyer_persona_formats # Usar directamente las claves del diccionario sin filtrar format_type = st.selectbox( "Formato del perfil", options=list(buyer_persona_formats.keys()), format_func=lambda x: x.capitalize(), index=list(buyer_persona_formats.keys()).index(st.session_state.formato) if st.session_state.formato in buyer_persona_formats else 0, help="Selecciona el formato en el que se presentar谩 el perfil del cliente ideal" ) st.session_state.formato = format_type # Nivel de creatividad con slider temperature = st.slider("Nivel de creatividad", min_value=0.0, max_value=2.0, value=st.session_state.creatividad, step=0.1, key="creatividad_slider") st.session_state.creatividad = temperature # Selector de nivel de conciencia consciousness_options = [] for i, key in enumerate(CONSCIOUSNESS_LEVELS.keys(), 1): # Replace underscores with spaces in the key display_name = key.replace("_", " ") consciousness_options.append(f"Nivel {i} - {display_name}") nivel_conciencia_display = st.selectbox( "Nivel de conciencia del cliente ideal", consciousness_options, index=0, help="Selecciona el nivel de conciencia en el que se encuentra tu cliente ideal" ) # Extract the original key from the display name level_number = nivel_conciencia_display.split(" - ")[0].replace("Nivel ", "") original_key = list(CONSCIOUSNESS_LEVELS.keys())[int(level_number) - 1] nivel_conciencia = original_key.replace("_", " ") # Get the description from the CONSCIOUSNESS_LEVELS dictionary if original_key in CONSCIOUSNESS_LEVELS: nivel_info = CONSCIOUSNESS_LEVELS[original_key]["estado_mental"] st.info(f"**{nivel_conciencia}**: {nivel_info}") st.session_state.nivel_conciencia = nivel_conciencia # Columna de resultados with col2: # Verificar si se ha enviado el formulario if 'submitted' in st.session_state and st.session_state.submitted: if st.session_state.producto and st.session_state.habilidades: with st.spinner("Creando tu Cliente Ideal So帽ado..."): # Generar el perfil del cliente buyer_persona = generate_buyer_persona( st.session_state.producto, st.session_state.habilidades, st.session_state.publico_objetivo, st.session_state.creatividad, st.session_state.nivel_conciencia, st.session_state.formato ) # Mejorar la limpieza del perfil para eliminar estructuras JSON/diccionario if isinstance(buyer_persona, str): import re import json # Eliminar marcadores de c贸digo markdown como ```json, ```python, etc. buyer_persona = re.sub(r'```[a-z]*\n', '', buyer_persona) buyer_persona = re.sub(r'```', '', buyer_persona) # Intentar detectar y limpiar formato JSON if '{' in buyer_persona and '}' in buyer_persona: # Intentar extraer solo el contenido textual, eliminando estructuras JSON # Primero, intentar encontrar el JSON completo json_pattern = r'(\{.*?\})' json_matches = re.findall(json_pattern, buyer_persona, re.DOTALL) if json_matches: for json_match in json_matches: # Intentar extraer el contenido real del JSON try: # Reemplazar el JSON con una cadena vac铆a buyer_persona = buyer_persona.replace(json_match, '') except: pass # Limpiar l铆neas que parecen ser parte de un diccionario lines = buyer_persona.split('\n') cleaned_lines = [] for line in lines: # Omitir l铆neas que parecen ser claves de diccionario if not re.match(r'^\s*["\']?[a-zA-Z_]+["\']?\s*:', line): cleaned_lines.append(line) buyer_persona = '\n'.join(cleaned_lines) # Eliminar llaves sueltas y corchetes buyer_persona = re.sub(r'[{}[\]]', '', buyer_persona) # Eliminar comillas y dos puntos que parecen ser de un diccionario buyer_persona = re.sub(r'["\']\s*:\s*["\']', '', buyer_persona) # Eliminar "template", "description", "example" y otras palabras clave comunes en el formato keywords = ["template", "description", "example", "Nivel de conciencia"] for keyword in keywords: buyer_persona = re.sub(rf'["\']?{keyword}["\']?\s*:\s*["\']?', '', buyer_persona) # Eliminar comillas sueltas buyer_persona = re.sub(r'^\s*["\']|["\']$', '', buyer_persona) # Normalizar espacios en blanco y sangr铆as lines = buyer_persona.split('\n') cleaned_lines = [] for line in lines: # Eliminar sangr铆as excesivas pero mantener estructura b谩sica cleaned_line = line.strip() if cleaned_line: # Solo agregar l铆neas no vac铆as cleaned_lines.append(cleaned_line) # Unir las l铆neas con saltos de l铆nea adecuados buyer_persona = '\n'.join(cleaned_lines) # Eliminar espacios en blanco adicionales buyer_persona = re.sub(r'\n\s*\n', '\n\n', buyer_persona) buyer_persona = buyer_persona.strip() # Al inicio del archivo, junto con las otras inicializaciones if 'perfil_cliente_plain' not in st.session_state: st.session_state.perfil_cliente_plain = None # Guardar versi贸n sin formato antes de aplicar estilos HTML st.session_state.perfil_cliente_plain = buyer_persona # Convertir los asteriscos de formato Markdown a HTML para una correcta visualizaci贸n # Convertir **texto** a texto buyer_persona = re.sub(r'\*\*(.*?)\*\*', r'\1', buyer_persona) # Convertir *texto* a texto buyer_persona = re.sub(r'\*(.*?)\*', r'\1', buyer_persona) # Mejorar el formato HTML para una mejor visualizaci贸n buyer_persona = """
{}
""".format(buyer_persona.replace('\n', '

')) # Guardar en session_state st.session_state.perfil_cliente = buyer_persona # Resetear el estado de env铆o st.session_state.submitted = False # Mostrar resultados if not isinstance(st.session_state.perfil_cliente, str): st.error("Error al generar el perfil de cliente ideal") else: # Crear un contenedor para el resultado result_container = st.container() # Aplicar estilos del contenedor st.markdown(styles["results_container"], unsafe_allow_html=True) with result_container: # Mostrar el t铆tulo st.markdown("
Tu Cliente Ideal
", unsafe_allow_html=True) # Mostrar el contenido como HTML para asegurar el formato correcto st.markdown(st.session_state.perfil_cliente, unsafe_allow_html=True) # Opci贸n para descargar # En la secci贸n de descarga anterior, asegurarse de usar la versi贸n plain si existe st.download_button( label="DESCARGAR MI CLIENTE SO脩ADO 鉃も灓", data=st.session_state.perfil_cliente_plain if st.session_state.perfil_cliente_plain else st.session_state.perfil_cliente, file_name="cliente_ideal.txt", mime="text/plain" ) else: st.warning("Por favor, completa los campos de producto y habilidades antes de generar el perfil.") # Mostrar resultados anteriores si existen elif st.session_state.perfil_cliente: # Crear un contenedor para el resultado result_container = st.container() # Aplicar estilos del contenedor st.markdown(styles["results_container"], unsafe_allow_html=True) with result_container: # Mostrar el t铆tulo st.markdown("
Tu Cliente Ideal
", unsafe_allow_html=True) # Mostrar el contenido como HTML para asegurar el formato correcto st.markdown(st.session_state.perfil_cliente, unsafe_allow_html=True) # Opci贸n para descargar st.download_button( label="DESCARGAR MI CLIENTE SO脩ADO 鉃も灓", data=st.session_state.perfil_cliente, file_name="cliente_ideal.txt", mime="text/plain" )