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.Model Performance: Most chat models support function calling. Kimi K3 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: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)
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);
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"
}
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 ?? "")
}
}
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: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
- Choose the Right Model: Among the models offered on Tinfoil API, Kimi K3 is recommended for function calling and agentic workflows
- Clear Descriptions: Write detailed function descriptions to help the model understand when to use each tool
- Parameter Validation: Always validate function parameters before execution
- Error Handling: Implement proper error handling for function calls
- Security: Never execute untrusted code - validate all function arguments
- Testing: Test your functions independently before integrating with the AI model
Model catalog
View all available models and their capabilities.
Python SDK
Complete Python SDK documentation with more examples.

