Saltar a contenido

Document Summary

Este script se usa para hacer resumenes de textos largos, haciendo uso de inteligencia artificial, a la vez te permite enviar este resumen al correo electronico.

Resumen del proceso completo

El script utiliza un algoritmo de "Divide y vencerás" para realizar resúmenes. Funciona de la siguiente manera:

  1. División del Texto: Inicialmente, el texto se divide en secciones que se ajustan al límite máximo de palabras que el modelo de lenguaje (LLM) puede procesar.

  2. Resumen Iterativo: Cada sección se resume por separado. Luego, el resumen de una sección se combina con la siguiente sección de texto original y este conjunto se resume nuevamente.

  3. Comprobación de Tamaño: Este proceso se repite hasta que la longitud combinada de todos los resúmenes es igual o superior al límite de palabras del LLM.

  4. Resumen Final: Una vez alcanzado este punto, se realiza un resumen final de todos los resúmenes intermedios.

  5. Repetición del Proceso: Si aún queda texto original por resumir, el proceso se reinicia con la siguiente sección del texto y los resúmenes anteriores.

Este método asegura que se resuma efectivamente todo el texto, manteniendo la coherencia y evitando exceder la capacidad de procesamiento del LLM.

Explicación del código empleado

Parte 1: Clase Email Sender

class EmailSender():
    def __init__(self, host="smtp.gmail.com", port=465):
        self.host = host
        self.port = port

    # METHOD THAT CREATES AN EMAIL WITH STRUCTURE INPUT VALUES AND SEND IT TO A LIST OF EMAILS

    def send_email(self, subject, message, email_sender, email_password, email_receiver, attachments):

        # Generate MIMEMultipart object to create mails with multiparts(text, attachments)
        msg = MIMEMultipart()

        # Add to message the subject, who send the email and who are the receptors
        msg['Subject'] = subject
        msg['From'] = email_sender
        msg['To'] = ', '.join(email_receiver)

        # Attach the message
        body = MIMEText(message)
        msg.attach(body)

        # Loop for attach docs to the mail, note that attachments is an array of objects with the properties
        # 'filename' and 'data' (which is the doc in binary data), you can obtain it if you have the original file
        # loaded in a variable and make file.read()
        for attach in attachments:
            attachment_mime = MIMEApplication(attach['data'])
            attachment_mime.add_header('Content-Disposition', 'attachment', filename=attach['filename'])
            msg.attach(attachment_mime)

        # Create a ssl context
        context = ssl.create_default_context()

        # Initialize a connection with the server from which we are going to send the email
        with smtplib.SMTP_SSL(self.host, self.port, context=context) as smtp:
            # Login the sender account(gmail), password is an application password
            smtp.login(email_sender, email_password)

            # Send the email to the receptors and add the MIMEMultipart object as string
            smtp.sendmail(email_sender, email_receiver, msg.as_string())

Esta clase, en resumen permite el envio del resumen a los correos electronicos que se desee.

Parte 2: Inicialización de las variables y clases globales

openai.api_key = st.secrets.openai.api_key
openai.api_base = st.secrets.openai.api_base
openai.api_type = st.secrets.openai.api_type
openai.api_version = st.secrets.openai.api_version

dummy_sender_mail = st.secrets.gmail_dummy.gml_email
dummy_sender_pwd = st.secrets.gmail_dummy.gml_pwd

# LOAD ENCODING OF THE PROMPTS TOKENS
encoding = tiktoken.get_encoding('cl100k_base')

# INSTANCE EMAILSENDER CLASS
emailSender = EmailSender()

En el código mencionado, se realizan las siguientes inicializaciones de variables globales para el funcionamiento del script:

  1. Variables para AzureOpenAI: Las primeras cuatro variables se establecen para permitir la conexión con la API de AzureOpenAI. Estas variables son fundamentales para interactuar con los servicios ofrecidos por AzureOpenAI.

  2. Variables para Correo Electrónico: Las dos variables siguientes se utilizan para configurar el correo electrónico que actuará como remitente en el proceso de envío de correos electrónicos a los usuarios. Estas variables son esenciales para la autenticación y configuración del servicio de correo electrónico.

  3. Inicialización del Tokenizer de OpenAI: Se instancian los componentes necesarios para utilizar el tokenizer de OpenAI, que es crucial para medir el número de tokens resultantes de un texto de entrada. Esta funcionalidad es importante para gestionar y optimizar el procesamiento del texto.

  4. Instancia de la Clase EmailSender: Finalmente, se crea una instancia de la clase EmailSender. Esta clase es probablemente responsable de manejar la lógica y la funcionalidad relacionada con el envío de correos electrónicos, incluyendo la preparación y el envío de mensajes.

Estas inicializaciones son esenciales para asegurar que el script funcione correctamente, permitiendo la interacción con la API de AzureOpenAI, la gestión de correos electrónicos y el procesamiento eficiente de textos.

Parte 3: Setup de la página de Streamlit

st.set_page_config(layout="wide")
sogetilabs_logo = Image.open('images/SogetiLabs_Logo.png')
st.image(sogetilabs_logo, width=200)

if 'user_id' not in st.session_state:
    st.session_state.user_id = str(uuid.uuid4())

