Gemini Function Calling Example Code

Google’s Gemini AI models have revolutionized how developers interact with large language models through their powerful function calling capabilities. This feature allows Gemini to execute specific functions based on user input, creating dynamic and interactive applications that go far beyond simple text generation. In this comprehensive guide, we’ll explore practical Gemini function calling example code that you can implement in your own projects.

What is Gemini Function Calling?

Function calling in Gemini represents a significant advancement in AI-human interaction. Unlike traditional chatbots that only generate text responses, Gemini can understand when a user’s request requires executing a specific function and can call that function with appropriate parameters. This capability enables developers to create AI applications that can perform real-world tasks such as retrieving data from APIs, manipulating databases, sending emails, or controlling external systems.

The function calling mechanism works through a structured approach where you define available functions with their parameters and descriptions. Gemini analyzes user input, determines if any defined functions should be executed, and returns structured data indicating which function to call and with what parameters.

🔧 Function Calling Flow

User Input → Gemini Analysis → Function Selection → Parameter Extraction → Function Execution → Response

Setting Up Your Development Environment

Before diving into function calling examples, you need to set up your development environment properly. First, install the Google AI Python SDK:

pip install google-generativeai

Next, obtain your API key from Google AI Studio and configure your environment:

import google.generativeai as genai
import os

# Configure the API key
genai.configure(api_key=os.environ['GOOGLE_API_KEY'])

# Initialize the model
model = genai.GenerativeModel('gemini-pro')

Basic Function Calling Example

Let’s start with a simple weather function that demonstrates the core concepts of Gemini function calling:

import google.generativeai as genai

def get_weather(location: str, unit: str = "celsius") -> dict:
    """
    Get weather information for a specific location.
    
    Args:
        location (str): The city or location name
        unit (str): Temperature unit (celsius or fahrenheit)
    
    Returns:
        dict: Weather information
    """
    # Simulated weather data - in practice, you'd call a real weather API
    weather_data = {
        "location": location,
        "temperature": 22 if unit == "celsius" else 72,
        "unit": unit,
        "condition": "Sunny",
        "humidity": "65%"
    }
    return weather_data

# Define the function for Gemini
weather_function = genai.protos.FunctionDeclaration(
    name="get_weather",
    description="Get weather information for a specific location",
    parameters=genai.protos.Schema(
        type=genai.protos.Type.OBJECT,
        properties={
            "location": genai.protos.Schema(
                type=genai.protos.Type.STRING,
                description="The city or location name"
            ),
            "unit": genai.protos.Schema(
                type=genai.protos.Type.STRING,
                description="Temperature unit (celsius or fahrenheit)",
                enum=["celsius", "fahrenheit"]
            )
        },
        required=["location"]
    )
)

# Create a tool with the function
weather_tool = genai.protos.Tool(
    function_declarations=[weather_function]
)

# Initialize model with tools
model = genai.GenerativeModel('gemini-pro', tools=[weather_tool])

# Start a chat session
chat = model.start_chat()

# Send a message that should trigger the function
response = chat.send_message("What's the weather like in Tokyo?")

# Check if function calling is required
if response.candidates[0].content.parts[0].function_call:
    function_call = response.candidates[0].content.parts[0].function_call
    
    # Extract function name and arguments
    function_name = function_call.name
    function_args = dict(function_call.args)
    
    # Call the actual function
    if function_name == "get_weather":
        result = get_weather(**function_args)
        
        # Send the function result back to Gemini
        function_response = genai.protos.Part(
            function_response=genai.protos.FunctionResponse(
                name=function_name,
                response={"result": result}
            )
        )
        
        # Get final response with function result
        final_response = chat.send_message(function_response)
        print(final_response.text)

Advanced Multi-Function Example

Real-world applications often require multiple functions working together. Here’s a more complex example that demonstrates a customer service system with multiple capabilities:

import json
from datetime import datetime, timedelta

