make the boss agent and the knowlage base agent know what files are in the knowlage base. and make it in the prompt that it says to the boss that it doesnt have information to that. also rename the agent that has internet access to a other name that the boss understands it better. also make that it doesnt take so long for the response to come back. output the full code

code:
import os
import shutil
import gradio as gr
from itertools import chain
from langsmith import traceable

# Import crewAI components
from crewai import Agent, Task, Crew, Process

from langchain.embeddings import SentenceTransformerEmbeddings
from langchain.document_loaders import DirectoryLoader
from langchain.text_splitter import CharacterTextSplitter
from langchain_chroma import Chroma  # Updated import

from langchain.tools import Tool
from langchain.utilities import WikipediaAPIWrapper, DuckDuckGoSearchAPIWrapper

# Set environment variables (if using LangSmith for tracing)
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"
if not os.path.exists(persist_directory):
    os.makedirs(persist_directory)
vectorstore = Chroma(persist_directory=persist_directory, embedding_function=embeddings)

# Initialize tools
duckduckgo_search = DuckDuckGoSearchAPIWrapper()
wikipedia = WikipediaAPIWrapper()

# Create Knowledge Base Tool
def create_knowledge_base_tool(vectorstore):
    def run_knowledge_base_tool(query: str) -> str:
        results = vectorstore.similarity_search(query, k=3)
        if results:
            return "\n\n".join([doc.page_content for doc in results])
        else:
            return "No relevant information found in the knowledge base."
    return Tool(
        name="Knowledge Base Tool",
        func=run_knowledge_base_tool,
        description="Useful for searching through the knowledge base using similarity search."
    )

knowledge_base_tool = create_knowledge_base_tool(vectorstore)

# Declare global variables
documents_list = []
knowledge_content = ""

# Load knowledge documents to include in the task description
def load_knowledge_documents():
    global documents_list
    contents = []
    for doc in documents_list:
        contents.append(doc.page_content)
    return "\n\n".join(contents) if contents else "No knowledge documents available."

# Define Agents using CrewAI with LiteLLM and Ollama

print("Documents: ", load_knowledge_documents())

# Boss Agent (Manager Agent) - **No tools assigned**
boss_agent = Agent(
    role='Boss',
    goal='Delegate tasks to appropriate agents and oversee the completion.',
    backstory='You are an experienced manager who oversees the team.',  # No dynamic content
    llm='ollama/llama3.1',
    verbose=True,
    allow_delegation=True
)

# General Knowledge Agent
agent1 = Agent(
    role='General Knowledge Agent',
    goal='Provide preliminary answers to general queries using web search and Wikipedia.',
    backstory='An agent proficient in general knowledge and current events.',
    tools=[
        Tool(
            name="DuckDuckGo Search",
            func=duckduckgo_search.run,
            description="Useful for answering questions about current events or the web."
        ),
        Tool(
            name="Wikipedia",
            func=wikipedia.run,
            description="Useful for accessing information from Wikipedia."
        ),
    ],
    llm='ollama/llama3.1',
    verbose=True
)

# Knowledge Base Agent
agent2 = Agent(
    role='Knowledge Base Agent',
    goal='Provide relevant information from the knowledge base.',
    backstory='An agent specializing in the knowledge base derived from uploaded documents.',
    tools=[knowledge_base_tool],
    llm='ollama/llama3.1',
    verbose=True
)

# Response Writer Agent
agent3 = Agent(
    role='Response Writer Agent',
    goal='Use the relevant information provided to write a detailed and informative answer to the user\'s question.',
    backstory='An agent that crafts comprehensive answers using only the relevant information supplied.',
    llm='ollama/llama3.1',
    verbose=True
)

# Functions for processing documents and clearing the database
def process_documents(files):
    global vectorstore
    global knowledge_content
    global documents_list

    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()
        documents_list.extend(documents)  # Store documents globally
        text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=200, length_function=len)
        docs = text_splitter.split_documents(documents)

        vectorstore.add_documents(docs)

    # Update the knowledge content after processing new documents
    knowledge_content = load_knowledge_documents()

    print("Knowledge content updated.")
    # For debugging: print the knowledge content
    print("Current Knowledge Content:")
    print(knowledge_content)

    result = f"Processed {len(processed_files)} new document(s)."
    if skipped_files:
        result += f"\nSkipped {len(skipped_files)} existing file(s): {', '.join(skipped_files)}"
        # For debugging: print skipped files
        print(f"Skipped files: {', '.join(skipped_files)}")
        gr.Warning(f"The following files already exist and were not processed: {', '.join(skipped_files)}")

    return result, get_processed_files()

def clear_database():
    global vectorstore
    global knowledge_content
    global documents_list

    try:
        vectorstore = None
        shutil.rmtree("uploads")
        os.makedirs("uploads", exist_ok=True)
        shutil.rmtree(persist_directory)
        os.makedirs(persist_directory, exist_ok=True)
    except Exception as e:
        print(f"Error clearing database: {e}")

    # Reinitialize the vectorstore and clear knowledge content
    vectorstore = Chroma(persist_directory=persist_directory, embedding_function=embeddings)
    knowledge_content = ""
    documents_list = []
    print("Database and uploaded files cleared.")
    return "Database and uploaded files cleared.", get_processed_files()

