make my program perform a similarity search insteat. here is my code you need to update:
import os
import gradio as gr
from langchain_ollama import ChatOllama
from langchain_unstructured import UnstructuredLoader
from langchain_core.messages import HumanMessage, SystemMessage, AIMessage
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_community.embeddings.sentence_transformer import SentenceTransformerEmbeddings
from langchain_community.vectorstores import Chroma
from langchain_community.retrievers import ArxivRetriever
from langchain_community.tools import DuckDuckGoSearchRun

# For LangSmith tracing
from langsmith import traceable
os.environ["LANGCHAIN_TRACING_V2"] = "true"
os.environ["LANGCHAIN_API_KEY"] = "<TRUFFLEHOG_REDACTED_LANGSMITH>"  # Replace with your actual API key
os.environ["LANGCHAIN_PROJECT"] = "l"  # Optional: "default" is used if not set

# Initialize Llama model
llm = ChatOllama(
    model="llama3.1",
    temperature=0,
    base_url="http://localhost:11434",
    streaming=True
)

# Set up embeddings and vector store
embeddings = SentenceTransformerEmbeddings(model_name="all-MiniLM-L6-v2")
persist_directory = "./chroma_db"
vectorstore = Chroma(persist_directory=persist_directory, embedding_function=embeddings)

# Initialize knowledge base retriever
knowledge_base = vectorstore.as_retriever(search_kwargs={"k": 3})

# Function to update the knowledge base
@traceable
def update_knowledge_base(uploaded_files, website_urls):
    docs = []
    # Load documents from uploaded files
    if uploaded_files:
        for uploaded_file in uploaded_files:
            loader = UnstructuredLoader(file_path=uploaded_file.name)
            docs.extend(loader.load())
    # Load documents from URLs
    if website_urls:
        urls = website_urls.split(',')
        for url in urls:
            url = url.strip()
            if url:
                loader = UnstructuredLoader(web_url=url)
                docs.extend(loader.load())
    # Split documents and add to vector store
    if docs:
        text_splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)
        split_docs = text_splitter.split_documents(docs)
        vectorstore.add_documents(split_docs)
    return "Knowledge base updated."

# Function to process uploaded files in the chat and include their content
def process_uploaded_files(files):
    content = ""
    if files:
        for file in files:
            if file.name.endswith(".pdf") or file.name.endswith(".txt"):
                loader = UnstructuredLoader(file_path=file.name)
                docs = loader.load()
                content += "\n".join([doc.page_content for doc in docs])
    return content

# Chat with Llama (normal chat) function
@traceable
def chat_with_llama(message, history, uploaded_files):
    # Process uploaded files and include their content
    file_content = process_uploaded_files(uploaded_files)
    # Convert history to list of messages
    messages = [SystemMessage(content="You are a helpful assistant.")]
    if history:
        for user_msg, assistant_msg in history:
            messages.append(HumanMessage(content=user_msg))
            messages.append(AIMessage(content=assistant_msg))
    # Append file content to user message if any
    if file_content:
        message += f"\n\nHere is some additional content:\n{file_content}"
    messages.append(HumanMessage(content=message))
    # Stream the response from the model
    try:
        response_stream = llm.stream(messages)
        partial_response = ""
        for chunk in response_stream:
            partial_response += chunk.content
            yield partial_response
    except Exception as e:
        yield f"Error: {str(e)}"

# Function to handle knowledge base Q&A
@traceable
def knowledge_base_chat(message, history):
    # Search in the knowledge base
    relevant_docs = knowledge_base.get_relevant_documents(message)
    if not relevant_docs:
        yield "No relevant information found in the knowledge base."
        return
    context = "\n\n".join([doc.page_content for doc in relevant_docs])
    prompt = f"Context:\n{context}\n\nQuestion:\n{message}\n\nAnswer:"
    messages = [HumanMessage(content=prompt)]
    # Stream the response from the model
    try:
        response_stream = llm.stream(messages)
        partial_response = ""
        for chunk in response_stream:
            partial_response += chunk.content
            yield partial_response
    except Exception as e:
        yield f"Error: {str(e)}"

