Skip to content

Function Calling

Amayyas edited this page Jul 7, 2026 · 3 revisions

Function Calling (Tools)

Flutter AI SDK supports function/tool calling, allowing AI models to interact with external systems.

Overview

Tools let you define functions that the AI can call to perform actions or retrieve information. The AI decides when to use tools based on the conversation.

Defining Tools

Basic Tool

final weatherTool = Tool(
  name: 'get_weather',
  description: 'Get the current weather for a location',
  parameters: ToolParameters(
    properties: {
      'location': ToolProperty.string(
        description: 'The city and country, e.g., "Paris, France"',
      ),
      'unit': ToolProperty.enumeration(
        description: 'Temperature unit',
        values: ['celsius', 'fahrenheit'],
      ),
    },
    required: ['location'],
  ),
);

Property Types

// String
ToolProperty.string(
  description: 'A text value',
)

// Number
ToolProperty.number(
  description: 'A numeric value',
)

// Integer
ToolProperty.integer(
  description: 'An integer value',
)

// Boolean
ToolProperty.boolean(
  description: 'A true/false value',
)

// Enum
ToolProperty.enumeration(
  description: 'One of the allowed values',
  values: ['option1', 'option2', 'option3'],
)

// Array
ToolProperty.array(
  description: 'A list of items',
  items: ToolProperty.string(description: 'Each item'),
)

// Object
ToolProperty.object(
  description: 'A complex object',
  properties: {
    'name': ToolProperty.string(description: 'Name'),
    'age': ToolProperty.integer(description: 'Age'),
  },
  required: ['name'],
)

Using Tools

Step 1: Call with Tools

final response = await ai.chatWithTools(
  'What is the weather in Paris?',
  tools: [weatherTool],
);

Step 2: Check for Tool Calls

if (response.hasToolCalls) {
  for (final call in response.toolCalls!) {
    print('Tool: ${call.name}');
    print('Arguments: ${call.arguments}');
  }
}

Step 3: Execute and Submit Results

for (final call in response.toolCalls!) {
  // Execute the function
  final result = await executeToolCall(call);
  
  // Submit result back to AI
  final finalResponse = await ai.submitToolResult(
    toolCallId: call.id,
    name: call.name,
    result: result,
  );
  
  print('AI: ${finalResponse.text}');
}

Complete Example

// Define tools
final weatherTool = Tool(
  name: 'get_weather',
  description: 'Get weather for a location',
  parameters: ToolParameters(
    properties: {
      'location': ToolProperty.string(
        description: 'City name',
      ),
    },
    required: ['location'],
  ),
);

final calculatorTool = Tool(
  name: 'calculate',
  description: 'Perform calculations',
  parameters: ToolParameters(
    properties: {
      'expression': ToolProperty.string(
        description: 'Math expression like "2 + 2"',
      ),
    },
    required: ['expression'],
  ),
);

// Initialize AI
final ai = FlutterAI(
  provider: AIProvider.openai,
  config: AIConfig(
    apiKey: 'your-key',
    model: 'gpt-5.5',
  ),
);

// Function implementations
Future<Map<String, dynamic>> executeToolCall(ToolCallContent call) async {
  switch (call.name) {
    case 'get_weather':
      final location = call.arguments['location'] as String;
      // Call real weather API here
      return {
        'temperature': 22,
        'condition': 'sunny',
        'humidity': 45,
        'location': location,
      };
      
    case 'calculate':
      final expr = call.arguments['expression'] as String;
      // Evaluate expression here
      return {'result': evaluateExpression(expr)};
      
    default:
      return {'error': 'Unknown tool'};
  }
}

// Main flow
Future<void> main() async {
  final response = await ai.chatWithTools(
    'What is the weather in Paris and calculate 25 * 4',
    tools: [weatherTool, calculatorTool],
  );

  if (response.hasToolCalls) {
    for (final call in response.toolCalls!) {
      final result = await executeToolCall(call);
      
      final finalResponse = await ai.submitToolResult(
        toolCallId: call.id,
        name: call.name,
        result: result,
      );
      
      print(finalResponse.text);
    }
  } else {
    print(response.text);
  }
  
  ai.dispose();
}

Tool Choice

Control how the model uses tools:

// Let the model decide (default)
final config = AIConfig(
  apiKey: 'key',
  toolChoice: ToolChoice.auto,
);

// Force tool use
final config = AIConfig(
  apiKey: 'key',
  toolChoice: ToolChoice.required,
);

