Skip to content

Latest commit

 

History

History
208 lines (146 loc) · 7.91 KB

tool_guide.md

File metadata and controls

208 lines (146 loc) · 7.91 KB

Custom Tools

Follow these instructions to create your own custom tools.

Custom tools will need to be built in the community folder. Make sure you've enabled the INSTALL_COMMUNITY_DEPS build arg in the docker-compose.yml file by setting it to true.

Step 1: Choose a Tool to Implement

You can take a tool implementation easily from:

Step 2: Select Your Tool Type

There are three types of tools:

  • Data Loader: This tool type retrieves data from a source. Examples include the LangChain Wikipedia retriever and Arxiv.
  • File Loader: This tool type loads and parses files. Examples include the LangChain Vector DB Retriever and LlamaIndex Upload PDF Retriever.
  • Function: This is a unique tool type that performs a specific action. Examples include the Python Interpreter and Calculator.

Step 3: Implement the Tool

Add your tool implementation here (please note that this link is subject to change).

If you need to install a new library to run your tool, execute the following command and run make dev again.

poetry add <MODULE> --group community

Implementing a Langchain Tool

Add the implementation inside a tool class that inherits from BaseTool. This class will need to implement the call() method, which should return a list of dictionary results.

Note: To enable citations, each result in the list should contain a "text" field.

For example, let's look at the community-implemented ArxivRetriever:

from typing import Any, Dict, List

from langchain_community.utilities import ArxivAPIWrapper

from community.tools import BaseTool


class ArxivRetriever(BaseTool):
    NAME = "arxiv"

    def __init__(self):
        self.client = ArxivAPIWrapper()

    @classmethod
    # If your tool requires any environment variables such as API keys,
    # you will need to assert that they're not None here
    def is_available(cls) -> bool:
        return True

    # Your tool needs to implement this call() method
    def call(self, parameters: str, **kwargs: Any) -> List[Dict[str, Any]]:
        result = self.client.run(parameters)

        return [{"text": result}] # <- Return list of results, in this case there is only one

Implementing a custom Tool

If you are implementing a custom tool, you can follow the same structure as the Langchain tools. This class will also need to implement the call() method, which should return a list of dictionary results with a "text" or "result" field.

Calculator Tool

For example, let's look at the calculator tool:

from typing import Any, Dict, List

from py_expression_eval import Parser

from backend.tools.base import BaseTool


class Calculator(BaseTool):
    """
    Function Tool that evaluates mathematical expressions.
    """
    NAME = "toolkit_calculator"

    @classmethod
    def is_available(cls) -> bool:
        return True

    async def call(self, parameters: dict, **kwargs: Any) -> List[Dict[str, Any]]:
        math_parser = Parser()
        to_evaluate = parameters.get("code", "").replace("pi", "PI").replace("e", "E")

        result = []
        try:
            result = {"result": math_parser.parse(to_evaluate).evaluate({})}
        except Exception:
            result = {"result": "Parsing error - syntax not allowed."}
        return result

The call() method receives a dictionary of parameters, which is defined in the Parameter_definitions field in the tool configuration (described in the section below). The parameters are generated by the model and passed to the tool, so the tool should assume that the parameters are in the correct format and handle them.

In this case, the calculator tool expects a code parameter that contains the mathematical expression to evaluate. We then parse the expression and evaluate it, returning the result.

Python Interpreter Tool

Another example is the Python Interpreter tool:

import json
import os
from typing import Any, Dict, Mapping

import requests
from langchain_core.tools import Tool as LangchainTool
from pydantic.v1 import BaseModel, Field

from backend.tools.base import BaseTool

class PythonInterpreter(BaseTool):
    """
    This class calls arbitrary code against a Python interpreter.
    It requires a URL at which the interpreter lives
    """
    NAME = "toolkit_python_interpreter"

    @classmethod
    def is_available(cls) -> bool:
        return cls.interpreter_url is not None

    def call(self, parameters: dict, **kwargs: Any):
        if not self.interpreter_url:
            raise Exception("Python Interpreter tool called while URL not set")

        code = parameters.get("code", "")
        res = requests.post(self.interpreter_url, json={"code": code})
        clean_res = self._clean_response(res.json())

        return clean_res

The python interpreter tool expects a code parameter that contains the python code to execute. It then sends a POST request to the interpreter URL with the code and returns the result. Note that this tool requires a secret interpreter_url to be set in the environment variables, and the same can be done for other tools that require secrets.

Step 4: Making Your Tool Available

To make your tool available, add its definition to the community tools config.py.

Start by adding the tool name to the ToolName enum found at the top of the file.

Next, include the tool configurations in the AVAILABLE_TOOLS list. The definition should include:

  • Name: Use the Enum definition you just created.
  • Implementation: Link the class you made in Step 3.
  • Parameter_definitions: If your class has specific configurations or fields that need to be set on __init__, set their values here.
  • Is_visible: A boolean value indicating whether this function should be visible in the UI.
  • Is_available: A boolean value indicating that this tool is ready to use. The class definition should help check for any variables or api keys that are required.
  • Error_message: A message returned when is_available is False.
  • Category: The type of tool.
  • Description: A brief description of the tool.
  • Env_vars: A list of secrets required by the tool.

Step 5: Test Your Tool!

Now, when you run the toolkit, all the visible tools, including the one you just added, should be available!

  • Run make dev
  • Open http://localhost:4000/
  • Open the side panel
  • Your tool should be there!
  • Select it and send a message that triggers it
  • Appreciate a grounded response with something ✨you created from scratch✨!

Remember, you can also access your tools via the API.

  • List tools:
curl --location --request GET 'http://localhost:8000/v1/tools' \
--header 'User-Id: me' \
--header 'Content-Type: application/json' \
--data '{}'
  • Chat turns with tools:
curl --location 'http://localhost:8000/v1/chat-stream' \
--header 'User-Id: me' \
--header 'Content-Type: application/json' \
--data '{
    "message": "Tell me about the aya model",
    "tools": [{"name": "Arxiv"}]
}
'

Step 6 (extra): Add Unit tests

If you would like to go above and beyond, it would be helpful to add some unit tests to ensure that your tool is working as expected. Create a file here and add a few test cases.