> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tinfoil.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# Tool calling

> Learn how to use function calling capabilities with Tinfoil's AI models for dynamic integrations.

## Function Calling

Function calling (also known as tool calling) lets AI models invoke external tools and APIs to fetch real-time data, perform calculations, or integrate with your existing systems.

> [!NOTE]
>
> **Model Performance:** Most chat models support function calling. **Kimi K3.1 Pro** is recommended for agentic workflows and complex tool calling scenarios.

### Basic Example

Here's a simple example of how to implement function calling with a weather API:

```python Python theme={"dark"}
from tinfoil import TinfoilAI
import json

# Initialize the client
client = TinfoilAI(
    api_key="<YOUR_API_KEY>"
)

# Define the tool/function
tools = [
    {
        "type": "function",
        "function": {
            "name": "get_weather",
            "description": "Get current weather for a specific location",
            "parameters": {
                "type": "object",
                "properties": {
                    "location": {
                        "type": "string",
                        "description": "The city and state, e.g. San Francisco, CA"
                    }
                },
                "required": ["location"]
            }
        }
    }
]

# Mock weather function (replace with real API call)
def get_weather(location):
    return f"The weather in {location} is sunny, 22°C"

# Make the initial request
response = client.chat.completions.create(
    model="<MODEL_NAME>",
    messages=[
        {"role": "user", "content": "What's the weather like in New York?"}
    ],
    tools=tools,
    tool_choice="auto"
)

# Check if the model wants to call a function
message = response.choices[0].message
if message.tool_calls:
    # Process each tool call
    for tool_call in message.tool_calls:
        if tool_call.function.name == "get_weather":
            # Parse function arguments
            args = json.loads(tool_call.function.arguments)
            location = args["location"]
            
            # Call the function
            weather_result = get_weather(location)
            
            # Send the function result back to the model
            messages = [
                {"role": "user", "content": "What's the weather like in New York?"},
                message,  # Assistant's message with tool call
                {
                    "role": "tool",
                    "content": weather_result,
                    "tool_call_id": tool_call.id
                }
            ]
            
            # Get the final response
            final_response = client.chat.completions.create(
                model="<MODEL_NAME>",
                messages=messages,
                tools=tools,
                tool_choice="auto"
            )
            
            print(final_response.choices[0].message.content)
else:
    print(message.content)
```

```typescript JavaScript theme={"dark"}
import { TinfoilAI } from 'tinfoil';

// Initialize the client
const client = new TinfoilAI({
  apiKey: process.env.TINFOIL_API_KEY
});

// Define the tool
const tools = [
  {
    type: 'function' as const,
    function: {
      name: 'get_weather',
      description: 'Get current weather for a specific location',
      parameters: {
        type: 'object',
        properties: {
          location: {
            type: 'string',
            description: 'The city and state, e.g. San Francisco, CA'
          }
        },
        required: ['location']
      }
    }
  }
];

// Mock weather function
function getWeather(location: string): string {
  return `The weather in ${location} is sunny, 22°C`;
}

async function main() {
  // Make initial request
  const response = await client.chat.completions.create({
    model: '<MODEL_NAME>',
    messages: [
      { role: 'user', content: "What's the weather like in New York?" }
    ],
    tools
  });

  const message = response.choices[0].message;
  
  if (message.tool_calls && message.tool_calls.length > 0) {
    // Process tool calls
    const toolResults = [];
    
    for (const toolCall of message.tool_calls) {
      if (toolCall.function.name === 'get_weather') {
        const args = JSON.parse(toolCall.function.arguments);
        const weatherResult = getWeather(args.location);
        
        toolResults.push({
          role: 'tool' as const,
          content: weatherResult,
          tool_call_id: toolCall.id
        });
      }
    }
    
    // Get final response
    const finalResponse = await client.chat.completions.create({
      model: '<MODEL_NAME>',
      messages: [
        { role: 'user', content: "What's the weather like in New York?" },
        message,
        ...toolResults
      ],
      tools
    });
    
    console.log(finalResponse.choices[0].message.content);
  } else {
    console.log(message.content);
  }
}

main().catch(console.error);
```

