from langchain_groq import ChatGroq from langchain_openai import ChatOpenAI from langchain_huggingface import HuggingFaceEmbeddings from langchain_pinecone import PineconeVectorStore from langchain_core.output_parsers import JsonOutputParser, StrOutputParser from langchain.output_parsers import ResponseSchema, StructuredOutputParser from langchain_core.prompts import ChatPromptTemplate from streamlit_option_menu import option_menu from elevenlabs.client import ElevenLabs from supabase import create_client, Client from openai import OpenAI from dotenv import load_dotenv import os import io import json import base64 import requests import pandas as pd import streamlit as st ##################### # CONFIGURACIÓN # ##################### # Función de configuración de embedding @st.cache_resource() def load_embedding_model(model_name): return HuggingFaceEmbeddings(model_name=model_name) # Función de configuración de supabase @st.cache_resource() def load_supabase(): url: str = os.environ.get("SUPABASE_URL") key: str = os.environ.get("SUPABASE_KEY") supabase: Client = create_client(url, key) return supabase # Función de configuración de pinecone @st.cache_resource() def load_pinecone(index_name, namespace, embedding): vectorstore = PineconeVectorStore(index_name=index_name, namespace=namespace, embedding=embedding) return vectorstore # Función de configuración def configuracion(): # API Keys # load_dotenv() # Cabecera st.header(":gear: Configuración", divider='gray') # API Keys # hf_token = os.getenv('HF_TOKEN') groq_api_key = os.getenv('GROQ_API_KEY') openai_api_key = os.getenv('OPENAI_API_KEY') pinecone_api_key = os.getenv('PINECONE_API_KEY') supabase_key = os.getenv('SUPABASE_KEY') supabase_url = os.getenv('SUPABASE_URL') eleven_api_key = os.getenv('ELEVEN_API_KEY') langsmith_api_key = os.getenv('LANGSMITH_API_KEY') langchain_project = os.getenv('LANGCHAIN_PROJECT') langchain_tracing_v2 = os.getenv('LANGCHAIN_TRACING_V2') with st.spinner("Configurando inicialmente el sistema..."): # Modelo de lenguaje # modelos_llm = [ 'gpt-4o', 'llama3-70b-8192', 'llama3-8b-8192', 'mixtral-8x7b-32768', 'gemma-7b-it', ] modelo_llm = st.selectbox('Modelo de lenguaje', list(modelos_llm), index=modelos_llm.index(st.session_state.modelo_llm), help="Especifica el modelo de lenguaje utilizado") # Temperatura temperature = st.slider("Temperatura", 0.0, 2.0, 1.0, disabled=False, step=0.1) if modelo_llm == "gpt-4o": llm = ChatOpenAI(model=modelo_llm) llm_creative = ChatOpenAI(model=modelo_llm, temperature=temperature) else: llm = ChatGroq(model=modelo_llm) llm_creative = ChatGroq(model=modelo_llm, temperature=temperature) # Modelo de lenguaje multimodal llm_images = OpenAI() # Embeddings # model_name = 'intfloat/multilingual-e5-small' model_name = st.text_input("Modelo de embedding:", value=model_name, disabled=True, help="Indica el modelo de _embedding_ utilizado en la vectorstore") #embedding = HuggingFaceEmbeddings(model_name=model_name) embedding = load_embedding_model(model_name) # Pinecone # index_name = "poemas-intfloat-multilingual-e5-small" namespace = "poesiacastellana" vectorstore = PineconeVectorStore(index_name=index_name, namespace=namespace, embedding=embedding) col1, col2 = st.columns(2) with col1: input1 = st.text_input("Index", value=index_name, disabled=True, help="Nombre del _index_ del vectorstore en Pinecone para RAG") with col2: input2 = st.text_input("namespace", value=namespace, disabled=True, help="Nombre del _namespace_ del vectorstore en Pinecone") # Supabase # url: str = os.environ.get("SUPABASE_URL") key: str = os.environ.get("SUPABASE_KEY") supabase: Client = create_client(url, key) pass st.success("¡Listo, ya puedes generar poemas!", icon='⚙️') # Variables globales st.session_state.modelo_llm = modelo_llm st.session_state.temperature = temperature st.session_state.llm = llm st.session_state.llm_creative = llm_creative st.session_state.llm_images = llm_images st.session_state.embedding = embedding st.session_state.db = vectorstore st.session_state.supabase = supabase # API Keys # with st.expander("API Keys"): GROQ_API_KEY = st.text_input('Groq API Key', value=groq_api_key, type='password') OPENAI_API_KEY = st.text_input('OpenAI API Key', value=openai_api_key, type='password') HF_TOKEN = st.text_input('HuggingFace API Key', value=hf_token, type='password') PINECONE_API_KEY = st.text_input('Pinecone API Key', value=pinecone_api_key, type='password') ELEVEN_API_KEY = st.text_input('ElevenLabs API Key', value=eleven_api_key, type='password') # Botón actualizar st.toast(f"Modelo de lenguaje: **{modelo_llm}** [🌡️{temperature}]", icon='📢') if st.button("Actualizar", type="primary", use_container_width=True, disabled=True): return # ----- Función de configuración ----------------------------------------------------------- @st.cache_resource() def __init__(): # API Keys # load_dotenv() # Modelo de lenguaje modelo_llm = 'gpt-4o' temperature = 1 #llm = ChatGroq(model=modelo_llm) #llm_creative = ChatGroq(model=modelo_llm, temperature=temperature) llm = ChatOpenAI(model=modelo_llm) llm_creative = ChatOpenAI(model=modelo_llm, temperature=temperature) # Modelo de lenguaje multimodal llm_images = OpenAI() # Configuración inicial with st.spinner("Configurando inicialmente el sistema..."): # Embeddings model_name = 'intfloat/multilingual-e5-small' embedding = load_embedding_model(model_name) # Pinecone index_name = "poemas-intfloat-multilingual-e5-small" namespace = "poesiacastellana" vectorstore = PineconeVectorStore(index_name=index_name, namespace=namespace, embedding=embedding) # Supabase url: str = os.environ.get("SUPABASE_URL") key: str = os.environ.get("SUPABASE_KEY") supabase: Client = create_client(url, key) pass #st.success("¡Listo, ya puedes generar poemas!", icon='⚙️') # Globals st.session_state.modelo_llm = modelo_llm st.session_state.temperature = temperature st.session_state.llm = llm st.session_state.llm_creative = llm_creative st.session_state.llm_images = llm_images st.session_state.supabase = supabase st.session_state.embedding = embedding st.session_state.db = vectorstore ##################### # GRAFO # ##################### # ----- Función para desarrollar la metodología ------------------------------------------------ def grafo(): # Añadir una imagen al lado del título col1, col2 = st.columns([4,20]) with col1: st.image("img/bot_.webp", width=100) with col2: st.title("Generador de poemas") # Botones de sugerencia para el tema/estilo #tema_suggestions = ["Amor", "Vida", "Misterio", "Aventura", "Sueños", "Amistad", "Impermanencia"] #estilo_suggestions = ["Romántico", "Gótico", "Épico", "Lírico", "Soneto", "Haiku", "Verso libre", "Gen27"] #tema_seleccionado = st.multiselect("Selecciona las opciones deseadas:", tema_suggestions) #estilo_seleccionado = st.multiselect("Elige un movimiento poético o un poeta cuyo estilo quieras imitar:", estilo_suggestions) # Campo para el tema placeholder = "e.g., «el paso del tiempo», «homo viator», «el amor no declarado», «el hogar» ..." tema = st.text_input("Tema:", placeholder=placeholder, help="Especifa el **tema** sobre el que quieres escribir el poema") # Campo para el estilo/estructura placeholder = "e.g., «Verso libre», «Generación del 27», «Rosalía de Castro», «soneto», «lirismo» ..." estilo = st.text_input("Estilo:", placeholder=placeholder, help="Especifa el *estilo* con el que quieres escribir el poema") # Mostrar el conocimiento experto with st.expander("Expertise Knowledge", icon="👩💻"): for item in st.session_state.expertise: st.markdown(f"```\n{item}\n```") # Botón para generar el poema if st.button("Generar Poema", type="primary", use_container_width=True): # Comprobamos que se ha introducido un tema if not tema: st.warning("Por favor, ingresa un _tema_ para generar el poema.", icon="🪶") return # Salir # Reseteamos globals globals_reset() # Variables globales st.session_state.tema = tema st.session_state.estilo = estilo st.session_state.professor = ["No hay revisiones del profesor."] # Generación del contexto try: with st.spinner("Recuperando conocimiento aumentado..."): rag_retrieval() pass st.success(":floppy_disk: ¡Conocimiento aumentado recuperado!") except Exception as e: st.warning(f"No se ha podido recuperar conocimiento.", icon="💾") # Generación del conocimiento if estilo: try: with st.spinner("Generando conocimiento experto..."): generated_knowledge() pass st.success(":notebook: ¡Conocimiento experto generado!") except Exception as e: st.warning(f"No se ha podido generar el conocimiento experto.", icon="✍️") else: st.warning(f"No se ha indicado ningún poeta o estilo poético de referencia.", icon="✍️") # Generación del borrador poético try: with st.spinner("Generando borrador poético..."): draft_poem() st.success(":memo: ¡Borrador poético generado!") except Exception as e: st.warning(f"No se ha podido generar el borrador poético. {e}", icon='📃') # Comprobación de copia o plagio #with st.spinner("Comprobando copia o plagio..."): #plagiarism_checker() # pass #st.success(":thumbsup: ¡Comprobación terminada!") st.warning("La comprobación de copia o plagio está desactivada.", icon="🕵️") # Generando reflexion try: with st.spinner("El catedrático está revisando el borrador poético..."): reflexion_catedratico() pass st.success(":female-student: ¡Borrador poético revisado!") except Exception as e: st.warning(f"No se ha podido revisar el borrador poético.", icon="👩🎓") # Generando poema try: with st.spinner("Generando el poema..."): st.session_state.poema = final_poem(st.session_state.draft_poem, st.session_state.reflexion) pass st.success(":scroll: ¡Poema generado!") except Exception as e: st.warning(f"No se ha podido generar el poema.", icon="📜") # Almacenar poema en Supabase almacenar("insert") # Mostrar poema st.markdown(f"```\n{st.session_state.poema}\n```") ##################### # AGENTES # ##################### # ----- Función para mostrar la recuperación aumentada ----------------------------------- def rag_retrieval(): # Define the filter condition based on the 'author' metadata field filter = {} # Define el número de resultados k=20 # Recupera los valores del grafo tema = st.session_state.tema # Perform the search: topic, human documents = st.session_state.db.similarity_search(tema, k=k, filter=filter) #db.similarity_search_with_score # Armamos la lista rag_poems = [doc.page_content for doc in documents] # Variable global st.session_state.rag = rag_poems # ----- Función para mostrar el conocimiento experto ------------------------------------- def expertise_knowledge(): # Mostramos una cabecera st.header(":female-technologist: Expertise Knowledge (HITL)", divider='red') # Campo de texto para ingresar poemas poema = st.text_area("Poema de referencia", height=300, help="Añade aquí los poemas de referencia que sirvan como ejemplo para la elaboración del poema. Pulsa _Actualziar_ para limpiar el conjunto de poemas existente.") # Botones para añadir y actualizar col1, col2 = st.columns(2, gap="small") with col1: if st.button("Añadir", use_container_width=True): st.session_state.expertise.insert(0, f"Poema:\n{poema.strip()}") poema = "" # Limpiar el campo poema with col2: if st.button("Actualizar", type="primary", use_container_width=True): st.session_state.expertise = [f"Poema:\n{poema.strip()}"] poema = "" # Limpiar el campo poema # Mostrar el conocimiento experto st.divider() #st.write("Expertise Knowledge:") for poem in st.session_state.expertise: st.markdown(f"```\n{poem}\n```") # ----- Función para mostrar el conocimiento generado ------------------------------------- def generated_knowledge(): # Armamos el prompt system = """ Eres un ratón de biblioteca y un profesor experto de literatura de una prestigiosa universidad con gran capacidad de \ sintetizar características de corrientes y estilos literarios. Tu objetivo es proporcionar las características principales \ y los detalles de las consultas académicas que te hagan sobre el estilo poético de '''{style_of}'''. Escribe las \ características principales sobre el estilo literario, movimiento o generación definido en el estilo o en el poeta nombrado, \ que sirvan de guía para la elaboración de nuevos textos que imiten el estilo indicado o al poeta solicitado. Es importante que no escribas ningún poema, tan solo enumera un listado detallado de características, que sean útiles \ para poder reproducirse y aplicarse en la generación de nuevos textos. """ human = 'Devuelve SOLO un listado detallado de las características en formato JSON. \ Formato: {{ "Características": ["característica 1", "característica 2", "característica 3"]}}' prompt = ChatPromptTemplate.from_messages([ ("system", system), ("human", human), ]) # Recupera los valores del grafo style_of = st.session_state.estilo # Lanzamos la consulta chain = prompt | st.session_state.llm | JsonOutputParser() knowledge = chain.invoke({"style_of": style_of}) # Parser knowledge_values = list(knowledge.values())[0] # Valor global st.session_state.generated = knowledge_values # ----- Función para generar el borrado poético------ ------------------------------------- def draft_poem(): # Armamos el prompt system = """ Eres un maestro de la poesía, capaz de escribir poemas que evocan emociones poderosas y obras maestras poéticas \ que capten la esencia del tema especificado de forma profunda y evocadora. Escribes desde la belleza. Cada línea \ cuidadosamente elaborada proyecta una imagen vívida, evocando emociones fuertes y hermosas con una destreza poética \ que deja sin aliento al lector. Tu estilo es una fusión de metáforas, aliteración y cadencia rítmica. Las imágenes que evocan tus palabras son originales, creativas y únicas, alejándose de los tópicos y dotadas de un \ significado profundo y auténtico. Tus poemas están escritos para recitarse y leerse en voz alta. Se hace hincapié en \ que la escritura sea vívida y detallada, al tiempo que se garantiza que los poemas tengan un mensaje profundo que \ resuene en el lector mucho después de que haya terminado de leerlos. El objetivo es crear una poesía impactante y \ bella que deje una impresión duradera en el público. Haces un uso de la rima es muy sutil, SOLO la utilizas cuando es necesaria. Si las características proporcionadas no \ indican lo contrario, haz un uso responsable y limitado de ella. Esta es una lista de poemas que debes utilizar como EJEMPLOS e inspiración para escribir el poema: '''{expertise}''' Aquí tienes una lista de poemas con una temática similar que puedes usar para inspirarte: ''' {rag_poems} ''' """ human = ( "El poema debe escribirse atendiendo a las siguientes CARACTERÍSTICAS: " "\n'''" "\n * 18 versos o menos, no debes superar NUNCA esta cantidad " "\n * {knowledge} " "\n''' " "\n\nEscribe un BREVE poema de menos de 18 versos sobre el tema de '''{topic}'''. " "No hagas comentarios. " + "Esto es muy importante para mi carrera." ) prompt = ChatPromptTemplate.from_messages([ ("system", system), ("human", human), ]) # Recupera los valores del grafo topic = st.session_state.tema knowledge = st.session_state.generated expertise = st.session_state.expertise rag_poems = st.session_state.rag # Convertir las listas en texto knowledge = '\n * '.join(knowledge) expertise = '\n\n '.join(expertise) rag_poems = ' '.join(rag_poems) # Lanzamos la consulta chain = prompt | st.session_state.llm_creative | StrOutputParser() draft_poem = chain.invoke({"topic": topic, "knowledge": knowledge, "expertise": expertise, "rag_poems": rag_poems}) # Valor global st.session_state.draft_poem = draft_poem # ----- Función para generar el poema final ------------------------------------------------- def final_poem(poema, review): # Armamos el prompt system = """ Eres un experto escritor de poesía que mejora los poemas. Tu objetivo es reescribir el poema proporcionado \ a partir de los comentarios proporcionadas por el profesor experto. \ POEMA: ''' {poema} ''' COMENTARIOS del profesor experto: ''' * {review} ''' """ human=( "Escribe solo el poema final mejorado, el mejor posible. " "No hagas comentarios, ni aclaraciones sobre lo que has modificado. Esto es muy importante para mi carrera." ) prompt = ChatPromptTemplate.from_messages([ ("system", system), ("human", human), ]) # Convertir las listas en texto review = '\n * '.join(list(review)) # Lanzamos la consulta chain = prompt | st.session_state.llm_creative | StrOutputParser() final_poem = chain.invoke({"poema": poema, "review": review}) # Valor global return final_poem # ----- Función para comprobar copia u overfitting ---------------------------------------- def plagiarism_checker(): ''' ¿Funciona? ''' # Definimos el tipo de salida response_schemas = [ ResponseSchema(name="plagio", description="Valor binario que informa de la existencia de plagio: (Sí, No)"), ResponseSchema( name="versos", description="Versos en los cuales se ha detectado una copia literal desde una fuente proporcionada.", ), ] output_parser = StructuredOutputParser.from_response_schemas(response_schemas) format_instructions = output_parser.get_format_instructions() # Armamos el prompt system = """ Te voy a proporcionar un POEMA y un CONTEXTO. Tu objetivo es verificar si hay frases del contexto copiadas de \ forma literal en el poema, lo cual podría considerarse plagio o copia. Proporcióname un análisis detallado indicando \ las frases exactas si se encuentran coincidencias. POEMA: ''' {draft_poem} ''' CONTEXTO:\n'''{expertise} \n {rag_poems}\n'''. """ human = "Analiza y detecta las frases o estrofas que han sido copiadas o plagiadas. {format_instructions}" prompt = ChatPromptTemplate.from_messages([ ("system", system), ("human", human), ]) # Recupera los valores del grafo draft_poem = st.session_state.draft_poem expertise = st.session_state.expertise rag_poems = st.session_state.rag # Convertir las listas en texto expertise = '\n\n '.join(expertise) rag_poems = ' '.join(rag_poems) # Lanzamos la consulta chain = prompt | st.session_state.llm | output_parser plagiarism = chain.invoke({"draft_poem": draft_poem, "expertise": expertise, "rag_poems": rag_poems, "format_instructions": format_instructions}) # Parser #plagiarism = list(plagiarism.values())[0] print(plagiarism) # Valor global st.session_state.plagiarism = plagiarism # ----- Función para mostrar el conocimiento generado ------------------------------------- def reflexion_catedratico(): # Armamos el prompt system = """ Eres un catedrático experto en poesía de una prestigiosa universidad con un profundo conocimiento y experiencia \ en el análisis de poemas. Tienes ojo crítico y una pasión por la perfección literaria. Tu objetivo es revisar y \ sugerir mejoras para los poemas. Evalúas y analizas la calidad del poema recibido y proporcionas comentarios constructivos que permitan mejorar el resultado final. Este es el poema: ''' {draft_poem} ''' """ #Devuelve un listado detallado de las características en formato JSON con una única clave 'comentarios'. human = 'Devuelve SOLO un listado detallado de los comentarios en formato JSON. \ Formato: {{ "comentarios": ["comentario 1", "comentario 2", "comentario 3"]}}' prompt = ChatPromptTemplate.from_messages([ ("system", system), ("human", human), ]) # Recupera los valores del grafo draft_poem = st.session_state.draft_poem # Lanzamos la consulta chain = prompt | st.session_state.llm | JsonOutputParser() reflexion = chain.invoke({"draft_poem": draft_poem}) # Parser comentarios = list(reflexion.values())[0] # Valor global st.session_state.reflexion = comentarios # ----- Función para mostrar el conocimiento experto ------------------------------------- def professor_reflexion(): st.header(":female-student: Professor Reflexion (HITL)", divider='red') # Mostrar el texto combinado st.write("Poema") st.markdown(f"```\n{st.session_state.poema}\n```") # Valor global if st.session_state.poema != "No hay poema elaborado.": col1, col2 = st.columns(2) with col1: # Audio voz = st.button("Recitar 🎙️", use_container_width=True, help="Recita el poema en alto") with col2: # Imagen imagen = st.button("Imaginar 🎨", use_container_width=True, help="Genera una imagen a partir del poema") if voz: try: with st.spinner("Generando el audio con el poema recitado..."): # Generamos el audio audio = recitar(st.session_state.poema) st.audio(audio, format="audio/mp3") # Guardar el audio codificado en supabase base64_audio = base64.b64encode(audio).decode('utf-8') # Globals st.session_state.audio = audio st.session_state.base64_audio = base64_audio # Almacenamos almacenar("update") pass st.success("¡Audio poético generado!", icon='🎙️') except Exception as e: # Manejo de la excepción st.error(f"No dispones de créditos suficientes para realizar esta operación.") if imagen: try: with st.spinner("Generando la imagen poética a partir del poema..."): # Generamos la imagen url_imagen, link = pintar(st.session_state.draft_poem) # Mostrar la imagen st.image(url_imagen, use_column_width="always") st.markdown(link, unsafe_allow_html=True) # Guardar la imagen codificada en supabase image_data = io.BytesIO(requests.get(url_imagen).content) base64_image = base64.b64encode(image_data.getvalue()).decode('utf-8') #image_data = io.BytesIO(base64.b64decode(base64_image)) # Globals st.session_state.url_imagen = url_imagen st.session_state.base64_image = base64_image st.session_state.image_data = image_data # Almacenamos la url de la imagen generada en Supabase almacenar("update") pass st.success("¡Imagen poética generada!", icon='🎨') except Exception as e: # Manejo de la excepción st.error(f"No dispones de créditos suficientes para realizar esta operación.", e) # Crear cuatro campos de textarea professor = st.text_area("Comentarios, crítica y sugerencias", help="Escribe aquí la revisión experta del catedrático.") # Reflexión if st.button("Escribir", type="primary", use_container_width=True): # Parser st.session_state.professor = [professor] st.session_state.expert_poem = st.session_state.poema with st.spinner("Reformando el poema..."): st.session_state.poema = final_poem(st.session_state.poema, st.session_state.professor) pass st.success("¡Poema reformado! :thumbsup:") # Almacenamos el poema en Supabase almacenar("update") # Mostramos el poema elaborado st.markdown(f"```{st.session_state.poema}```") ###################### # Funciones útiles # ###################### # ----- Función para mostrar el resumen del poema generado ---------------------------------- @st.cache_data() def pintar(poema): # Lanzamos la consulta al modelo de lenguaje multimodal prompt = "Dibuja una imagen que represente de forma abstracta el siguiente poema. No incluyas NINGUN texto. No incluyas texto. POEMA:" + poema response = st.session_state.llm_images.images.generate( model="dall-e-3", prompt=prompt, size="1024x1024", quality="standard", n=1, ) # Capturar el link url_imagen = response.data[0].url # Crear el enlace en Markdown link_text = "Haz clic aquí para ver la imagen en grande" link = f'[{link_text}]({url_imagen})' return url_imagen, link # ----- Función para mostrar el resumen del poema generado ---------------------------------- @st.cache_data() def recitar(poema): eleven_api_key = os.environ.get("ELEVEN_API_KEY") client = ElevenLabs( api_key=eleven_api_key, ) audio = client.generate( text=poema, voice="Sara Martin 3", model="eleven_multilingual_v2" ) audio_bytes = b'' for chunk in audio: audio_bytes += chunk return audio_bytes # ----- Función para resetear los valores globals ------------------------------------------ def globals_reset(): st.session_state.tema = "" st.session_state.estilo = "" st.session_state.generated = ["No hay conocimiento generado."] #st.session_state.expertise = ["No hay conocimiento experto proporcionado."] st.session_state.rag = ["No hay conocimiento aumentado por recuperación."] st.session_state.draft_poem = "No hay borrador poético." st.session_state.reflexion = ["No hay revisiones del catedrático."] st.session_state.plagiarism = "No aplica." st.session_state.professor = ["No hay revisiones del profesor."] st.session_state.expert_poem = "" st.session_state.poema = "No hay poema elaborado." st.session_state.url_imagen = "" st.session_state.base64_image = "" st.session_state.base64_audio = "" st.session_state.image_data = "" st.session_state.audio = "" # ----- Función para inicializar los valores globals ---------------------------------------- def globals_setup(): # Inicializamos las globals if "draft_poem" not in st.session_state: st.session_state.draft_poem = "No hay borrador poético." #st.session_state.expert_poem = "" st.session_state.poema = "No hay poema elaborado." st.session_state.plagiarism = "No aplica." st.session_state.url_imagen = "" st.session_state.base64_image = "" st.session_state.image_data = "" st.session_state.audio = "" if "generated" not in st.session_state: st.session_state.generated = ["No hay conocimiento generado."] if "expertise" not in st.session_state: st.session_state.expertise = ["No hay conocimiento experto proporcionado."] if "rag" not in st.session_state: st.session_state.rag = ["No hay conocimiento aumentado por recuperación."] if "reflexion" not in st.session_state: st.session_state.reflexion = ["No hay revisiones del catedrático."] if "professor" not in st.session_state: st.session_state.professor = ["No hay revisiones del profesor."] if 'tema' not in st.session_state: st.session_state.tema = "" if 'estilo' not in st.session_state: st.session_state.estilo = "" if "modelo_llm" not in st.session_state: st.session_state.modelo_llm = "gpt-4o" if "supabase" not in st.session_state: st.session_state.supabase = "" if "db" not in st.session_state: st.session_state.db = "" # ----- Función para almacenar el registro en la base de datos ---------------------------- def almacenar(accion): # Insertar los datos en la tabla data = { "model": st.session_state.modelo_llm, "temperature": st.session_state.temperature, "topic": st.session_state.tema, "style_of": st.session_state.estilo, "knowledge": st.session_state.generated, "expertise": st.session_state.expertise, "rag_poems": st.session_state.rag, "draft_poem": st.session_state.draft_poem, "feedback": st.session_state.reflexion, "plagiarism": st.session_state.plagiarism, "professor": st.session_state.professor, "expert_poem": st.session_state.expert_poem, "final_poem": st.session_state.poema, "url_imagen": st.session_state.url_imagen, "base64_image": st.session_state.base64_image, "base64_audio": st.session_state.base64_audio, } # Almacenamos los poemas generados if accion == "insert": # Añadimos registro data, count = st.session_state.supabase.table("Poemas").insert(data).execute() # Recuperamos el id insertado st.session_state.id = data[1][0]['id'] if accion == "update": id = st.session_state.id data, count = st.session_state.supabase.table("Poemas").update(data).eq("id", id).execute() # ----- Almacena en globals los datos del poema recuperado ---------------------------------- def recuperar(id): # Recuperamos el poema poema = st.session_state.supabase.table("Poemas").select("*").eq("id", id).execute() st.session_state.id = poema.data[0]["id"] st.session_state.tema = poema.data[0]["topic"] st.session_state.estilo = poema.data[0]["style_of"] st.session_state.generated = json.loads(poema.data[0]["knowledge"]) st.session_state.expertise = json.loads(poema.data[0]["expertise"]) st.session_state.rag = json.loads(poema.data[0]["rag_poems"]) st.session_state.draft_poem = poema.data[0]["draft_poem"] st.session_state.reflexion = json.loads(poema.data[0]["feedback"]) st.session_state.plagiarism = poema.data[0]["plagiarism"] st.session_state.professor = json.loads(poema.data[0]["professor"]) st.session_state.expert_poem = poema.data[0]["expert_poem"] st.session_state.poema = poema.data[0]["final_poem"] st.session_state.url_imagen = poema.data[0]["url_imagen"] st.session_state.base64_image = poema.data[0]["base64_image"] if st.session_state.base64_image: st.session_state.image_data = io.BytesIO(base64.b64decode(st.session_state.base64_image)) else: st.session_state.image_data = "" st.session_state.base64_audio = poema.data[0]["base64_audio"] if st.session_state.base64_audio: st.session_state.audio = io.BytesIO(base64.b64decode(st.session_state.base64_audio)) else: st.session_state.audio = "" # ----- Función para recuperar los poemas almacenados --------------------------------------- def historial(): # Cabecera st.header("⏳ Historial de poemas", divider='gray') # Recuperamos poemas de la base de datos st.session_state.poemas = st.session_state.supabase.table("Poemas").select("*").order("created_at", desc=True).execute() # Convertir los datos a un DataFrame de pandas df = pd.DataFrame(st.session_state.poemas.data) # Mostrar el DataFrame en una tabla #st.dataframe(df) from datetime import datetime # Cadena de texto con formato de fecha y hora fecha_str = "2024-06-14T07:46:08.918696+00:00" # Mostrar los poemas for i in range(len(df)): col1, col2 = st.columns([11, 1]) with col1: # Obtenemos valores topic = df.iloc[i]['topic'] estilo = df.iloc[i]['style_of'] created_at = df.iloc[i]['created_at'] poema = df.iloc[i]['draft_poem'] id = df.iloc[i]['id'] modelo = df.iloc[i]['model'] temperatura = df.iloc[i]['temperature'] professor = df.iloc[i]['professor'] base64_audio = "audio" if df.iloc[i]['base64_audio'] else "" base64_image = "image" if df.iloc[i]['base64_image'] else "" # Convertir created_at a objeto datetime created_at = datetime.strptime(created_at, "%Y-%m-%dT%H:%M:%S.%f%z") created_at = created_at.strftime("%B %d, %H:%M") # Mostramos iconito si se ha generado imagen icono_ = "🎨" if base64_image else "" icono__ = "🎙️" if base64_audio else "" icono___ = "👩🎓" if professor and not professor.startswith('["No hay revisiones') else "" # Mostramos la info básica info = f"**[#{i+1}]** {created_at}, 🪶{icono_}{icono__}{icono___} **TEMA:** {topic}, **ESTILO:** {estilo}" # Mostramos el resto de campos with st.expander(info): # Seleccionar los campos a mostrar st.write(f"*{modelo}* 🌡️{temperatura}") #st.write(poema) st.markdown(f"```\n{poema}\n```") with col2: if st.button("🪅", key=f"select_row{i}", help="Recupera este poema y su metodología", use_container_width=True): body = f"¡Seleccionado el poema #{id}! *Ve a **Metodología** para ver los detalles.*" st.toast(body, icon="👍") recuperar(id) # Generar el archivo de Excel excel_file = io.BytesIO() df.to_excel(excel_file, index=False, sheet_name='poemas') excel_file.seek(0) if st.download_button( label="Descargar los poemas", data=excel_file, file_name="poemas.xlsx", #mime="application/vnd.ms-excel", mime='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', type="primary", use_container_width=True, ): st.toast("¡Archivo descargado con éxito!", icon='👏') # ----- Función para mostrar el resumen del poema generado ---------------------------------- def resumen(): # Cabecera st.header(":memo: Metodología desarrollada", divider='gray') # Figura de la metodología st.image("img/metodologia.webp") # Muestro el tema y estilo proporcionados col1, col2 = st.columns(2) with col1: input1 = st.text_input("Tema", value=st.session_state.tema, disabled=True) with col2: input2 = st.text_input("Estilo", value=st.session_state.estilo, disabled=True) # Separador st.subheader('Contexto', divider='red', help="Se muestra el *contexto* generado que se incrustará en el _prompt_") # Mostrar el conocimiento experto with st.expander("Expertise Knowledge (HITL)", icon="👩💻"): for item in st.session_state.expertise: st.markdown(f"```\n{item}\n```") # Imprimir la conocimiento generado with st.expander("Conocimiento generado", icon="💻"): for item in st.session_state.generated: st.markdown(f"""{item}""", unsafe_allow_html=True) # Imprimir la conocimiento recuperado with st.expander("Conocimiento recuperado", icon="💾"): for item in st.session_state.rag: st.markdown(f"```\n{item}\n```") # Separador st.subheader('Reflexión', divider='red', help="Se muestra el borrador poético y las reflexiones de los expertos") # Imprimir el borrador poético with st.expander("Borrador poético", icon="📃"): st.markdown(f"```\n{st.session_state.draft_poem}\n```") # Muestra el resultado de la comprobación with st.expander("Comprobación de plagio", icon="🕵️"): st.markdown(f"```\n{st.session_state.plagiarism}\n```") # Imprimir la reflexión del catedrático with st.expander("Reflexión del catedrático", icon="🎓"): for item in st.session_state.reflexion: st.markdown(f"""{item}""", unsafe_allow_html=True) # Imprimir el poema if "expert_poem" in st.session_state: if st.session_state.expert_poem: with st.expander("Poema revisado", icon="📃"): st.markdown(f"```\n{st.session_state.expert_poem}\n```") # Imprimir la reflexión del catedrático if "professor" in st.session_state: with st.expander("Reflexión del profesor (HITL)", icon="👩🎓"): for item in st.session_state.professor: st.markdown(f"""{item}""", unsafe_allow_html=True) # Separador st.subheader('Poema :scroll:', divider='rainbow', help="Se muestra el poema elaborado") # Imprimir el poema st.markdown(f"```\n{st.session_state.poema}\n```") # Mostrar el audio if st.session_state.audio: st.audio(st.session_state.audio, format="audio/mp3") # Imprimir la imagen if st.session_state.image_data: st.image(st.session_state.image_data) ##################### # Función principal # ##################### def main(): st.set_page_config(page_title="Generador de poemas", page_icon="img/bot.ico") # Ejecutamos la configuración __init__() # Inicializamos las globals globals_setup() # Sidebar con los enlaces a los pasos opciones = { "Topic&Style": grafo, "Metodología": resumen, "Expertise (HITL)": expertise_knowledge, "Professor (HITL)": professor_reflexion, "Historial": historial, "Configuración": configuracion, } #st.sidebar.title("Pasos") #selected = st.sidebar.radio("Selecciona un paso", list(opciones.keys())) # Enlace a la librería de menús: https://github.com/victoryhb/streamlit-option-menu with st.sidebar: seleccion = option_menu(None, list(opciones.keys()), icons=['envelope-paper-heart','envelope-heart','person-hearts','person-square', 'hourglass-split', 'gear'], menu_icon="cast", default_index=5) st.image("img/bot.webp", width=250) st.markdown("