class CustomerService:
    def __init__(self):
        # Sample data - in practice, these would be database queries
        self.orders = {
            "ORD123": {
                "status": "shipped",
                "items": ["Laptop", "Mouse"],
                "total": 1299.99,
                "tracking": "TRK456789"
            },
            "ORD456": {
                "status": "processing",
                "items": ["Headphones"],
                "total": 199.99,
                "tracking": None
            }
        }
        
        self.support_tickets = []
    
    def check_order_status(self, order_id: str) -> dict:
        """Check the status of a customer order."""
        if order_id in self.orders:
            return self.orders[order_id]
        return {"error": "Order not found"}
    
    def create_support_ticket(self, customer_email: str, issue_description: str, priority: str = "medium") -> dict:
        """Create a new support ticket for a customer issue."""
        ticket_id = f"TICK{len(self.support_tickets) + 1:03d}"
        ticket = {
            "ticket_id": ticket_id,
            "email": customer_email,
            "description": issue_description,
            "priority": priority,
            "status": "open",
            "created_at": datetime.now().isoformat()
        }
        self.support_tickets.append(ticket)
        return ticket
    
    def calculate_refund(self, order_id: str, refund_percentage: float = 100.0) -> dict:
        """Calculate refund amount for an order."""
        if order_id in self.orders:
            order_total = self.orders[order_id]["total"]
            refund_amount = order_total * (refund_percentage / 100)
            return {
                "order_id": order_id,
                "original_amount": order_total,
                "refund_percentage": refund_percentage,
                "refund_amount": round(refund_amount, 2)
            }
        return {"error": "Order not found"}

# Initialize the customer service system
cs_system = CustomerService()

# Define all functions for Gemini
functions = [
    genai.protos.FunctionDeclaration(
        name="check_order_status",
        description="Check the current status of a customer order",
        parameters=genai.protos.Schema(
            type=genai.protos.Type.OBJECT,
            properties={
                "order_id": genai.protos.Schema(
                    type=genai.protos.Type.STRING,
                    description="The order ID to check"
                )
            },
            required=["order_id"]
        )
    ),
    genai.protos.FunctionDeclaration(
        name="create_support_ticket",
        description="Create a new customer support ticket",
        parameters=genai.protos.Schema(
            type=genai.protos.Type.OBJECT,
            properties={
                "customer_email": genai.protos.Schema(
                    type=genai.protos.Type.STRING,
                    description="Customer's email address"
                ),
                "issue_description": genai.protos.Schema(
                    type=genai.protos.Type.STRING,
                    description="Description of the customer's issue"
                ),
                "priority": genai.protos.Schema(
                    type=genai.protos.Type.STRING,
                    description="Priority level of the ticket",
                    enum=["low", "medium", "high", "urgent"]
                )
            },
            required=["customer_email", "issue_description"]
        )
    ),
    genai.protos.FunctionDeclaration(
        name="calculate_refund",
        description="Calculate refund amount for an order",
        parameters=genai.protos.Schema(
            type=genai.protos.Type.OBJECT,
            properties={
                "order_id": genai.protos.Schema(
                    type=genai.protos.Type.STRING,
                    description="The order ID for refund calculation"
                ),
                "refund_percentage": genai.protos.Schema(
                    type=genai.protos.Type.NUMBER,
                    description="Percentage of refund (default 100%)"
                )
            },
            required=["order_id"]
        )
    )
]

# Create the tool with all functions
customer_service_tool = genai.protos.Tool(function_declarations=functions)

# Initialize model with the customer service tools
model = genai.GenerativeModel('gemini-pro', tools=[customer_service_tool])

Handling Function Execution and Responses

The key to successful function calling lies in properly handling the execution flow and responses. Here’s a robust handler that manages the complete function calling lifecycle:

class FunctionCallHandler:
    def __init__(self, model, functions_map):
        self.model = model
        self.functions_map = functions_map
        self.chat = model.start_chat()
    
    def process_message(self, user_message: str) -> str:
        """Process a user message and handle any function calls."""
        response = self.chat.send_message(user_message)
        
        # Check if the response contains function calls
        if (response.candidates and 
            response.candidates[0].content.parts and 
            response.candidates[0].content.parts[0].function_call):
            
            return self._handle_function_call(response)
        else:
            return response.text
    
    def _handle_function_call(self, response):
        """Handle function call execution and return final response."""
        function_call = response.candidates[0].content.parts[0].function_call
        function_name = function_call.name
        function_args = dict(function_call.args)
        
        try:
            # Execute the function if it exists in our functions map
            if function_name in self.functions_map:
                result = self.functions_map[function_name](**function_args)
                
                # Create function response
                function_response = genai.protos.Part(
                    function_response=genai.protos.FunctionResponse(
                        name=function_name,
                        response={"result": result}
                    )
                )
                
                # Send function result back to get final response
                final_response = self.chat.send_message(function_response)
                return final_response.text
            else:
                return f"Function '{function_name}' not found"
                
        except Exception as e:
            # Handle function execution errors gracefully
            error_response = genai.protos.Part(
                function_response=genai.protos.FunctionResponse(
                    name=function_name,
                    response={"error": str(e)}
                )
            )
            error_final = self.chat.send_message(error_response)
            return error_final.text

