Traceback (most recent call last):
  File "C:\Users\theod\Desktop\Coding\AI\rag_test\venv\lib\site-packages\crewai\agents\crew_agent_executor.py", line 124, in _invoke_loop
    formatted_answer = self._format_answer(answer)
  File "C:\Users\theod\Desktop\Coding\AI\rag_test\venv\lib\site-packages\crewai\agents\crew_agent_executor.py", line 349, in _format_answer
    return CrewAgentParser(agent=self.agent).parse(answer)
  File "C:\Users\theod\Desktop\Coding\AI\rag_test\venv\lib\site-packages\crewai\agents\parser.py", line 100, in parse
    raise OutputParserException(
crewai.agents.parser.OutputParserException: I did it wrong. Invalid Format: I missed the 'Action:' after 'Thought:'. I will do right next, and don't use a tool I have already used.

If you don't need to use any more tools, you must give your best complete final answer, make sure it satisfy the expect criteria, use the EXACT format below:

Thought: I now can give a great answer
Final Answer: my best complete final answer to the task.



During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\Users\theod\Desktop\Coding\AI\rag_test\venv\lib\site-packages\crewai\agents\crew_agent_executor.py", line 124, in _invoke_loop
    formatted_answer = self._format_answer(answer)
  File "C:\Users\theod\Desktop\Coding\AI\rag_test\venv\lib\site-packages\crewai\agents\crew_agent_executor.py", line 349, in _format_answer
    return CrewAgentParser(agent=self.agent).parse(answer)
  File "C:\Users\theod\Desktop\Coding\AI\rag_test\venv\lib\site-packages\crewai\agents\parser.py", line 100, in parse
    raise OutputParserException(
crewai.agents.parser.OutputParserException: I did it wrong. Invalid Format: I missed the 'Action:' after 'Thought:'. I will do right next, and don't use a tool I have already used.

If you don't need to use any more tools, you must give your best complete final answer, make sure it satisfy the expect criteria, use the EXACT format below:

Thought: I now can give a great answer
Final Answer: my best complete final answer to the task.



During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\Users\theod\Desktop\Coding\AI\rag_test\venv\lib\site-packages\crewai\agents\crew_agent_executor.py", line 124, in _invoke_loop
    formatted_answer = self._format_answer(answer)
  File "C:\Users\theod\Desktop\Coding\AI\rag_test\venv\lib\site-packages\crewai\agents\crew_agent_executor.py", line 349, in _format_answer
    return CrewAgentParser(agent=self.agent).parse(answer)
  File "C:\Users\theod\Desktop\Coding\AI\rag_test\venv\lib\site-packages\crewai\agents\parser.py", line 100, in parse
    raise OutputParserException(
crewai.agents.parser.OutputParserException: I did it wrong. Invalid Format: I missed the 'Action:' after 'Thought:'. I will do right next, and don't use a tool I have already used.

If you don't need to use any more tools, you must give your best complete final answer, make sure it satisfy the expect criteria, use the EXACT format below:

Thought: I now can give a great answer
Final Answer: my best complete final answer to the task.



During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\Users\theod\Desktop\Coding\AI\rag_test\venv\lib\site-packages\crewai\agents\crew_agent_executor.py", line 124, in _invoke_loop
    formatted_answer = self._format_answer(answer)
  File "C:\Users\theod\Desktop\Coding\AI\rag_test\venv\lib\site-packages\crewai\agents\crew_agent_executor.py", line 349, in _format_answer
    return CrewAgentParser(agent=self.agent).parse(answer)
  File "C:\Users\theod\Desktop\Coding\AI\rag_test\venv\lib\site-packages\crewai\agents\parser.py", line 100, in parse
    raise OutputParserException(
crewai.agents.parser.OutputParserException: I did it wrong. Invalid Format: I missed the 'Action:' after 'Thought:'. I will do right next, and don't use a tool I have already used.

If you don't need to use any more tools, you must give your best complete final answer, make sure it satisfy the expect criteria, use the EXACT format below:

Thought: I now can give a great answer
Final Answer: my best complete final answer to the task.



During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\Users\theod\Desktop\Coding\AI\rag_test\venv\lib\site-packages\crewai\agents\crew_agent_executor.py", line 124, in _invoke_loop
    formatted_answer = self._format_answer(answer)
  File "C:\Users\theod\Desktop\Coding\AI\rag_test\venv\lib\site-packages\crewai\agents\crew_agent_executor.py", line 349, in _format_answer
    return CrewAgentParser(agent=self.agent).parse(answer)
  File "C:\Users\theod\Desktop\Coding\AI\rag_test\venv\lib\site-packages\crewai\agents\parser.py", line 100, in parse
    raise OutputParserException(
crewai.agents.parser.OutputParserException: I did it wrong. Invalid Format: I missed the 'Action:' after 'Thought:'. I will do right next, and don't use a tool I have already used.

If you don't need to use any more tools, you must give your best complete final answer, make sure it satisfy the expect criteria, use the EXACT format below:

Thought: I now can give a great answer
Final Answer: my best complete final answer to the task.



During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\Users\theod\Desktop\Coding\AI\rag_test\venv\lib\site-packages\crewai\agents\crew_agent_executor.py", line 124, in _invoke_loop
    formatted_answer = self._format_answer(answer)
  File "C:\Users\theod\Desktop\Coding\AI\rag_test\venv\lib\site-packages\crewai\agents\crew_agent_executor.py", line 349, in _format_answer
    return CrewAgentParser(agent=self.agent).parse(answer)
  File "C:\Users\theod\Desktop\Coding\AI\rag_test\venv\lib\site-packages\crewai\agents\parser.py", line 100, in parse
    raise OutputParserException(
crewai.agents.parser.OutputParserException: I did it wrong. Invalid Format: I missed the 'Action:' after 'Thought:'. I will do right next, and don't use a tool I have already used.

If you don't need to use any more tools, you must give your best complete final answer, make sure it satisfy the expect criteria, use the EXACT format below:

Thought: I now can give a great answer
Final Answer: my best complete final answer to the task.



During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\Users\theod\Desktop\Coding\AI\rag_test\venv\lib\site-packages\crewai\agents\crew_agent_executor.py", line 124, in _invoke_loop
    formatted_answer = self._format_answer(answer)
  File "C:\Users\theod\Desktop\Coding\AI\rag_test\venv\lib\site-packages\crewai\agents\crew_agent_executor.py", line 349, in _format_answer
    return CrewAgentParser(agent=self.agent).parse(answer)
  File "C:\Users\theod\Desktop\Coding\AI\rag_test\venv\lib\site-packages\crewai\agents\parser.py", line 100, in parse
    raise OutputParserException(
crewai.agents.parser.OutputParserException: I did it wrong. Invalid Format: I missed the 'Action:' after 'Thought:'. I will do right next, and don't use a tool I have already used.

If you don't need to use any more tools, you must give your best complete final answer, make sure it satisfy the expect criteria, use the EXACT format below:

Thought: I now can give a great answer
Final Answer: my best complete final answer to the task.



During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\Users\theod\Desktop\Coding\AI\rag_test\venv\lib\site-packages\crewai\agents\crew_agent_executor.py", line 124, in _invoke_loop
    formatted_answer = self._format_answer(answer)
  File "C:\Users\theod\Desktop\Coding\AI\rag_test\venv\lib\site-packages\crewai\agents\crew_agent_executor.py", line 349, in _format_answer
    return CrewAgentParser(agent=self.agent).parse(answer)
  File "C:\Users\theod\Desktop\Coding\AI\rag_test\venv\lib\site-packages\crewai\agents\parser.py", line 100, in parse
    raise OutputParserException(
crewai.agents.parser.OutputParserException: I did it wrong. Invalid Format: I missed the 'Action:' after 'Thought:'. I will do right next, and don't use a tool I have already used.

If you don't need to use any more tools, you must give your best complete final answer, make sure it satisfy the expect criteria, use the EXACT format below:

Thought: I now can give a great answer
Final Answer: my best complete final answer to the task.



During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\Users\theod\Desktop\Coding\AI\rag_test\venv\lib\site-packages\litellm\main.py", line 2557, in completion
    generator = ollama.get_ollama_response(
  File "C:\Users\theod\Desktop\Coding\AI\rag_test\venv\lib\site-packages\litellm\llms\ollama.py", line 265, in get_ollama_response
    response = requests.post(
  File "C:\Users\theod\Desktop\Coding\AI\rag_test\venv\lib\site-packages\requests\api.py", line 115, in post
    return request("post", url, data=data, json=json, **kwargs)
  File "C:\Users\theod\Desktop\Coding\AI\rag_test\venv\lib\site-packages\requests\api.py", line 59, in request
    return session.request(method=method, url=url, **kwargs)
  File "C:\Users\theod\Desktop\Coding\AI\rag_test\venv\lib\site-packages\requests\sessions.py", line 589, in request
    resp = self.send(prep, **send_kwargs)
  File "C:\Users\theod\Desktop\Coding\AI\rag_test\venv\lib\site-packages\requests\sessions.py", line 703, in send
    r = adapter.send(request, **kwargs)
  File "C:\Users\theod\Desktop\Coding\AI\rag_test\venv\lib\site-packages\requests\adapters.py", line 667, in send
    resp = conn.urlopen(
  File "C:\Users\theod\Desktop\Coding\AI\rag_test\venv\lib\site-packages\urllib3\connectionpool.py", line 789, in urlopen
    response = self._make_request(
  File "C:\Users\theod\Desktop\Coding\AI\rag_test\venv\lib\site-packages\urllib3\connectionpool.py", line 536, in _make_request
    response = conn.getresponse()
  File "C:\Users\theod\Desktop\Coding\AI\rag_test\venv\lib\site-packages\urllib3\connection.py", line 507, in getresponse
    httplib_response = super().getresponse()
  File "C:\Users\theod\AppData\Local\Programs\Python\Python310\lib\http\client.py", line 1374, in getresponse
    response.begin()
  File "C:\Users\theod\AppData\Local\Programs\Python\Python310\lib\http\client.py", line 337, in begin
    self.headers = self.msg = parse_headers(self.fp)
  File "C:\Users\theod\AppData\Local\Programs\Python\Python310\lib\http\client.py", line 236, in parse_headers
    return email.parser.Parser(_class=_class).parsestr(hstring)
  File "C:\Users\theod\AppData\Local\Programs\Python\Python310\lib\email\parser.py", line 67, in parsestr
    return self.parse(StringIO(text), headersonly=headersonly)
  File "C:\Users\theod\AppData\Local\Programs\Python\Python310\lib\email\parser.py", line 56, in parse
    feedparser.feed(data)
  File "C:\Users\theod\AppData\Local\Programs\Python\Python310\lib\email\feedparser.py", line 176, in feed
    self._call_parse()
  File "C:\Users\theod\AppData\Local\Programs\Python\Python310\lib\email\feedparser.py", line 180, in _call_parse
    self._parse()
  File "C:\Users\theod\AppData\Local\Programs\Python\Python310\lib\email\feedparser.py", line 295, in _parsegen
    if self._cur.get_content_maintype() == 'message':
  File "C:\Users\theod\AppData\Local\Programs\Python\Python310\lib\email\message.py", line 594, in get_content_maintype
    ctype = self.get_content_type()
  File "C:\Users\theod\AppData\Local\Programs\Python\Python310\lib\email\message.py", line 578, in get_content_type
    value = self.get('content-type', missing)
  File "C:\Users\theod\AppData\Local\Programs\Python\Python310\lib\email\message.py", line 471, in get
    return self.policy.header_fetch_parse(k, v)
  File "C:\Users\theod\AppData\Local\Programs\Python\Python310\lib\email\_policybase.py", line 316, in header_fetch_parse
    return self._sanitize_header(name, value)
  File "C:\Users\theod\AppData\Local\Programs\Python\Python310\lib\email\_policybase.py", line 287, in _sanitize_header
    if _has_surrogates(value):
  File "C:\Users\theod\AppData\Local\Programs\Python\Python310\lib\email\utils.py", line 57, in _has_surrogates
    s.encode()
RecursionError: maximum recursion depth exceeded while calling a Python object


During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\Users\theod\Desktop\Coding\AI\rag_test\venv\lib\site-packages\litellm\main.py", line 2557, in completion
    generator = ollama.get_ollama_response(
  File "C:\Users\theod\Desktop\Coding\AI\rag_test\venv\lib\site-packages\litellm\llms\ollama.py", line 265, in get_ollama_response
    response = requests.post(
  File "C:\Users\theod\Desktop\Coding\AI\rag_test\venv\lib\site-packages\requests\api.py", line 115, in post
    return request("post", url, data=data, json=json, **kwargs)
  File "C:\Users\theod\Desktop\Coding\AI\rag_test\venv\lib\site-packages\requests\api.py", line 59, in request
    return session.request(method=method, url=url, **kwargs)
  File "C:\Users\theod\Desktop\Coding\AI\rag_test\venv\lib\site-packages\requests\sessions.py", line 589, in request
    resp = self.send(prep, **send_kwargs)
  File "C:\Users\theod\Desktop\Coding\AI\rag_test\venv\lib\site-packages\requests\sessions.py", line 703, in send
    r = adapter.send(request, **kwargs)
  File "C:\Users\theod\Desktop\Coding\AI\rag_test\venv\lib\site-packages\requests\adapters.py", line 667, in send
    resp = conn.urlopen(
  File "C:\Users\theod\Desktop\Coding\AI\rag_test\venv\lib\site-packages\urllib3\connectionpool.py", line 789, in urlopen
    response = self._make_request(
  File "C:\Users\theod\Desktop\Coding\AI\rag_test\venv\lib\site-packages\urllib3\connectionpool.py", line 495, in _make_request
    conn.request(
  File "C:\Users\theod\Desktop\Coding\AI\rag_test\venv\lib\site-packages\urllib3\connection.py", line 441, in request
    self.endheaders()
  File "C:\Users\theod\AppData\Local\Programs\Python\Python310\lib\http\client.py", line 1277, in endheaders
    self._send_output(message_body, encode_chunked=encode_chunked)
  File "C:\Users\theod\AppData\Local\Programs\Python\Python310\lib\http\client.py", line 1037, in _send_output
    self.send(msg)
  File "C:\Users\theod\AppData\Local\Programs\Python\Python310\lib\http\client.py", line 975, in send
    self.connect()
  File "C:\Users\theod\Desktop\Coding\AI\rag_test\venv\lib\site-packages\urllib3\connection.py", line 279, in connect
    self.sock = self._new_conn()
  File "C:\Users\theod\Desktop\Coding\AI\rag_test\venv\lib\site-packages\urllib3\connection.py", line 199, in _new_conn
    sock = connection.create_connection(
  File "C:\Users\theod\Desktop\Coding\AI\rag_test\venv\lib\site-packages\urllib3\util\connection.py", line 60, in create_connection
    for res in socket.getaddrinfo(host, port, family, socket.SOCK_STREAM):
  File "C:\Users\theod\AppData\Local\Programs\Python\Python310\lib\socket.py", line 957, in getaddrinfo
    addrlist.append((_intenum_converter(af, AddressFamily),
  File "C:\Users\theod\AppData\Local\Programs\Python\Python310\lib\socket.py", line 105, in _intenum_converter
    return enum_klass(value)
  File "C:\Users\theod\AppData\Local\Programs\Python\Python310\lib\enum.py", line 385, in __call__
    return cls.__new__(cls, value)
  File "C:\Users\theod\AppData\Local\Programs\Python\Python310\lib\enum.py", line 688, in __new__
    return cls._value2member_map_[value]
RecursionError: maximum recursion depth exceeded while calling a Python object

Code:
import os
import shutil
import gradio as gr
from itertools import chain

# 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"] = "false"  # Disable tracing for faster responses

# 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."

# Get list of processed files (knowledge base files)
def get_knowledge_base_files():
    if os.path.exists("uploads"):
        files = os.listdir("uploads")
        return "\n".join(files) if files else "No knowledge base documents available."
    else:
        return "No knowledge base documents available."

# 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)
        print(f"Number of documents in vector store: {vectorstore._collection.count()}")
        # Print the content of the documents
        for doc in docs:
            print(f"Indexed Document Chunk: {doc.page_content[:100]}")  # Print first 100 chars

    # 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
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
    knowledge_files = get_knowledge_base_files()
    initial_task_description = (
        f"Using the following information, write a detailed and informative answer "
        f"to the user's question:\n\n{user_message}"
        # f"The user asked: {user_message}\n\n"
        # f"The following documents are available in the knowledge base: {knowledge_files}\n"
        # f"Use the knowledge base to help answer the question. If the information is not available, inform the boss."
    )

    # Instantiate agents inside the function to avoid state carryover

    # System template for agents
    system_template = """You are {role}. {backstory}"""

    # Response template for agents
    response_template = """{{ .Response }}"""

    # Boss Agent Prompt Template
    boss_agent_prompt = """
{system_message}

Your goal is to {goal}

As the boss, you should delegate tasks to the appropriate agents based on their capabilities and the available knowledge base documents.

Remember to be clear and concise in your instructions.
"""

    # 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. '
            'You are aware of the following documents available in the knowledge base of the knowlage base agent.: '
            f'{knowledge_files}. '
            'You should assign tasks accordingly.'
        ),
        llm='ollama/llama3.1',
        verbose=True,
        allow_delegation=True,
        max_iterations=3,  # Limit iterations for faster responses
        system_template=system_template,
        prompt_template=boss_agent_prompt,
        response_template=response_template
    )

    # Rename the agent with internet access to "Internet Research Agent"
    agent1_prompt = """
{system_message}

Your goal is to {goal}

You have access to the following tools:
{tool_names_and_descriptions}

Use the following format:

Thought: Think about what you need to do.
Action: The action to take, should be one of [{tool_names}]
Action Input: The input to the action.
Observation: The result of the action.

(You can repeat the Thought/Action/Action Input/Observation sequence as needed.)

Thought: I now know the final answer.
Final Answer: Provide the final answer to the user's question.

Remember to be concise and clear in your responses.
"""

    # Internet Research Agent
    agent1 = Agent(
        role='Internet Research Agent',
        goal='Provide preliminary answers to general queries using web search and Wikipedia.',
        backstory='An agent proficient in researching information on the internet.',
        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,
        prompt_template=agent1_prompt,
        system_template=system_template,
        response_template=response_template,
        max_iterations=3  # Limit iterations for faster responses
    )

    # Improved Knowledge Base Agent Prompt
    agent2_prompt = """
{system_message}

Your goal is to {goal}

You have access to the following tool:
{tool_names_and_descriptions}

Use the following format:

Thought: Think about what you need to do.
Action: The action to take, which should be "Knowledge Base Tool"
Action Input: The input to the action.
Observation: The result of the action.

(You can repeat the Thought/Action/Action Input/Observation sequence as needed.)

If no relevant information is found, inform the boss that the knowledge base does not contain the required information.

Thought: I now know the final answer.
Final Answer: Provide the relevant information found or state that the information is not available.

Remember to be concise and focus on providing information from the knowledge base.
"""

    # 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. '
            f'The knowledge base contains the following documents: {knowledge_files}.'
        ),
        tools=[knowledge_base_tool],
        llm='ollama/llama3.1',
        verbose=True,
        prompt_template=agent2_prompt,
        system_template=system_template,
        response_template=response_template,
        max_iterations=3  # Limit iterations for faster responses
    )

    # Improved Response Writer Agent Prompt
    agent3_prompt = """
{system_message}

Your goal is to {goal}

Use the information provided to craft a detailed and informative answer to the user's question.

Remember to be clear, concise, and focus on providing value to the user.
"""

    # 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,
        prompt_template=agent3_prompt,
        system_template=system_template,
        response_template=response_template,
        max_iterations=3  # Limit iterations for faster responses
    )

    # 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. Or if not needet, answer the user directly!",
        agent=boss_agent  # 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=False,
        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_description = (
        f"Using the following information, write a detailed and informative answer "
        f"to the user's question:\n\n{gathered_info}"
    )

    response_task = Task(
        description=response_task_description,
        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
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(max_threads=1)  # Set max_threads to 1 to avoid overloading