Summary
Hello
I am trying to make a streamlit app of a chatbot using langchain with OpenAI. The chatbot works good if I run it alone in the main area of the web app but it failed when I integrate it into a more dinamyc environment.
What I want to do is a streamlit with a sidebar with a st.form that contains all the parameters that will use the model and then once that I click on the submit button starts the chatbot in the main page of the app. However, what I get is an empty chatbot display.
the code is the f following:
Steps to reproduce
Code snippet:
####### Retrival Augmented Generation (RAE) ##########
# RAE: es el proceso de integración de datos externos (external knowledge base) al LLM.
import streamlit as st
import os
from pypdf import PdfReader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.embeddings import OpenAIEmbeddings
from langchain.vectorstores import Chroma
from langchain import OpenAI, PromptTemplate
from langchain.chains import RetrievalQA
import openai # Lo usaremos para enlistar los modelos disponibles y verificar rapidamente la autenticidad del API Key
######### 1) SET UP GENERAL DE LA WEB APP
# Configuracion del layout de la pagina
st.set_page_config(layout="wide")
# Set del titulo
st.title(body="Chatbot")
# Integracion de un documento externo
st.sidebar.subheader(body="Ingreso de datos y configuración")
# 1.1) Configuracion del modelo dentro un form (formulario) de stramlit
with st.sidebar.form(key ='Form1'):
with st.sidebar:
api_key = st.sidebar.text_input(label="Ingresa tu API Key de OpenAI")
documento = st.sidebar.file_uploader(label="Carga del archivo PDF que será ingegrado al LLM "
"(Large Language Model) para su consulta")
chunk_size = st.sidebar.number_input(label="Chunk size:", min_value=100,
max_value=1000, value=400)
chunk_overlap = st.sidebar.number_input(label="Chunk Overlap:", min_value=50,
max_value=500, value=200)
submit_button = st.form_submit_button(label="Enviar")
# Nota: Actualmente form sumit button no puede ir directamtne en el sidebar, por eso se usa este doble with st.sidebar
# IMPORANTE:
# En Streamlit, "st.form" es una función que te permite crear un formulario interactivo en una aplicación web. Este formulario es útil para recolectar información del usuario, como entradas de texto, selecciones etc.
# puedes agregar widgets interactivos dentro del formulario utilizando el mismo patrón de llamadas que utilizarías fuera del formulario (por ejemplo, st.text_input, st.selectbox, etc.). Los widgets dentro del formulario interactuarán con los valores ingresados por el usuario cuando se envíe el formulario.
# Para enviar el formulario, debes llamar al método .submit() de la función del formulario. Una vez que el usuario hace clic en el botón de enviar, el código posterior al método .submit() (IMPORANTE: pero teninedo la sentencia condicionante de que e ha presinado el boton) se ejecutará, permitiéndote realizar acciones basadas en los valores ingresados por el usuario en el formulario.
########## 2) Comienzo del Retrival Augmented Generation (RAE) #############
# Esto se ejecuta hasta que el usuario ingrese los datos del formulario
# Lógica que se ejecuta cuando el botón de envío es presionado
if submit_button:
# 2.1) Verificación de ingresos de datos
if api_key:
try:
models = openai.Model.list(api_key=api_key)
except:
st.error("Tu API Key es invalida")
st.stop()
else:
st.error(body="Ingresa una llave")
st.stop() #Detiene el proceso
if documento:
print("Si hay documentos")
else:
st.error("No has ingresado un documento PDF")
st.stop()
###### 2.1) LOADING
reader = PdfReader(stream=documento)
# Obtén el número total de páginas
number_of_pages = len(reader.pages)
all_text = "" # Declaramos un str vacio
# Lee el texto de cada página
for i in range(number_of_pages):
page = reader.pages[i]
all_text += page.extract_text() # ponerle un += es como usar un append en un str
# 2.2) TRANFORM (chunk strategy)
# Set up del objto text_slpiter
text_splitter = RecursiveCharacterTextSplitter(
# Set a really small chunk size, just to show.
chunk_size= chunk_size, # numero de caracteres
chunk_overlap=chunk_size, # solapamiento
length_function=len)
# IMPORTANTE: Con RecursiveCharacterTextSplitter podemos usar length function, esta funcion "len" cunta solo los caraceres. PEro podriamos usar en lugar de esta funcion alguna otra de contabiliszacion de tokens. Cualquiera haria una dividion de chunks, exsten otro modulo que es exclusivo para dividir los textos solo por caracteres
# Ejecucion del objto text spliter
# Se tiene que ingresar solo string
chunks = text_splitter.create_documents(texts=[all_text]) # Este lo ejecutamos solo con el string
##### 2.3) EMBEDDING
# En este caso estamos integrando un embedding modelo de OpenAI, set up el embedding model
embeddings_model = OpenAIEmbeddings(openai_api_key=api_key)
####### 2.4) Vector Database
# Creamos nuestro Vectordatabse, le integramos los texgtos (chunkgs) y el modelo de embedding que se usara
db = Chroma.from_documents(documents=chunks, embedding=embeddings_model)
# Creamos el objeeto Vector Store Retriver
retriever = db.as_retriever()
######## 2.5) CREACION E INTEGRACION DE UN TEMPLATE
prompt_template = """Use the following pieces of context to answer the question. If you don't know the answer, just say that you don't know, don't try to make up an answer.
{context}
Question: {question}
Answer in spanish:"""
PROMPT = PromptTemplate(
template=prompt_template, input_variables=["context", "question"])
######## 2.6) SET UP DEL MODELO
llm_openai = OpenAI(openai_api_key= api_key,
model_name="gpt-3.5-turbo", temperature=0.2) # Entre mayor la temperatura más random las respuestas
##### 2.7) CREACION DEL CHAIN que integra todo: el vector database (con el retriver), el llm y su prompt
chain_type_kwargs = {"prompt": PROMPT}
qa = RetrievalQA.from_chain_type(llm=llm_openai, chain_type="stuff", retriever=retriever,
chain_type_kwargs=chain_type_kwargs)
# Initialize chat history
if "messages" not in st.session_state:
st.session_state.messages = []
# Display chat messages from history on app rerun
for message in st.session_state.messages:
with st.chat_message(message["role"]):
st.markdown(message["content"])
# Accept user input
if prompt := st.chat_input("What is up?"):
# Add user message to chat history
st.session_state.messages.append({"role": "user", "content": prompt})
# Display user message in chat message container
with st.chat_message("user"):
st.markdown(prompt)
# Display assistant response in chat message container
with st.chat_message("assistant"):
message_placeholder = st.empty()
full_response = ""
Can you help me people to understand what is going on?
Best regards,
Orlando