seansullivan's picture
Update app.py
da262da verified
raw
history blame
7.16 kB
import os
import streamlit as st
from streamlit_chat import message
from langchain_openai import OpenAIEmbeddings
from pinecone import Pinecone
import time
from langchain_pinecone.vectorstores import Pinecone as PineconeVectorStore
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnableParallel, RunnablePassthrough
from langchain_openai import ChatOpenAI
from langchain_groq import ChatGroq
from langchain_anthropic import ChatAnthropic
from langchain_core.messages import AIMessage, HumanMessage, get_buffer_string
from langchain.memory import ConversationBufferMemory
from langchain_core.runnables import RunnableLambda
from operator import itemgetter
# Streamlit App Configuration
st.set_page_config(page_title="Docu-Help")
# Dropdown for namespace selection
namespace_name = st.sidebar.selectbox("Select Website:", ('crawlee', ''), key='namespace_name')
# Read API keys from environment variables
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
PINE_API_KEY = os.getenv("PINE_API_KEY")
LANGCHAIN_API_KEY = os.getenv("LANGCHAIN_API_KEY")
LANGCHAIN_TRACING_V2 = 'true'
LANGCHAIN_ENDPOINT = "https://api.smith.langchain.com"
LANGCHAIN_PROJECT = "docu-help"
# Sidebar for model selection and Pinecone index name input
st.sidebar.title("Sidebar")
model_name = st.sidebar.radio("Choose a model:", ("gpt-3.5-turbo-1106", "gpt-4-0125-preview", "Claude-Sonnet", "mixtral-groq"))
openai_api_key2 = st.sidebar.text_input("Enter OpenAI Key: ")
groq_api_key = st.sidebar.text_input("Groq API Key: ")
anthropic_api_key = st.sidebar.text_input("Claude API Key: ")
pinecone_index_name = os.getenv("pinecone_index_name")
namespace_name = "crawlee"
# Initialize session state variables if they don't exist
if 'generated' not in st.session_state:
st.session_state['generated'] = []
if 'past' not in st.session_state:
st.session_state['past'] = []
if 'messages' not in st.session_state:
st.session_state['messages'] = [{"role": "system", "content": "You are a helpful assistant."}]
if 'total_cost' not in st.session_state:
st.session_state['total_cost'] = 0.0
def refresh_text():
with response_container:
for i in range(len(st.session_state['past'])):
try:
user_message_content = st.session_state["past"][i]
message = st.chat_message("user")
message.write(user_message_content)
except:
print("Past error")
try:
ai_message_content = st.session_state["generated"][i]
message = st.chat_message("assistant")
message.write(ai_message_content)
except:
print("Generated Error")
# Function to generate a response using App 2's functionality
def generate_response(prompt):
st.session_state['messages'].append({"role": "user", "content": prompt})
embed = OpenAIEmbeddings(model="text-embedding-3-small", openai_api_key=OPENAI_API_KEY)
pc = Pinecone(api_key=PINE_API_KEY)
index = pc.Index(pinecone_index_name)
time.sleep(1) # Ensure index is ready
index.describe_index_stats()
vectorstore = PineconeVectorStore(index, embed, "text", namespace=namespace_name)
retriever = vectorstore.as_retriever()
template = """You are an expert software developer who specializes in APIs. Answer the user's question based only on the following context:
{context}
Chat History:
{chat_history}
Question: {question}
"""
prompt_template = ChatPromptTemplate.from_template(template)
if model_name == "Claude-Sonnet":
chat_model = ChatAnthropic(temperature=0, model="claude-3-sonnet-20240229", anthropic_api_key=anthropic_api_key)
elif model_name == "mixtral-groq":
chat_model = ChatGroq(temperature=0, groq_api_key=groq_api_key, model_name="mixtral-8x7b-32768")
else:
chat_model = ChatOpenAI(temperature=0, model=model_name, openai_api_key=openai_api_key2)
memory = ConversationBufferMemory(
return_messages=True, output_key="answer", input_key="question"
)
# Loading the previous chat messages into memory
for i in range(len(st.session_state['generated'])):
# Replaced "Answer: " with "" to stop the model from learning to add "Answer: " to the beginning by itself
memory.save_context({"question": st.session_state["past"][i]}, {"answer": st.session_state["generated"][i].replace("Answer: ", "")})
# Prints the memory that the model will be using
print(f"Memory: {memory.load_memory_variables({})}")
rag_chain = (
RunnablePassthrough.assign(context=(lambda x: x["context"]), chat_history=lambda x: get_buffer_string(x["chat_history"]))
| prompt_template
| chat_model
| StrOutputParser()
)
rag_chain_with_source = RunnableParallel(
{"context": retriever, "question": RunnablePassthrough(), "chat_history": RunnableLambda(memory.load_memory_variables) | itemgetter("history")}
).assign(answer=rag_chain)
# Function that extracts the individual tokens from the output of the model
def make_stream():
sources = []
st.session_state['generated'].append("Answer: ")
yield st.session_state['generated'][-1]
for chunk in rag_chain_with_source.stream(prompt):
if list(chunk.keys())[0] == 'answer':
st.session_state['generated'][-1] += chunk['answer']
yield chunk['answer']
elif list(chunk.keys())[0] == 'context':
# sources = chunk['context']
sources = [doc.metadata['source'] for doc in chunk['context']]
sources_txt = "\n\nSources:\n" + "\n".join(sources)
st.session_state['generated'][-1] += sources_txt
yield sources_txt
# Sending the message as a stream using the function above
print("Running the response streamer...")
with response_container:
message = st.chat_message("assistant")
my_generator = make_stream()
message.write_stream(my_generator)
formatted_response = st.session_state['generated'][-1]
#response = rag_chain_with_source.invoke(prompt)
#sources = [doc.metadata['source'] for doc in response['context']]
#answer = response['answer'] # Extracting the 'answer' part
#formatted_response = f"Answer: {answer}\n\nSources:\n" + "\n".join(sources)
st.session_state['messages'].append({"role": "assistant", "content": formatted_response})
return formatted_response
# Container for chat history and text box
response_container = st.container()
container = st.container()
# Implementing chat input as opposed to a form because chat_input stays locked at the bottom
if prompt := st.chat_input("Ask a question..."):
# I moved reponse here because, for some reason, I get an error if I only have an if statement for user_input later...
st.session_state['past'].append(prompt)
refresh_text()
response = generate_response(prompt)