Tool Calling

What is Tool Calling?

  • Allows LLMs to interact with other systems

  • Supported by most of the newest LLMs, but not all

  • Sounds complicated? Scary? It’s not too bad, actually…

Reference: https://jcheng5.github.io/llm-quickstart/quickstart.html#/how-it-works

How it does NOT work

How it DOES work

A tool is a function

  • A tool call is a function that the LLM can use
  • It can either infer what the function does by the function name, docstring, and/or parameter names
  • You can also provide it the context you want

Example: Weather Tool

To ask the LLM about the weather in the current location we need to write a function that does a few things:

  1. Geocode a location to a latitude and longitude (this can also be an API)
  2. Use the latitude and longitude in an API that can look up the weather

Example Weather tool - Geocode

import requests
from typing import Dict


def get_coordinates(location: str) -> Dict[str, float]:
    base_url: str = "https://nominatim.openstreetmap.org/search"
    params = {
        "q": location,
        "format": "json",
        "limit": 1,  # only return the top result
        "addressdetails": 1,  # include detailed address info
    }

    headers = {"User-Agent": "example_weather/1.0 (daniel.chen@posit.co)"}
    response = requests.get(
        base_url,
        params=params,
        headers=headers,
    )

    data = response.json()

    lat = float(data[0]["lat"])
    lon = float(data[0]["lon"])

    return {"lat": lat, "lon": lon}

Example Weather tool - Geocode

get_coordinates("New York City")

Example Weather tool - Weather

import requests


def get_weather(lat: float, lon: float):
    base_url = "https://api.open-meteo.com/v1/forecast"
    params = {
        "latitude": lat,
        "longitude": lon,
        "current_weather": True,
    }

    response = requests.get(
        base_url,
        params=params,
    )

    data = response.json()

    return {k: v for k, v in data.items()}

Example Weather tool - Weather

get_weather(40.7127281, -74.0060152)

Example Weather tool - Register

from chatlas import ChatAnthropic

chat = ChatAnthropic()

chat.register_tool(get_coordinates)
chat.register_tool(get_weather)

Demo: Weather Python

import requests
from typing import Dict

from chatlas import ChatAnthropic
from dotenv import load_dotenv

load_dotenv()


def get_coordinates(location: str) -> Dict[str, float]:
    base_url: str = "https://nominatim.openstreetmap.org/search"
    params = {
        "q": location,
        "format": "json",
        "limit": 1,  # only return the top result
        "addressdetails": 1,  # include detailed address info
    }

    headers = {"User-Agent": "example_weather/1.0 (daniel.chen@posit.co)"}
    response = requests.get(
        base_url,
        params=params,
        headers=headers,
    )

    data = response.json()

    lat = float(data[0]["lat"])
    lon = float(data[0]["lon"])

    return {"lat": lat, "lon": lon}


def get_weather(lat: float, lon: float):
    base_url = "https://api.open-meteo.com/v1/forecast"
    params = {
        "latitude": lat,
        "longitude": lon,
        "current_weather": True,
    }

    response = requests.get(
        base_url,
        params=params,
    )

    data = response.json()

    return {k: v for k, v in data.items()}


chat = ChatAnthropic(
    system_prompt=(
        "You are a helpful assistant that can check the weather. "
        "Report results in imperial units."
    ),
)

# chat.list_models()

chat.register_tool(get_weather)
chat.register_tool(get_coordinates)
chat.chat("What is the weather in Seattle?")

Demo: Shiny Application

import requests
from typing import Dict

from chatlas import ChatAnthropic
from dotenv import load_dotenv
from shiny.express import ui

load_dotenv(verbose=False)


def get_coordinates(location: str) -> Dict[str, float]:
    base_url: str = "https://nominatim.openstreetmap.org/search"
    params = {
        "q": location,
        "format": "json",
        "limit": 1,  # only return the top result
        "addressdetails": 1,  # include detailed address info
    }

    headers = {"User-Agent": "example_weather/1.0 (daniel.chen@posit.co)"}
    response = requests.get(
        base_url,
        params=params,
        headers=headers,
    )

    data = response.json()

    lat = float(data[0]["lat"])
    lon = float(data[0]["lon"])

    return {"lat": lat, "lon": lon}


def get_weather(lat: float, lon: float):
    base_url = "https://api.open-meteo.com/v1/forecast"
    params = {
        "latitude": lat,
        "longitude": lon,
        "current_weather": True,
    }

    response = requests.get(
        base_url,
        params=params,
    )

    data = response.json()

    return {k: v for k, v in data.items()}


chat_client = ChatAnthropic()

chat_client.register_tool(get_coordinates)
chat_client.register_tool(get_weather)

chat = ui.Chat(id="chat")
chat.ui(
    messages=[
        "Hello! I am a weather bot! Where would you like to get the weather form?"
    ]
)


@chat.on_user_submit
async def _(user_input: str):
    response = await chat_client.stream_async(user_input, content="all")
    await chat.append_message_stream(response)

You try: register a tool