// Never use tools
final config = AIConfig(
  apiKey: 'key',
  toolChoice: ToolChoice.none,
);

// Force specific tool
final config = AIConfig(
  apiKey: 'key',
  toolChoice: ToolChoice.function('get_weather'),
);

Tool in Config

You can also set tools in the config:

final ai = FlutterAI(
  provider: AIProvider.openai,
  config: AIConfig(
    apiKey: 'your-key',
    tools: [weatherTool, calculatorTool],
    toolChoice: ToolChoice.auto,
  ),
);

// Now all chat calls can use these tools
final response = await ai.chat('What is the weather in London?');

ToolCallContent Properties

class ToolCallContent {
  final String id;           // Unique call ID
  final String name;         // Function name
  final Map<String, dynamic> arguments;  // Parsed arguments
}

Error Handling

Handle tool execution errors:

for (final call in response.toolCalls!) {
  try {
    final result = await executeToolCall(call);
    
    await ai.submitToolResult(
      toolCallId: call.id,
      name: call.name,
      result: result,
    );
  } catch (e) {
    // Report error back to AI
    await ai.submitToolResult(
      toolCallId: call.id,
      name: call.name,
      result: {'error': e.toString()},
      isError: true,
    );
  }
}

Streaming with Tools

await for (final chunk in ai.streamChatWithTools(
  'What is the weather?',
  tools: [weatherTool],
)) {
  if (chunk.type == StreamEventType.toolCallDelta) {
    print('Tool call: ${chunk.toolCallDelta}');
  }
  if (chunk.isDelta) {
    print(chunk.delta);
  }
}

Practical Examples

Web Search Tool

final searchTool = Tool(
  name: 'web_search',
  description: 'Search the web for current information',
  parameters: ToolParameters(
    properties: {
      'query': ToolProperty.string(
        description: 'Search query',
      ),
      'num_results': ToolProperty.integer(
        description: 'Number of results to return',
      ),
    },
    required: ['query'],
  ),
);

Database Query Tool

final dbTool = Tool(
  name: 'query_database',
  description: 'Query the user database',
  parameters: ToolParameters(
    properties: {
      'table': ToolProperty.enumeration(
        description: 'Table to query',
        values: ['users', 'orders', 'products'],
      ),
      'conditions': ToolProperty.object(
        description: 'Query conditions',
        properties: {
          'field': ToolProperty.string(description: 'Field name'),
          'operator': ToolProperty.enumeration(
            description: 'Comparison operator',
            values: ['=', '>', '<', '>=', '<=', 'like'],
          ),
          'value': ToolProperty.string(description: 'Value to compare'),
        },
        required: ['field', 'operator', 'value'],
      ),
    },
    required: ['table'],
  ),
);

Send Email Tool

final emailTool = Tool(
  name: 'send_email',
  description: 'Send an email',
  parameters: ToolParameters(
    properties: {
      'to': ToolProperty.string(description: 'Recipient email'),
      'subject': ToolProperty.string(description: 'Email subject'),
      'body': ToolProperty.string(description: 'Email body'),
    },
    required: ['to', 'subject', 'body'],
  ),
);

Best Practices

  1. Clear Descriptions: Write detailed descriptions for tools and parameters
  2. Validate Arguments: Always validate the arguments before executing
  3. Handle Errors: Return error information when tools fail
  4. Limit Tools: Only provide relevant tools for the task
  5. Use Enums: Use enumeration for limited choices

Provider Support

Provider Function Calling Parallel Calls
OpenAI
Anthropic
Google AI

Related


Tool Runner (automatic execution)

Instead of handling tool_calls manually, let ToolRunner drive the loop: it executes every requested tool, feeds results back to the model and stops when the model produces a final answer.

final runner = ToolRunner.create(
  provider: AIProvider.anthropic,
  config: AIConfig(apiKey: 'sk-ant-...'),
  tools: [
    ExecutableTool(
      definition: weatherTool,
      executor: (args) async => fetchWeather(args['location'] as String),
    ),
  ],
  maxIterations: 5,
);

final result = await runner.run('What is the weather in Paris?');
print(result.text);       // Final answer
print(result.iterations); // Tool rounds executed
print(result.messages);   // Full transcript

Behavior:

  • Tool calls within one model turn run in parallel
  • A failing executor is reported to the model as an error result
  • The loop throws ToolRunnerException after maxIterations rounds
  • onToolCall / onToolResult callbacks allow logging/observability

Clone this wiki locally