CactusCactus

Function Calling

Enable structured outputs and tool use with on-device language models

Defining Tools

Tools are defined as JSON schemas that describe available functions:

final tools = [
  {
    'name': 'get_weather',
    'description': 'Get weather for a location',
    'parameters': {
      'type': 'object',
      'properties': {
        'location': {'type': 'string', 'description': 'City name'}
      },
      'required': ['location']
    }
  }
];
val tools = listOf(
    mapOf(
        "name" to "get_weather",
        "description" to "Get weather for a location",
        "parameters" to mapOf(
            "type" to "object",
            "properties" to mapOf(
                "location" to mapOf("type" to "string", "description" to "City name")
            ),
            "required" to listOf("location")
        )
    )
)
const char* tools = R"([
    {
        "name": "get_weather",
        "description": "Get current weather for a location",
        "parameters": {
            "type": "object",
            "properties": {
                "location": {"type": "string"}
            },
            "required": ["location"]
        }
    }
])";

Calling with Tools

Pass tools to the completion call and parse the structured response:

final result = model.completeMessages(
  [Message.user("What's the weather in Paris?")],
  tools: tools,
);

if (result.functionCalls != null) {
  for (final call in result.functionCalls!) {
    print('Function: ${call['name']}');
    print('Arguments: ${call['arguments']}');
  }
}
val result = model.complete(
    messages = listOf(Message.user("What's the weather in Paris?")),
    tools = tools
)

result.functionCalls?.forEach { call ->
    println("Function: ${call["name"]}")
    println("Arguments: ${call["arguments"]}")
}
char response[4096];
cactus_complete(
    model, messages, response, sizeof(response),
    nullptr,  // use default options
    tools,    // pass tools JSON
    nullptr,
    nullptr
);

The response JSON includes parsed function calls:

{
    "success": true,
    "function_calls": [{
        "name": "get_weather",
        "arguments": {"location": "Paris"}
    }],
    "response": null,
    "confidence": 0.91
}

Tool Schema Reference

interface Tool {
  name: string;
  description: string;
  parameters: {
    type: 'object';
    properties: Record<string, {
      type: string;          // 'string', 'number', 'boolean', 'array', 'object'
      description?: string;
    }>;
    required?: string[];
  };
}

Tips

  • Keep descriptions clear — The model uses tool descriptions to decide which function to call
  • Use required fields — Mark parameters as required when they are always needed
  • Smaller models may struggle with complex multi-tool scenarios; use larger models for reliability
  • Cloud handoff — If the model confidence is low on a tool call, consider routing to a cloud API for better accuracy