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:
-
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.
-
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.
-
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.
-
Resumen Final: Una vez alcanzado este punto, se realiza un resumen final de todos los resúmenes intermedios.
-
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:
-
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.
-
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.
-
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.
-
Instancia de la Clase
EmailSender
: Finalmente, se crea una instancia de la claseEmailSender
. 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:
-
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.
-
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.
-
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.