Tools & Function Calling

Use tools and function calling with the Sansa API

Overview

Tools let the model call functions you define. The model decides when to call a tool and writes the arguments; your code runs it and sends the result back.

Tool Definition Format

{
  "tools": [{
    "type": "function",
    "function": {
      // Descriptive name, snake_case recommended.
      "name": "get_weather",
      
      // Detailed description helps the model decide when to use it.
      "description": "Get current weather for a location",
      
      // JSON Schema object.
      "parameters": {
        "type": "object",
        "properties": {
          "location": {
            "type": "string",
            "description": "City name or coordinates"
          },
          "units": {
            "type": "string",
            "enum": ["celsius", "fahrenheit"],
            "description": "Temperature unit"
          }
        },
        "required": ["location"]
      }
    }
  }]
}

Three-Step Flow

Step 1: Send request with tools

Include tools in your request. The model may respond with text or with tool_calls.

Step 2: Model responds with tool_calls

When the model calls a tool, content is null and tool_calls holds the calls, each with an id you send back in Step 3:

{
  "choices": [{
    "message": {
      "role": "assistant",
      "content": null,
      "tool_calls": [{
        "id": "call12345",
        "type": "function",
        "function": {
          "name": "get_weather",
          "arguments": "{\"location\": \"Tokyo\", \"units\": \"celsius\"}"
        }
      }]
    },
    "finish_reason": "tool_calls"
  }]
}

Step 3: Send tool result back

Append the assistant message from Step 2 and a tool message with the result. Pass back the exact id; you don't need to worry about its format. Include tools in every request, not just the first.

{
  "messages": [
    {"role": "user", "content": "What's the weather in Tokyo?"},
    {
      "role": "assistant",
      "content": null,
      "tool_calls": [{
        "id": "call12345",
        "type": "function",
        "function": {
          "name": "get_weather",
          "arguments": "{\"location\": \"Tokyo\", \"units\": \"celsius\"}"
        }
      }]
    },
    {
      "role": "tool",
      "tool_call_id": "call12345",  // Same ID as the assistant's tool call
      "content": "{\"temperature\": 22, \"conditions\": \"sunny\"}"
    }
  ],
  "tools": [/* same tool definitions */]
}

tool_choice

See tool_choice for the values.

parallel_tool_calls

Default: true. When false, the model makes one tool call at a time. Match each result to its call by tool_call_id.

Streaming with Tool Calls

  • delta.tool_calls[].id and delta.tool_calls[].function.name arrive first.
  • delta.tool_calls[].function.arguments streams as partial JSON fragments.
  • Client must accumulate argument fragments, then JSON.parse the complete string.
  • finish_reason: "tool_calls" signals all tool calls are complete.

Best Practices

  • Use descriptive function names and detailed descriptions.
  • Include property descriptions in the JSON Schema.
  • Design tools that compose well (search → get details → take action).
  • Set a max iteration limit in agentic loops.
  • Handle errors in tool execution gracefully (return error info as tool content).