```go Go theme={"dark"}
package main

import (
    "context"
    "encoding/json"
    "fmt"
    "os"

    "github.com/openai/openai-go/v3"
    "github.com/openai/openai-go/v3/option"
    "github.com/openai/openai-go/v3/shared"
    "github.com/tinfoilsh/tinfoil-go"
)

func main() {
    // Create a secure Tinfoil client
    client, err := tinfoil.NewClient(
        option.WithAPIKey(os.Getenv("TINFOIL_API_KEY")),
    )
    if err != nil {
        panic(err.Error())
    }

    ctx := context.Background()

    question := "What is the weather in New York City?"

    params := openai.ChatCompletionNewParams{
        Messages: []openai.ChatCompletionMessageParamUnion{
            openai.UserMessage(question),
        },
        Tools: []openai.ChatCompletionToolUnionParam{
            openai.ChatCompletionFunctionTool(shared.FunctionDefinitionParam{
                Name:        "get_weather",
                Description: openai.String("Get weather at the given location"),
                Parameters: shared.FunctionParameters{
                    "type": "object",
                    "properties": map[string]interface{}{
                        "location": map[string]string{
                            "type": "string",
                        },
                    },
                    "required": []string{"location"},
                },
            }),
        },
        Model: "<MODEL_NAME>",
    }

    // Make initial chat completion request
    completion, err := client.Chat.Completions.New(ctx, params)
    if err != nil {
        panic(err)
    }

    toolCalls := completion.Choices[0].Message.ToolCalls

    // Return early if there are no tool calls
    if len(toolCalls) == 0 {
        fmt.Printf("No function call")
        return
    }

    // If there was a function call, continue the conversation
    params.Messages = append(params.Messages, completion.Choices[0].Message.ToParam())
    for _, toolCall := range toolCalls {
        if toolCall.Function.Name == "get_weather" {
            // Extract the location from the function call arguments
            var args map[string]interface{}
            err := json.Unmarshal([]byte(toolCall.Function.Arguments), &args)
            if err != nil {
                panic(err)
            }
            location := args["location"].(string)

            // Simulate getting weather data
            weatherData := getWeather(location)

            params.Messages = append(params.Messages, openai.ToolMessage(weatherData, toolCall.ID))
        }
    }

    completion, err = client.Chat.Completions.New(ctx, params)
    if err != nil {
        panic(err)
    }

    fmt.Println(completion.Choices[0].Message.Content)
}

// Mock function to simulate weather data retrieval
func getWeather(location string) string {
    // In a real implementation, this function would call a weather API
    return "Sunny, 25°C"
}
```

```swift Swift theme={"dark"}
import Foundation
import TinfoilAI
import OpenAI

// This example uses top-level await syntax
// Wrap in an async function or use in an async context as needed

let client = try await TinfoilAI.create(
    apiKey: ProcessInfo.processInfo.environment["TINFOIL_API_KEY"] ?? ""
)

let weatherSchema = JSONSchema(
    .type(.object),
    .properties([
        "location": JSONSchema(
            .type(.string),
            .description("The city and state, e.g. San Francisco, CA")
        )
    ]),
    .required(["location"])
)

let tools = [
    ChatQuery.ChatCompletionToolParam(
        function: .init(
            name: "get_weather",
            description: "Get current weather for a specific location",
            parameters: weatherSchema
        )
    )
]

func getWeather(location: String) -> String {
    return "The weather in \(location) is sunny, 22°C"
}

let chatQuery = ChatQuery(
    messages: [
        .user(.init(content: .string("What's the weather like in New York?")))
    ],
    model: "<MODEL_NAME>",
    tools: tools
)

let response = try await client.chats(query: chatQuery)

if let toolCalls = response.choices.first?.message.toolCalls, !toolCalls.isEmpty {
    if let firstToolCall = toolCalls.first,
       let argsData = firstToolCall.function.arguments.data(using: String.Encoding.utf8),
       let args = try? JSONSerialization.jsonObject(with: argsData) as? [String: Any],
       let location = args["location"] as? String {

        let weatherResult = getWeather(location: location)

        let followUpQuery = ChatQuery(
            messages: [
                .user(.init(content: .string("What's the weather like in New York?"))),
                .assistant(.init(
                    content: nil,
                    toolCalls: response.choices.first!.message.toolCalls?.map {
                        ChatQuery.ChatCompletionMessageParam.AssistantMessageParam.ToolCallParam(
                            id: $0.id,
                            function: ChatQuery.ChatCompletionMessageParam.AssistantMessageParam.ToolCallParam.FunctionCall(
                                arguments: $0.function.arguments,
                                name: $0.function.name
                            )
                        )
                    }
                )),
                .tool(.init(content: .textContent(weatherResult), toolCallId: firstToolCall.id))
            ],
            model: "<MODEL_NAME>",
            tools: tools
        )

        let finalResponse = try await client.chats(query: followUpQuery)
        print(finalResponse.choices.first?.message.content ?? "")
    }
}
```