def get_processed_files():
    files = os.listdir("uploads") if os.path.exists("uploads") else []
    return "Processed files:\n" + "\n".join(files) if files else "No files processed yet."

# Function to handle user input and process with the crew
@traceable
def handle_user_input(user_message, history):
    global knowledge_content

    # Print the current knowledge content for debugging
    print("Knowledge Content at the time of handling user input:")
    print(knowledge_content)

    # Include knowledge_content in the task description
    initial_task_description = (
        f"The user asked: {user_message}\n\n"
        f"Relevant Knowledge Base Content:\n{knowledge_content}"
    )

    # Instantiate agents inside the function to avoid state carryover

    # Boss Agent (Manager Agent) - **No tools assigned**
    boss_agent = Agent(
        role='Boss',
        goal='Delegate tasks to appropriate agents and oversee the completion.',
        backstory='You are an experienced manager who oversees the team.',
        llm='ollama/llama3.1',
        verbose=True,
        allow_delegation=True
    )

    # General Knowledge Agent
    agent1 = Agent(
        role='General Knowledge Agent',
        goal='Provide preliminary answers to general queries using web search and Wikipedia.',
        backstory='An agent proficient in general knowledge and current events.',
        tools=[
            Tool(
                name="DuckDuckGo Search",
                func=duckduckgo_search.run,
                description="Useful for answering questions about current events or the web."
            ),
            Tool(
                name="Wikipedia",
                func=wikipedia.run,
                description="Useful for accessing information from Wikipedia."
            ),
        ],
        llm='ollama/llama3.1',
        verbose=True
    )

    # Knowledge Base Agent
    agent2 = Agent(
        role='Knowledge Base Agent',
        goal='Provide relevant information from the knowledge base.',
        backstory='An agent specializing in the knowledge base derived from uploaded documents.',
        tools=[knowledge_base_tool],
        llm='ollama/llama3.1',
        verbose=True
    )

    # Response Writer Agent
    agent3 = Agent(
        role='Response Writer Agent',
        goal='Use the relevant information provided to write a detailed and informative answer to the user\'s question.',
        backstory='An agent that crafts comprehensive answers using only the relevant information supplied.',
        llm='ollama/llama3.1',
        verbose=True
    )

    # Create a task with the updated description
    initial_task = Task(
        description=initial_task_description,
        expected_output="Gather relevant information to answer the user's question.",
        agent=None  # Let the boss agent decide who should handle it
    )

    # Create a new crew with the task and agents
    crew = Crew(
        agents=[agent1, agent2],
        tasks=[initial_task],
        process=Process.hierarchical,
        manager_agent=boss_agent,
        planning=True,
        manager_llm='ollama/llama3.1',
        verbose=True
    )
    # Kick off the crew
    print("Kicking off the crew for initial task...")
    crew_output = crew.kickoff()

    # Get the output from the initial task
    if initial_task.output and initial_task.output.raw:
        gathered_info = initial_task.output.raw
    else:
        gathered_info = "No relevant information was found."

    # For debugging: print gathered information
    print("Gathered Information:")
    print(gathered_info)

    # Create a second task for the Response Writer Agent
    response_task = Task(
        description=(
            f"Using the following information, write a detailed and informative answer "
            f"to the user's question:\n\n{gathered_info}"
        ),
        expected_output="A detailed and informative answer to the user's question.",
        agent=agent3  # Assign to the Response Writer Agent
    )

    # Create a new crew for the response task
    response_crew = Crew(
        agents=[agent3],
        tasks=[response_task],
        process=Process.sequential,
        verbose=True
    )
    # Kick off the response crew
    print("Kicking off the crew for response task...")
    response_crew_output = response_crew.kickoff()

    # Get the final answer
    if response_task.output and response_task.output.raw:
        bot_response = response_task.output.raw
    else:
        bot_response = "I'm sorry, I couldn't generate a response to your question."

    print("Crew Output:")
    print(response_crew_output)

    # For debugging: print the bot's final response
    print("Bot's Response:")
    print(bot_response)

    # Update the history
    history.append([user_message, bot_response])
    return "", history

# Gradio UI setup (same as before)
with gr.Blocks() as demo:
    gr.Markdown("# Collaborative Agents Chatbot with Boss Agent and Response Writer Agent")

    with gr.Tabs():
        with gr.TabItem("Chat"):
            chatbot = gr.Chatbot()
            msg = gr.Textbox(placeholder="Enter your message...", label="Your Message")
            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())

    # Define interactions
    msg.submit(handle_user_input, [msg, chatbot], [msg, chatbot], queue=False)
    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: [], outputs=chatbot, queue=False)

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