# Function for the Internet-Enabled LLM
@traceable
def internet_access_llm(message, history, use_wikipedia):
    # Generate a query from the user input
    query = message
    # Search the internet
    search_tool = DuckDuckGoSearchRun()
    search_results = search_tool.invoke(query)
    # Optionally search Wikipedia
    if use_wikipedia:
        wikipedia_tool = DuckDuckGoSearchRun(backend="wikipedia")
        wiki_results = wikipedia_tool.invoke(query)
        search_results += "\n\n" + wiki_results
    # Combine search results and prepare the prompt
    prompt = f"Search Results:\n{search_results}\n\nQuestion:\n{message}\n\nAnswer:"
    messages = [HumanMessage(content=prompt)]
    # Stream the response from the model
    try:
        response_stream = llm.stream(messages)
        partial_response = ""
        for chunk in response_stream:
            partial_response += chunk.content
            yield partial_response
    except Exception as e:
        yield f"Error: {str(e)}"

# Function to retrieve arXiv papers and update knowledge base
@traceable
def arxiv_research(query, max_papers):
    retriever = ArxivRetriever(load_max_docs=int(max_papers), get_full_documents=True)
    docs = retriever.get_relevant_documents(query)
    # Add documents to the knowledge base
    if docs:
        text_splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)
        split_docs = text_splitter.split_documents(docs)
        vectorstore.add_documents(split_docs)
    return f"Added {len(docs)} papers to the knowledge base."

# Function to handle arXiv Knowledge Base Q&A
@traceable
def ask_arxiv_knowledge_base(message, history):
    # Search in the knowledge base
    relevant_docs = knowledge_base.get_relevant_documents(message)
    if not relevant_docs:
        yield "No relevant information found in the arXiv knowledge base."
        return
    context = "\n\n".join([doc.page_content for doc in relevant_docs])
    prompt = f"Context:\n{context}\n\nQuestion:\n{message}\n\nAnswer:"
    messages = [HumanMessage(content=prompt)]
    # Stream the response from the model
    try:
        response_stream = llm.stream(messages)
        partial_response = ""
        for chunk in response_stream:
            partial_response += chunk.content
            yield partial_response
    except Exception as e:
        yield f"Error: {str(e)}"

# Gradio interface
with gr.Blocks() as demo:
    gr.Markdown("# Multi-Tab Gradio App with Llama")
    # Chat with Llama Tab
    with gr.Tab("Chat with Llama"):
        gr.Markdown("## Chat Interface")
        with gr.Row():
            upload_files = gr.File(
                label="Upload PDFs or TXT files",
                file_types=[".pdf", ".txt"],
                file_count="multiple",
                visible=True
            )
        gr.ChatInterface(
            fn=chat_with_llama,
            title="Chat with Llama",
            additional_inputs=[upload_files]
        )
    # Knowledge Base Tab
    with gr.Tab("Knowledge Base"):
        gr.Markdown("## Knowledge Base Management and Chat")
        with gr.Row():
            with gr.Column(scale=1):
                gr.Markdown("### Update Knowledge Base")
                kb_upload_files = gr.File(
                    label="Upload Files",
                    file_types=["text", "pdf", "docx"],
                    file_count="multiple"
                )
                kb_website_urls = gr.Textbox(
                    label="Website URLs",
                    placeholder="Enter comma-separated URLs"
                )
                kb_update_button = gr.Button("Update Knowledge Base")
                kb_update_status = gr.Textbox(label="Update Status", interactive=False)
                kb_update_button.click(
                    update_knowledge_base,
                    inputs=[kb_upload_files, kb_website_urls],
                    outputs=kb_update_status
                )
            with gr.Column(scale=2):
                gr.Markdown("### Chat with Knowledge Base")
                gr.ChatInterface(
                    fn=knowledge_base_chat,
                    title="Knowledge Base Chat"
                )
    # LLM with Internet Access Tab
    with gr.Tab("LLM with Internet Access"):
        gr.Markdown("## Internet-Enabled LLM")
        use_wikipedia = gr.Checkbox(label="Use Wikipedia")
        gr.ChatInterface(
            fn=internet_access_llm,
            title="LLM with Internet Access",
            additional_inputs=[use_wikipedia]
        )
    # arXiv Research Papers Tab
    with gr.Tab("arXiv Research Papers"):
        gr.Markdown("## Retrieve arXiv Papers")
        arxiv_query = gr.Textbox(
            label="Search Query",
            placeholder="Enter your search query for arXiv papers"
        )
        max_papers = gr.Slider(
            label="Number of Papers",
            minimum=1,
            maximum=10,
            step=1,
            value=5
        )
        arxiv_button = gr.Button("Retrieve Papers")
        arxiv_result = gr.Textbox(label="Result", interactive=False)
        arxiv_button.click(
            arxiv_research,
            inputs=[arxiv_query, max_papers],
            outputs=arxiv_result
        )
        gr.Markdown("## Chat with arXiv Knowledge Base")
        gr.ChatInterface(
            fn=ask_arxiv_knowledge_base,
            title="arXiv Knowledge Base Chat"
        )

