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.