En el código anteriormente mencionado se puede observar la incialización de la página de Streamlit y la variable de sesión del usuario en caso de no encontrarse.

Parte 4: Función que realiza el resumen

def Summarization(text_doc,language="English"):
    if(len(text_doc) == 1): return SummarizeTextByChat(text_doc[0],language)
    else: return SummarizeTextByChatWithAnInitialSummary(Summarization(text_doc[:-1]), text_doc[-1], language)

Función basada en backtraking que hace uso de dos funciones auxiliares para realizar el proceso descrito al inicio de la documentación.

Parte 5: Columna 1 de la interfaz de usuario

with col1:
    st.write("Upload your meeting transcription to summarize it or generate a minute")
    st.session_state['uploaded_files'] = st.file_uploader("Choose a file", accept_multiple_files=True)

    if len(st.session_state['uploaded_files']) != 0:
        for file in st.session_state['uploaded_files']:
            found_object = next((obj for obj in st.session_state['text_files'] if obj['file_name'] == file.name), None)
            if found_object is None:
                st.session_state['text_files'].append({"file_name": file.name, "text": getTextArrayByMaxTokens(file, 3000)})
                st.session_state['summarized'] = False
                st.session_state['minute'] = False

        language = st.selectbox("Select the language of the summary", ["English", "Spanish"])
        # BUTTON FOR SUMMARIES
        if st.button("Summarize it"):
            with st.spinner("Summarizing..."):
                for i in range(len(st.session_state['text_files'])):
                    text_summary = Summarization(st.session_state['text_files'][i]['text'], language)
                    st.session_state['text_files'][i]['text_summarized'] = text_summary
            st.session_state['summarized'] = True
            st.success("Summarized!")

        # BUTTON FOR MINUTES
        if st.button("Generate minute"):
            with st.spinner("Generating minute..."):
                # GENERATE MINUTE FOR EVERY 
                for i in range(len(st.session_state['text_files'])):
                    text_minute = GenerateMinute(st.session_state['text_files'][i]['text'], language)
                    st.session_state['text_files'][i]['text_minute'] = text_minute
                st.session_state['minute'] = True
                st.success("Minute generated!")

    if st.session_state['summarized']:
        for doc_summarized in st.session_state['text_files']:
            st.write(f"Summary preview of {doc_summarized['file_name']}")
            st.markdown(doc_summarized['text_summarized'])

    if st.session_state['minute']:
        for doc_minute in st.session_state['text_files']:
            st.write(f"Minute of {doc_minute['file_name']}")
            st.markdown(doc_minute['text_minute'])

El código descrito implementa una interfaz de usuario con las siguientes características:

  1. Botón de Subida de Documentos: La interfaz presenta un botón que permite a los usuarios subir el o los documentos de los cuales desean obtener un resumen. Este botón es el punto de partida para que el usuario interactúe con la funcionalidad de resumen del documento.

  2. Visualización de un Spinner Durante el Proceso: Una vez que los documentos son subidos, se muestra un spinner en la interfaz. Este spinner sirve como indicador visual de que el proceso de resumen está en curso. Ayuda a los usuarios a entender que la tarea se está realizando y que deben esperar hasta su finalización.

  3. Presentación del Resumen en Pantalla: Tras completar el proceso de resumen, el resultado se muestra en la pantalla. Esto permite a los usuarios ver inmediatamente el resumen de los documentos que han subido.

Este diseño de interfaz garantiza una experiencia de usuario fluida y clara, desde la carga de documentos hasta la presentación de los resúmenes, manteniendo informados a los usuarios durante todo el proceso.

Parte 6: Columna 2 de la interfaz de usuario

with col2:
    st.markdown(f'<b>Send an email to:</b>', unsafe_allow_html=True)
    st.session_state['recipients'] = st.text_area("Send an email to:", label_visibility="hidden", 
                                                placeholder="Write the emails separate with enters or white spaces")
    if st.button("Send"):
        emails_receiver = st.session_state['recipients'].split()
        attachments = []
        for file in st.session_state['text_files']:
            attachments.append({'filename': file['file_name'], 'data': file['binary_data']})
        emailSender.send_email("Share Teams meetings summaries", 
                            f"Hello,\n\n There are attached to this email a summaries about some Teams meetings that are shared to you.",
                            dummy_sender_mail, dummy_sender_pwd, emails_receiver, attachments)

    st.markdown(f'<b>Download links:</b>', unsafe_allow_html=True)
    if st.session_state['summarized']:
        for docs_properties in st.session_state['text_files']:
            doc_name = f"Summary_{docs_properties['file_name']}"
            doc = Document()
            doc.add_paragraph(docs_properties['text_summarized'])
            doc.save(f"Summary_{docs_properties['file_name']}")

            # Read the document file as binary data
            with open(f"Summary_{docs_properties['file_name']}", "rb") as file:
                doc_data = file.read()

            docs_properties['binary_data'] = doc_data
            # Encode the document data as base64
            b64_doc = base64.b64encode(doc_data).decode()
            docs_properties['b64_doc'] = b64_doc

El código que se describe, muestra al usuario la interfaz necesaria para poder enviar por correo electrónico los resumenes generados.