Tutorial: Function calling with the Gemini API


Function calling makes it easier for you to get structured data outputs from generative models. You can then use these outputs to call other APIs and return the relevant response data to the model. In other words, function calling helps you connect generative models to external systems so that the generated content includes the most up-to-date and accurate information.

You can provide Gemini models with descriptions of functions. These are functions that you write in the language of your app (that is, they're not Google Cloud Functions). The model may ask you to call a function and send back the result to help the model handle your query.

If you haven't already, check out the Introduction to function calling to learn more.

Set up your project

Before calling the Gemini API, you need to set up your project, which includes setting up your API key, adding the SDK to your pub dependencies, and initializing the model.

Set up a function call

For this tutorial, you'll have the model interact with a hypothetical currency exchange API that supports the following parameters:

Parameter Type Required Description
currencyDate string yes Date to fetch the exchange rate for
(which must always be in YYYY-MM-DD format or the value latest if a time period is not specified)
currencyFrom string yes Currency to convert from
currencyTo string no Currency to convert to

Example API request

{
  "currencyDate": "2024-04-17",
  "currencyFrom": "USD",
  "currencyTo": "SEK"
}

Example API response

{
  "base": "USD",
  "date": "2024-04-17",
  "rates": {"SEK": 0.091}
}

Step 1: Create the function that makes the API request

If you haven't already, start by creating the function that makes an API request.

For demonstration purposes in this tutorial, rather than sending an actual API request, you'll be returning hardcoded values in the same format that an actual API would return.

Future<Map<String, Object?>> findExchangeRate(
  Map<String, Object?> arguments,
) async =>
    // This hypothetical API returns a JSON such as:
    // {"base":"USD","date":"2024-04-17","rates":{"SEK": 0.091}}
    {
      'date': arguments['currencyDate'],
      'base': arguments['currencyFrom'],
      'rates': <String, Object?>{arguments['currencyTo'] as String: 0.091}
    };

Step 2: Create a function declaration

Create the function declaration that you'll pass to the generative model (next step of this tutorial).

Include as much detail as possible in the function and parameter descriptions. The generative model uses this information to determine which function to select and how to provide values for the parameters in the function call.

final exchangeRateTool = FunctionDeclaration(
    'findExchangeRate',
    'Returns the exchange rate between currencies on given date.',
    Schema(SchemaType.object, properties: {
      'currencyDate': Schema(SchemaType.string,
          description: 'A date in YYYY-MM-DD format or '
              'the exact value "latest" if a time period is not specified.'),
      'currencyFrom': Schema(SchemaType.string,
          description: 'The currency code of the currency to convert from, '
              'such as "USD".'),
      'currencyTo': Schema(SchemaType.string,
          description: 'The currency code of the currency to convert to, '
              'such as "USD".')
    }, requiredProperties: [
      'currencyDate',
      'currencyFrom'
    ]));

Step 3: Specify the function declaration during model initialization

Specify the function declaration when initializing the generative model by passing it into the model's tools parameter:

final model = GenerativeModel(
  // Use a model that supports function calling, like a Gemini 1.5 model
  model: 'gemini-1.5-flash',
  apiKey: apiKey,

  // Specify the function declaration.
  tools: [
    Tool(functionDeclarations: [exchangeRateTool])
  ],
);

Step 4: Generate a function call

Now you can prompt the model with the defined function.

The recommended way to use function calling is through the chat interface, since function calls fit nicely into chat's multi-turn structure.

final chat = model.startChat();
final prompt = 'How much is 50 US dollars worth in Swedish krona?';

// Send the message to the generative model.
var response = await chat.sendMessage(Content.text(prompt));

final functionCalls = response.functionCalls.toList();
// When the model response with a function call, invoke the function.
if (functionCalls.isNotEmpty) {
  final functionCall = functionCalls.first;
  final result = switch (functionCall.name) {
    // Forward arguments to the hypothetical API.
    'findExchangeRate' => await findExchangeRate(functionCall.args),
    // Throw an exception if the model attempted to call a function that was
    // not declared.
    _ => throw UnimplementedError(
        'Function not implemented: ${functionCall.name}')
  };
  // Send the response to the model so that it can use the result to generate
  // text for the user.
  response = await chat
      .sendMessage(Content.functionResponse(functionCall.name, result));
}
// When the model responds with non-null text content, print it.
if (response.text case final text?) {
  print(text);
}