Much simplified function calling in Gemini 2.X models


Last year, in my Deep dive into function calling in Gemini post, I talked about how to do function calling in Gemini. More specifically, I showed how to call two functions (location_to_lat_long and lat_long_to_weather) to get the weather information for a location from Gemini. It wasn’t difficult but it involved a lot of steps for 2 simple function calls.

I’m pleased to see that the latest Gemini 2.X models and the unified Google Gen AI SDK (that I talked about in my Gemini on Vertex AI and Google AI now unified with the new Google Gen AI SDK) made function calling much simpler.

Let’s take a look.

Old way

As a recap, before Gemini 2.X models and the Google Gen AI SDK, you had to do the following steps to use function calling:

  1. Declare the functions available to the model.
  2. Create a tool of function declarations.
  3. Pass the tool to the model and generate content.
  4. Look for function call requests in the model responses.
  5. If a function call request is found, make the API call, get the response, and pass it back to the model.
  6. Get the final text response from the model.

The steps weren’t difficult but it involved quite a number of steps for something simple. You can see the full sample in main.py.

New way

In Gemini 2.X models and the Google Gen AI SDK, the function calling process got much simpler. All you need to do is to declare your functions with some comments and pass them directly to the model. No need for special function declarations, no need to look for function call requests, making API calls, and returning them back to the model. All is handled for you under the covers.

Let’s take a look how it works.

First, you define your functions with some explanation on what the function does:

def location_to_lat_long(location: str):
    """Given a location, returns the latitude and longitude

    Args:
        location: The location for which to get the weather.

    Returns:
        The latitude and longitude information in JSON.
    """
    logger.info(f"Calling location_to_lat_long({location})")
    url = f"https://geocoding-api.open-meteo.com/v1/search?name={location}&count=1"
    return api_request(url)


def lat_long_to_weather(latitude: str, longitude: str):
    """Given a latitude and longitude, returns the weather information

    Args:
        latitude: The latitude of a location
        longitude: The longitude of a location

    Returns:
        The weather information for the location in JSON.
    """
    logger.info(f"Calling lat_long_to_weather({latitude}, {longitude})")
    url = (f"https://api.open-meteo.com/v1/forecast?latitude={latitude}&longitude={longitude}&current=temperature_2m,"
           f"relative_humidity_2m,surface_pressure,wind_speed_10m,wind_direction_10m&forecast_days=1")
    return api_request(url)

Then, you create a GenAI client with the new SDK:

def generate_content_with_function_calls():
    client = genai.Client(
        vertexai=True,
        project=PROJECT_ID,
        location=LOCATION)

Pass the functions as tools while generating content:

    response = client.models.generate_content(
        model=MODEL_ID,
        contents=PROMPT,
        config=GenerateContentConfig(
            system_instruction=[
                "You are a helpful weather assistant.",
                "Your mission is to provide weather information for different cities."
                "Make sure your responses are in plain text format (no markdown) and include all the cities asked.",
            ],
            tools=[location_to_lat_long, lat_long_to_weather],
            temperature=0),

This will do all the heavy lifting of calling the right functions, getting the responses, and giving you a final response.

You can simply get the response like this. That’s it!

print(response.text)

You can look at a full sample in main_genaisdk.py.

Run the sample and you’ll get the text response:

python main_genaisdk.py

Here's the weather information for each city:

London: Temperature: 15.4°C, Wind: 9.2 km/h, Humidity: 42%
Paris: Temperature: 18.5°C, Wind: 8.4 km/h, Humidity: 23%
Tokyo: Temperature: 12.2°C, Wind: 4.2 km/h, Humidity: 68%

It seems magical but if you enable the debug logging by uncommenting this line:

logging.basicConfig(level=logging.DEBUG, format='%(message)s')

You’ll see that all the function calling requests and the API requests you had to do yourself before. Now all is handled automatically for you:

Calling location_to_lat_long(London)
Making a request to: https://geocoding-api.open-meteo.com/v1/search?name=London&count=1
Starting new HTTPS connection (1): geocoding-api.open-meteo.com:443
https://geocoding-api.open-meteo.com:443 "GET /v1/search?name=London&count=1 HTTP/11" 200 238
Response: {'results': [{'id': 2643743, 'name': 'London', 'latitude': 51.50853, 'longitude': -0.12574, 'elevation': 25.0, 'feature_code': 'PPLC', 'country_code': 'GB', 'admin1_id': 6269131, 'admin2_id': 2648110, 'timezone': 'Europe/London', 'population': 7556900, 'country_id': 2635167, 'country': 'United Kingdom', 'admin1': 'England', 'admin2': 'Greater London'}], 'generationtime_ms': 1.3620853}
Calling location_to_lat_long(Paris)
Making a request to: https://geocoding-api.open-meteo.com/v1/search?name=Paris&count=1
...

Nice, I like when things get simpler!


See also