demo.launch()


and here is the code with the similarity search implemented:
import os
import shutil
import gradio as gr
from itertools import chain
from langchain_community.embeddings.sentence_transformer import SentenceTransformerEmbeddings
from langchain_community.document_loaders import DirectoryLoader
from langchain_text_splitters import CharacterTextSplitter
from langchain_community.vectorstores import Chroma
from langchain.chains import create_retrieval_chain, create_history_aware_retriever
from langchain.chains.combine_documents import create_stuff_documents_chain
from langchain.memory import ConversationBufferMemory
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder, PromptTemplate
from langchain.tools import WikipediaQueryRun, DuckDuckGoSearchResults
from langchain_core.tools import Tool
from langchain_community.utilities import WikipediaAPIWrapper
from langsmith import traceable
from langchain_core.messages import AIMessage, HumanMessage
from langchain.agents import AgentExecutor, create_react_agent
from langchain import hub
from langchain_community.chat_models import ChatOllama
from langchain_experimental.llms.ollama_functions import OllamaFunctions

os.environ["LANGCHAIN_TRACING_V2"] = "true"
os.environ["LANGCHAIN_API_KEY"] = "<TRUFFLEHOG_REDACTED_LANGSMITH>"
os.environ["LANGCHAIN_PROJECT"] = "rag test"  # Optional: "default" is used if not set

# Initialize embeddings and vector store
embeddings = SentenceTransformerEmbeddings(model_name="all-MiniLM-L6-v2")
persist_directory = "./chroma_db"
vectorstore = Chroma(persist_directory=persist_directory, embedding_function=embeddings)

# Initialize conversation memory
memory = ConversationBufferMemory(memory_key="chat_history", return_messages=True)

# Initialize LLM
# llm = ChatOllama(model="llama3.1:8b-instruct-q8_0", temperature=0)

llm = OllamaFunctions(
    base_url="http://localhost:11434",
    model="llama3.1:8b-instruct-q8_0", # "llama3.1" , "llama3.1:8b-instruct-q8_0"
    temperature="1"
)

# Initialize tools
wikipedia_tool = WikipediaQueryRun(api_wrapper=WikipediaAPIWrapper())
duckduckgo_tool = DuckDuckGoSearchResults()

def create_knowledge_base_tool(vectorstore):
    def run_knowledge_base_tool(query: str) -> str:
        results = vectorstore.similarity_search(query, k=3)
        return "\n".join([doc.page_content for doc in results])
    
    return Tool(
        name="knowledge_base_tool",
        description="Tool for searching through the knowledge base using similarity search.",
        func=run_knowledge_base_tool
    )

knowledge_base_tool = create_knowledge_base_tool(vectorstore)

# Define tools
tools = [
    wikipedia_tool,
    duckduckgo_tool,
    knowledge_base_tool
]

# Get the ReAct prompt template from LangChain Hub
# prompt = hub.pull("hundsfutz5")1
# prompt = hub.pull("hundsfutz5-test")
prompt = hub.pull("hundsfutz6")

# Create the ReAct agent
agent = create_react_agent(llm, tools, prompt)

# Create an agent executor
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True, handle_parsing_errors=True)

