I changed my code to use ChatOllama that really supports tool calling instead of just prompting:
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_ollama 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"
# )

llm = ChatOllama(
    model="llama3.1",
    temperature=0,
    # other params...
)

# 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()





but no I have the issue that my old tools dont work anymore. here is some documentation from the ChatOllama that maybe help you:
ChatOllama

Ollama allows you to run open-source large language models, such as Llama 2, locally.

Ollama bundles model weights, configuration, and data into a single package, defined by a Modelfile.

It optimizes setup and configuration details, including GPU usage.

For a complete list of supported models and model variants, see the Ollama model library.
Overview
Integration details
Class	Package	Local	Serializable	JS support	Package downloads	Package latest
ChatOllama	langchain-ollama	✅	❌	✅	PyPI - Downloads	PyPI - Version
Model features
Tool calling	Structured output	JSON mode	Image input	Audio input	Video input	Token-level streaming	Native async	Token usage	Logprobs
✅	❌	✅	❌	❌	❌	✅	✅	❌	❌
Setup

First, follow these instructions to set up and run a local Ollama instance:

    Download and install Ollama onto the available supported platforms (including Windows Subsystem for Linux)
    Fetch available LLM model via ollama pull <name-of-model>
        View a list of available models via the model library
        e.g., ollama pull llama3
    This will download the default tagged version of the model. Typically, the default points to the latest, smallest sized-parameter model.

    On Mac, the models will be download to ~/.ollama/models

    On Linux (or WSL), the models will be stored at /usr/share/ollama/.ollama/models

    Specify the exact version of the model of interest as such ollama pull vicuna:13b-v1.5-16k-q4_0 (View the various tags for the Vicuna model in this instance)
    To view all pulled models, use ollama list
    To chat directly with a model from the command line, use ollama run <name-of-model>
    View the Ollama documentation for more commands. Run ollama help in the terminal to see available commands too.

If you want to get automated tracing of your model calls you can also set your LangSmith API key by uncommenting below:

# os.environ["LANGSMITH_API_KEY"] = getpass.getpass("Enter your LangSmith API key: ")
# os.environ["LANGSMITH_TRACING"] = "true"

Installation

The LangChain Ollama integration lives in the langchain-ollama package:

%pip install -qU langchain-ollama

Instantiation

Now we can instantiate our model object and generate chat completions:

    TODO: Update model instantiation with relevant params.

from langchain_ollama import ChatOllama

llm = ChatOllama(
    model="llama3.1",
    temperature=0,
    # other params...
)

API Reference:ChatOllama
Invocation

from langchain_core.messages import AIMessage

messages = [
    (
        "system",
        "You are a helpful assistant that translates English to French. Translate the user sentence.",
    ),
    ("human", "I love programming."),
]
ai_msg = llm.invoke(messages)
ai_msg

API Reference:AIMessage

AIMessage(content='The translation of "I love programming" from English to French is:\n\n"J\'adore programmer."', response_metadata={'model': 'llama3.1', 'created_at': '2024-08-19T16:05:32.81965Z', 'message': {'role': 'assistant', 'content': ''}, 'done_reason': 'stop', 'done': True, 'total_duration': 2167842917, 'load_duration': 54222584, 'prompt_eval_count': 35, 'prompt_eval_duration': 893007000, 'eval_count': 22, 'eval_duration': 1218962000}, id='run-0863daa2-43bf-4a43-86cc-611b23eae466-0', usage_metadata={'input_tokens': 35, 'output_tokens': 22, 'total_tokens': 57})

print(ai_msg.content)

The translation of "I love programming" from English to French is:

"J'adore programmer."

Chaining

We can chain our model with a prompt template like so:

from langchain_core.prompts import ChatPromptTemplate

prompt = ChatPromptTemplate.from_messages(
    [
        (
            "system",
            "You are a helpful assistant that translates {input_language} to {output_language}.",
        ),
        ("human", "{input}"),
    ]
)

chain = prompt | llm
chain.invoke(
    {
        "input_language": "English",
        "output_language": "German",
        "input": "I love programming.",
    }
)

API Reference:ChatPromptTemplate

AIMessage(content='Das Programmieren ist mir ein Leidenschaft! (That\'s "Programming is my passion!" in German.) Would you like me to translate anything else?', response_metadata={'model': 'llama3.1', 'created_at': '2024-08-19T16:05:34.893548Z', 'message': {'role': 'assistant', 'content': ''}, 'done_reason': 'stop', 'done': True, 'total_duration': 2045997333, 'load_duration': 22584792, 'prompt_eval_count': 30, 'prompt_eval_duration': 213210000, 'eval_count': 32, 'eval_duration': 1808541000}, id='run-d18e1c6b-50e0-4b1d-b23a-973fa058edad-0', usage_metadata={'input_tokens': 30, 'output_tokens': 32, 'total_tokens': 62})

Tool calling

We can use tool calling with an LLM that has been fine-tuned for tool use:

ollama pull llama3.1

Details on creating custom tools are available in this guide. Below, we demonstrate how to create a tool using the @tool decorator on a normal python function.

from typing import List

from langchain_core.tools import tool
from langchain_ollama import ChatOllama


@tool
def validate_user(user_id: int, addresses: List[str]) -> bool:
    """Validate user using historical addresses.

    Args:
        user_id (int): the user ID.
        addresses (List[str]): Previous addresses as a list of strings.
    """
    return True


llm = ChatOllama(
    model="llama3.1",
    temperature=0,
).bind_tools([validate_user])

result = llm.invoke(
    "Could you validate user 123? They previously lived at "
    "123 Fake St in Boston MA and 234 Pretend Boulevard in "
    "Houston TX."
)
result.tool_calls

API Reference:tool | ChatOllama

[{'name': 'validate_user',
  'args': {'addresses': '["123 Fake St, Boston, MA", "234 Pretend Boulevard, Houston, TX"]',
   'user_id': '123'},
  'id': '40fe3de0-500c-4b91-9616-5932a929e640',
  'type': 'tool_call'}]