From b7670cc762206445e4e0ffed76f535c661bb85f4 Mon Sep 17 00:00:00 2001 From: oobabooga <112222186+oobabooga@users.noreply.github.com> Date: Fri, 13 Mar 2026 04:00:30 -0700 Subject: [PATCH] Add a tool calling tutorial --- docs/Tool Calling Tutorial.md | 144 ++++++++++++++++++++++++++++++++++ 1 file changed, 144 insertions(+) create mode 100644 docs/Tool Calling Tutorial.md diff --git a/docs/Tool Calling Tutorial.md b/docs/Tool Calling Tutorial.md new file mode 100644 index 00000000..170bdff7 --- /dev/null +++ b/docs/Tool Calling Tutorial.md @@ -0,0 +1,144 @@ +## Tool calling in the UI + +### 1. Load a model with tool-calling support + +Load a model with tool-calling support (Qwen, Mistral, Llama 4, etc.) from the Model tab. + +### 2. Select tools + +In the chat sidebar, check the tools you want the model to use: + +- **web_search** -- Search the web using DuckDuckGo. +- **fetch_webpage** -- Fetch the content of a URL. +- **calculate** -- Evaluate math expressions. +- **get_datetime** -- Get the current date and time. +- **roll_dice** -- Roll dice. + +### 3. Chat + +Send a message as usual. When the model decides it needs a tool, it will call it automatically. You will see each tool call and its result in a collapsible accordion inside the chat message. + +The model may call multiple tools in sequence before giving its final answer. + +## Writing custom tools + +Each tool is a single `.py` file in `user_data/tools/`. It needs two things: + +1. A `tool` dictionary that describes the function (name, description, parameters). +2. An `execute(arguments)` function that runs it and returns the result. + +Here is a minimal example (`user_data/tools/get_datetime.py`): + +```python +from datetime import datetime + +tool = { + "type": "function", + "function": { + "name": "get_datetime", + "description": "Get the current date and time.", + "parameters": { + "type": "object", + "properties": {}, + } + } +} + + +def execute(arguments): + now = datetime.now() + return {"date": now.strftime("%Y-%m-%d"), "time": now.strftime("%I:%M %p")} +``` + +An example with parameters (`user_data/tools/roll_dice.py`): + +```python +import random + +tool = { + "type": "function", + "function": { + "name": "roll_dice", + "description": "Roll one or more dice with the specified number of sides.", + "parameters": { + "type": "object", + "properties": { + "count": {"type": "integer", "description": "Number of dice to roll.", "default": 1}, + "sides": {"type": "integer", "description": "Number of sides per die.", "default": 20}, + }, + } + } +} + + +def execute(arguments): + count = max(1, min(arguments.get("count", 1), 1000)) + sides = max(2, min(arguments.get("sides", 20), 1000)) + rolls = [random.randint(1, sides) for _ in range(count)] + return {"rolls": rolls, "total": sum(rolls)} +``` + +You can open the built-in tools in `user_data/tools/` for more examples. + +## Tool calling over the API + +Tool calling over the API follows the [OpenAI API](https://platform.openai.com/docs/guides/function-calling) convention. Define your tools, send them with your messages, and handle tool calls in a loop until the model gives a final answer. + +```python +import json +import requests + +url = "http://127.0.0.1:5000/v1/chat/completions" + +tools = [ + { + "type": "function", + "function": { + "name": "get_weather", + "description": "Get the current weather for a given location.", + "parameters": { + "type": "object", + "properties": { + "location": {"type": "string", "description": "City name"}, + }, + "required": ["location"] + } + } + } +] + + +def execute_tool(name, arguments): + if name == "get_weather": + return {"temperature": "14°C", "condition": "partly cloudy"} + return {"error": f"Unknown tool: {name}"} + + +messages = [{"role": "user", "content": "What's the weather like in Paris?"}] + +for _ in range(10): + response = requests.post(url, json={"messages": messages, "tools": tools}).json() + choice = response["choices"][0] + + if choice["finish_reason"] == "tool_calls": + messages.append({ + "role": "assistant", + "content": choice["message"]["content"], + "tool_calls": choice["message"]["tool_calls"], + }) + + for tool_call in choice["message"]["tool_calls"]: + name = tool_call["function"]["name"] + arguments = json.loads(tool_call["function"]["arguments"]) + result = execute_tool(name, arguments) + print(f"Tool call: {name}({arguments}) => {result}") + + messages.append({ + "role": "tool", + "tool_call_id": tool_call["id"], + "content": json.dumps(result), + }) + else: + print(f"\nAssistant: {choice['message']['content']}") + break +```