# Function to process uploaded documents
def process_documents(files):
    if not files:
        return "No new documents to process.", get_processed_files()
    
    processed_files = []
    skipped_files = []
    
    for file in files:
        file_path = file.name
        destination_path = os.path.join("uploads", os.path.basename(file_path))
        
        if os.path.exists(destination_path):
            skipped_files.append(os.path.basename(file_path))
        else:
            try:
                shutil.move(file_path, destination_path)
                processed_files.append(os.path.basename(file_path))
            except Exception as e:
                return f"Error moving file: {str(e)}", get_processed_files()
    
    if processed_files:
        loader = DirectoryLoader("uploads", glob="**/*")
        documents = loader.load()
        
        text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=200, length_function=len)
        docs = text_splitter.split_documents(documents)
        
        vectorstore.add_documents(docs)
    
    result = f"Processed {len(processed_files)} new document(s)."
    if skipped_files:
        result += f"\nSkipped {len(skipped_files)} existing file(s): {', '.join(skipped_files)}"
        gr.Warning(f"The following files already exist and were not processed: {', '.join(skipped_files)}")
    
    return result, get_processed_files()

# Function to clear database and uploaded files
def clear_database():
    global vectorstore

    try:
        vectorstore._client._client.close()
    except AttributeError:
        pass

    shutil.rmtree("uploads", ignore_errors=True)
    os.makedirs("uploads", exist_ok=True)
    
    shutil.rmtree(persist_directory, ignore_errors=True)
    
    vectorstore = Chroma(persist_directory=persist_directory, embedding_function=embeddings)
    
    return "Database and uploaded files cleared.", get_processed_files()

# Function to get processed files
def get_processed_files():
    files = os.listdir("uploads")
    return "Processed files:\n" + "\n".join(files) if files else "No files processed yet."

@traceable
def chat_with_docs(message, history, use_wikipedia, use_duckduckgo, use_knowledge_base):
    # Construct the chat history
    chat_history = "\n".join([f"Human: {h[0]}\nAI: {h[1]}" for h in history])

    # Filter tools based on checkbox selections
    selected_tools = []
    if use_wikipedia:
        selected_tools.append(wikipedia_tool)
    if use_duckduckgo:
        selected_tools.append(duckduckgo_tool)
    if use_knowledge_base:
        selected_tools.append(knowledge_base_tool)

    # Create a new agent executor with selected tools
    filtered_agent = create_react_agent(llm, selected_tools, prompt)
    filtered_executor = AgentExecutor(agent=filtered_agent, tools=selected_tools, verbose=True, handle_parsing_errors=True)

    # Invoke the agent executor
    result = filtered_executor.invoke({
        "input": message,
        "chat_history": chat_history
    })

    return result['output']

# User message handler for Gradio
def user(user_message, history):
    return "", history + [[user_message, None]]

# Bot message handler for Gradio
def bot(history, use_wikipedia, use_duckduckgo, use_knowledge_base):
    bot_message = chat_with_docs(history[-1][0], history[:-1], use_wikipedia, use_duckduckgo, use_knowledge_base)
    history[-1][1] = ""
    for character in bot_message:
        history[-1][1] += character
        yield history

# Gradio UI setup
with gr.Blocks() as demo:
    gr.Markdown("# Document Chatbot with ReAct Agent")
    
    with gr.Tabs():
        with gr.TabItem("Chat"):
            with gr.Row():
                use_wikipedia = gr.Checkbox(label="Use Wikipedia", value=True)
                use_duckduckgo = gr.Checkbox(label="Use DuckDuckGo", value=True)
                use_knowledge_base = gr.Checkbox(label="Use Knowledge Base", value=True)
            
            chatbot = gr.Chatbot()
            msg = gr.Textbox()
            clear_chat = gr.Button("Clear Chat")

        with gr.TabItem("Knowledge Base"):
            with gr.Row():
                file_output = gr.File(file_count="multiple", label="Upload Documents")
                process_button = gr.Button("Process Documents")
                clear_button = gr.Button("Clear Database")
            
            process_output = gr.Textbox(label="Processing Output")
            processed_files = gr.Textbox(label="Processed Files", value=get_processed_files())

    msg.submit(user, [msg, chatbot], [msg, chatbot], queue=False).then(
        bot, [chatbot, use_wikipedia, use_duckduckgo, use_knowledge_base], chatbot
    )
    process_button.click(process_documents, inputs=[file_output], outputs=[process_output, processed_files])
    clear_button.click(clear_database, outputs=[process_output, processed_files])
    clear_chat.click(lambda: None, outputs=chatbot, queue=False)

# Launch the Gradio interface
if __name__ == "__main__":
    if not os.path.exists("uploads"):
        os.makedirs("uploads")
    demo.queue().launch()