starlet/tool

Tool definitions and dispatch helpers for function calling.

Defining Tools

import gleam/json
import starlet/tool

let weather_tool =
  tool.function(
    name: "get_weather",
    description: "Get current weather for a city",
    parameters: json.object([
      #("type", json.string("object")),
      #("properties", json.object([
        #("city", json.object([#("type", json.string("string"))])),
      ])),
    ]),
  )

Handling Tool Calls

let city_decoder = {
  use city <- decode.field("city", decode.string)
  decode.success(city)
}

let dispatcher = tool.dispatch([
  tool.handler("get_weather", city_decoder, fn(city) {
    let temp = case city {
      "Tokyo" -> 18
      _ -> 22
    }
    Ok(json.object([#("temp", json.int(temp))]))
  }),
])

Types

A tool invocation from the model’s response. The arguments field contains the parsed JSON arguments as a Dynamic value.

pub type Call {
  Call(id: String, name: String, arguments: dynamic.Dynamic)
}

Constructors

A tool the model can call. Currently only function tools are supported.

pub type Definition {
  Function(
    name: String,
    description: String,
    parameters: json.Json,
  )
}

Constructors

  • Function(
      name: String,
      description: String,
      parameters: json.Json,
    )

A function that handles a tool call and returns a result.

pub type Handler =
  fn(Call) -> Result(ToolResult, ToolError)

Errors that can occur during tool execution.

pub type ToolError {
  NotFound(name: String)
  InvalidArguments(message: String)
  ExecutionFailed(message: String)
}

Constructors

  • NotFound(name: String)
  • InvalidArguments(message: String)
  • ExecutionFailed(message: String)

The result of executing a tool call.

pub type ToolResult {
  ToolResult(id: String, name: String, output: json.Json)
}

Constructors

  • ToolResult(id: String, name: String, output: json.Json)

Values

pub fn dispatch(
  handlers: List(
    #(String, fn(Call) -> Result(ToolResult, ToolError)),
  ),
) -> fn(Call) -> Result(ToolResult, ToolError)

Create a dispatcher that routes calls to the right handler by name.

pub fn dynamic_handler(
  name: String,
  run: fn(dynamic.Dynamic) -> Result(json.Json, ToolError),
) -> #(String, fn(Call) -> Result(ToolResult, ToolError))

Create a handler tuple from name and a function that receives Dynamic arguments. For most cases, prefer handler which provides automatic argument decoding.

pub fn error(call: Call, message: String) -> ToolResult

Create an error result (encoded as JSON with error message).

pub fn function(
  name name: String,
  description description: String,
  parameters parameters: json.Json,
) -> Definition

Create a function tool definition.

pub fn handler(
  name: String,
  decoder: decode.Decoder(a),
  run: fn(a) -> Result(json.Json, ToolError),
) -> #(String, fn(Call) -> Result(ToolResult, ToolError))

Create a handler tuple that automatically decodes arguments to a typed value.

Example:

let decoder = {
  use city <- decode.field("city", decode.string)
  decode.success(city)
}

let #(name, run) =
  tool.handler("get_weather", decoder, fn(city) {
    Ok(json.string("Weather in " <> city))
  })
pub fn success(call: Call, output: json.Json) -> ToolResult

Create a successful tool result from a call.

pub fn to_string(call: Call) -> String

Formats a tool call for display, e.g. get_weather({"city":"Paris"}). Useful for logging and debugging.

Search Document