From 874f5ba08eb381852a906cd33a1f18ec253c90ee Mon Sep 17 00:00:00 2001 From: Alex Clarke Date: Fri, 7 Nov 2025 13:45:23 -0700 Subject: [PATCH] docs: Documented how to create custom tools in Python, and how custom tools are created and used --- docs/function-calling/CUSTOM-TOOLS.md | 119 ++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 docs/function-calling/CUSTOM-TOOLS.md diff --git a/docs/function-calling/CUSTOM-TOOLS.md b/docs/function-calling/CUSTOM-TOOLS.md new file mode 100644 index 0000000..1cabcad --- /dev/null +++ b/docs/function-calling/CUSTOM-TOOLS.md @@ -0,0 +1,119 @@ +# Custom Tools +Loki is designed to be as flexible and as customizable as possible. One of the key +features that enables this flexibility is the ability to create and integrate custom tools +into your Loki setup. This document provides a guide on how to create and use custom tools within Loki. + +## Quick Links + +- [Supported Languages](#supported-languages) +- [Creating a Custom Tool](#creating-a-custom-tool) + - [Environment Variables](#environment-variables) + - [Custom Bash-Based Tools](#custom-bash-based-tools) + - [Custom Python-Based Tools](#custom-python-based-tools) + + +--- + +## Supported Languages +Loki supports custom tools written in the following programming languages: + +* Python +* Bash + +## Creating a Custom Tool +All tools are created as scripts in either Python or Bash. They should be placed in the `functions/tools` directory. +The location of the `functions` directory varies between systems, so you can use the following command to locate +your `functions` directory: + +```shell +loki --info | grep functions_dir | awk '{print $2}' +``` + +Once you've created your custom tool, remember to add it to the `visible_tools` array in your global `config.yaml` file +to enable it globally. See the [Tools](TOOLS.md#enablingdisabling-global-tools) documentation for more information on how Loki utilizes the +`visible_tools` array. + +### Environment Variables +All tools have access to the following environment variables that provide context about the current execution environment: + +| Variable | Description | +|----------------------|--------------------------------------------------------------------------------------------------------------------------------------------| +| `LLM_OUTPUT` | Indicates where the output of the tool should go.
In certain situations, this may be set to a temporary file instead of `/dev/stdout`. | +| `LLM_ROOT_DIR` | The root `config_dir` directory for Loki
(i.e. `dirname $(loki --info \| grep config_file \| awk '{print $2}')`) | +| `LLM_TOOL_NAME` | The name of the tool being executed | +| `LLM_TOOL_CACHE_DIR` | A directory specific to the tool for storing cache or temporary files | + +Loki also searches the tools directory on startup for a `.env` file. If found, all tools in `functions/tools/` will have +the environment variables defined in the `.env` file available to them. + +### Custom Bash-Based Tools +To create a Bash-based tool, refer to the [custom bash tools documentation](CUSTOM-BASH-TOOLS.md). + +### Custom Python-Based Tools +Loki supports tools written in Python. + +Each Python-based tool must follow a specific structure in order for Loki to be able to properly compile and +execute it: + +* The tool must be a Python script with a `.py` file extension. +* The tool must have a `def run` function that serves as the entry point for the tool. +* The `run` function must accept parameters that define the inputs for the tool. + * Always use type hints to specify the data type of each parameter. + * Use `Optional[...]` to indicate optional parameters +* The `run` function must return a `str`. + * For Python, this is automatically written to the `LLM_OUTPUT` environment variable, so there's no need to explicitly + write to the environment variable within the function. +* The function must also have a docstring that describes the tool and its parameters. + * Each parameter in the `run` function should be documented in the docstring using the `Args:` section. They should use the following format: + * `: ` Where + * ``: The name of the parameter + * ``: The description of the parameter + * These are *very* important because these descriptions are what's passed to the LLM as the description of the tool, + letting the LLM know what the tool does and how to use it. + +It's important to note that any functions prefixed with `_` are not sent to the LLM, so they will be invisible to the LLM +at runtime. + +Below is the [`demo_py.py`](../../assets/functions/tools/demo_py.py) tool definition that comes pre-packaged with +Loki and demonstrates how to create a Python-based tool: + +```python +import os +from typing import List, Literal, Optional + +def run( + string: str, + string_enum: Literal["foo", "bar"], + boolean: bool, + integer: int, + number: float, + array: List[str], + string_optional: Optional[str] = None, + array_optional: Optional[List[str]] = None, +): + """Demonstrates how to create a tool using Python and how to use comments. + Args: + string: Define a required string property + string_enum: Define a required string property with enum + boolean: Define a required boolean property + integer: Define a required integer property + number: Define a required number property + array: Define a required string array property + string_optional: Define an optional string property + array_optional: Define an optional string array property + """ + output = f"""string: {string} +string_enum: {string_enum} +string_optional: {string_optional} +boolean: {boolean} +integer: {integer} +number: {number} +array: {array} +array_optional: {array_optional}""" + + for key, value in os.environ.items(): + if key.startswith("LLM_"): + output = f"{output}\n{key}: {value}" + + return output +```