> ## 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 — fetching real-time data, performing calculations, or integrating with your existing systems.

<Info>
  **Model Performance:** Most chat models support function calling. **Kimi K3** is recommended for agentic workflows and complex tool calling scenarios.
</Info>

### Basic Example

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

<CodeGroup>
  ```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(())
  }
  ```
</CodeGroup>

### 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 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

<CardGroup cols={2}>
  <Card title="Model catalog" icon="list" href="/models/overview">
    View all available models and their capabilities.
  </Card>

  <Card title="Python SDK" icon="python" href="/sdk/python-sdk">
    Complete Python SDK documentation with more examples.
  </Card>
</CardGroup>