# Example usage with the customer service system
functions_map = {
    "check_order_status": cs_system.check_order_status,
    "create_support_ticket": cs_system.create_support_ticket,
    "calculate_refund": cs_system.calculate_refund
}

handler = FunctionCallHandler(model, functions_map)

# Test the system
print(handler.process_message("Can you check the status of order ORD123?"))
print(handler.process_message("I need to create a support ticket for john@email.com about a defective laptop"))
print(handler.process_message("Calculate a 50% refund for order ORD456"))

Error Handling and Best Practices

Robust function calling implementations require comprehensive error handling and adherence to best practices:

Input Validation and Error Handling

def safe_function_executor(func, **kwargs):
    """Safely execute a function with proper error handling."""
    try:
        # Validate required parameters
        import inspect
        sig = inspect.signature(func)
        for param_name, param in sig.parameters.items():
            if param.default == inspect.Parameter.empty and param_name not in kwargs:
                raise ValueError(f"Missing required parameter: {param_name}")
        
        # Execute function
        result = func(**kwargs)
        
        # Ensure result is serializable
        json.dumps(result)
        return {"success": True, "data": result}
        
    except Exception as e:
        return {"success": False, "error": str(e), "error_type": type(e).__name__}

Function Definition Best Practices

When defining functions for Gemini, follow these essential practices:

• Clear Descriptions: Provide detailed, unambiguous descriptions for both functions and parameters • Proper Type Definitions: Use appropriate schema types that match your function signatures • Required vs Optional: Clearly mark which parameters are required and provide sensible defaults • Enum Values: Use enums for parameters with limited valid options • Error Responses: Design functions to return structured error information when things go wrong

💡 Pro Tip

Always test your function calling implementation with edge cases and invalid inputs. Gemini might call your functions with unexpected parameter combinations, so robust error handling is crucial for production applications.

Performance Optimization and Caching

For production applications, implementing caching and optimization strategies can significantly improve performance:

from functools import lru_cache
import time

class OptimizedFunctionHandler:
    def __init__(self):
        self.call_count = 0
        self.cache_hits = 0
    
    @lru_cache(maxsize=128)
    def cached_api_call(self, endpoint: str, params: str) -> dict:
        """Simulate an API call with caching."""
        self.call_count += 1
        time.sleep(0.1)  # Simulate API latency
        return {
            "endpoint": endpoint,
            "params": params,
            "timestamp": time.time(),
            "call_number": self.call_count
        }
    
    def get_cache_stats(self) -> dict:
        """Get caching statistics."""
        return {
            "total_calls": self.call_count,
            "cache_hits": self.cache_hits,
            "cache_hit_ratio": self.cache_hits / max(self.call_count, 1)
        }

Real-World Integration Example

Here’s a complete example showing how to integrate Gemini function calling into a Flask web application:

from flask import Flask, request, jsonify
import google.generativeai as genai

app = Flask(__name__)

class WebAppFunctionHandler:
    def __init__(self, api_key):
        genai.configure(api_key=api_key)
        self.setup_functions()
        
    def setup_functions(self):
        # Define your functions here
        self.functions = [
            # Function definitions as shown in previous examples
        ]
        
        self.tool = genai.protos.Tool(function_declarations=self.functions)
        self.model = genai.GenerativeModel('gemini-pro', tools=[self.tool])
    
    def handle_request(self, message):
        chat = self.model.start_chat()
        response = chat.send_message(message)
        
        # Process function calls if present
        if self._has_function_call(response):
            return self._execute_function_call(chat, response)
        return response.text

@app.route('/chat', methods=['POST'])
def chat_endpoint():
    user_message = request.json.get('message')
    handler = WebAppFunctionHandler(os.environ['GOOGLE_API_KEY'])
    response = handler.handle_request(user_message)
    return jsonify({"response": response})

if __name__ == '__main__':
    app.run(debug=True)

Function calling with Gemini opens up tremendous possibilities for creating intelligent, interactive applications. By following the patterns and best practices outlined in this guide, you can build robust systems that leverage AI to perform real-world tasks. Remember to always implement proper error handling, validate inputs, and test your functions thoroughly before deploying to production.

Conclusion

The examples provided here serve as a foundation that you can extend and customize for your specific use cases. Whether you’re building customer service bots, data analysis tools, or complex workflow automation systems, Gemini’s function calling capabilities provide the flexibility and power you need to create sophisticated AI-driven applications.

Leave a Comment