```rust Rust theme={"dark"}
use serde_json::{json, Value};
use tinfoil::Client;

fn get_weather(location: &str) -> String {
    format!("The weather in {} is sunny, 22°C", location)
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = Client::new_default().await?;

    let tools = json!([{
        "type": "function",
        "function": {
            "name": "get_weather",
            "description": "Get current weather for a specific location",
            "parameters": {
                "type": "object",
                "properties": {
                    "location": { "type": "string", "description": "The city and state, e.g. San Francisco, CA" }
                },
                "required": ["location"]
            }
        }
    }]);

    let mut messages: Vec<Value> = vec![
        json!({"role": "user", "content": "What's the weather like in New York?"}),
    ];

    let body = client.chat_relaxed().request()
        .model("<MODEL_NAME>")
        .messages(messages.clone())
        .set("tools", tools.clone());

    let response = client.chat_relaxed().create(body).await?;

    let tool_calls = response.typed_tool_calls();
    if tool_calls.is_empty() {
        println!("{}", response.content().unwrap_or(""));
        return Ok(());
    }

    // Append the assistant turn (raw, includes tool_calls verbatim).
    if let Some(assistant) = response.raw().pointer("/choices/0/message") {
        messages.push(assistant.clone());
    }

    // Run each tool and append its result.
    for call in &tool_calls {
        if call.function_name.as_deref() == Some("get_weather") {
            let args: Value = serde_json::from_str(&call.arguments_raw)?;
            let location = args["location"].as_str().unwrap_or("");
            let result = get_weather(location);
            messages.push(json!({
                "role": "tool",
                "content": result,
                "tool_call_id": call.id.as_deref().unwrap_or(""),
            }));
        }
    }

    let body = client.chat_relaxed().request()
        .model("<MODEL_NAME>")
        .messages(messages)
        .set("tools", tools);

    let final_response = client.chat_relaxed().create(body).await?;
    println!("{}", final_response.content().unwrap_or(""));
    Ok(())
}
```

### Multiple Tools Example

You can define multiple tools for more complex workflows:

```python theme={"dark"}
tools = [
    {
        "type": "function",
        "function": {
            "name": "get_weather",
            "description": "Get current weather for a location",
            "parameters": {
                "type": "object",
                "properties": {
                    "location": {"type": "string"}
                },
                "required": ["location"]
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "calculate",
            "description": "Perform mathematical calculations",
            "parameters": {
                "type": "object",
                "properties": {
                    "expression": {
                        "type": "string",
                        "description": "Mathematical expression to evaluate"
                    }
                },
                "required": ["expression"]
            }
        }
    }
]

def calculate(expression):
    # Safe evaluation of mathematical expressions
    try:
        result = eval(expression)
        return str(result)
    except:
        return "Error: Invalid mathematical expression"

# The model can now choose between weather and calculation functions
response = client.chat.completions.create(
    model="<MODEL_NAME>",
    messages=[
        {"role": "user", "content": "What's 15 * 23 + 45?"}
    ],
    tools=tools,
    tool_choice="auto"
)
```

### Best Practices

1. **Choose the Right Model**: Among the models offered on Tinfoil API, Kimi K3.1 Pro is recommended for function calling and agentic workflows
2. **Clear Descriptions**: Write detailed function descriptions to help the model understand when to use each tool
3. **Parameter Validation**: Always validate function parameters before execution
4. **Error Handling**: Implement proper error handling for function calls
5. **Security**: Never execute untrusted code - validate all function arguments
6. **Testing**: Test your functions independently before integrating with the AI model

> [card] [Model catalog](https://docs.tinfoil.sh/models/overview.md)
>
> View all available models and their capabilities.

> [card] [Python SDK](https://docs.tinfoil.sh/sdk/python-sdk.md)
>
> Complete Python SDK documentation with more examples.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.