Skip to content

Instantly share code, notes, and snippets.

@cheeyeo
Created March 14, 2025 14:35
Show Gist options
  • Save cheeyeo/9e21eb7f35ecad1f3b1f62a1680b5d0f to your computer and use it in GitHub Desktop.
Save cheeyeo/9e21eb7f35ecad1f3b1f62a1680b5d0f to your computer and use it in GitHub Desktop.
Example of Gemini Function Calling
package main
import (
"context"
"fmt"
"log"
"os"
"github.com/google/generative-ai-go/genai"
"github.com/hectormalot/omgo"
"google.golang.org/api/option"
"googlemaps.github.io/maps"
)
func getGeoCode(address string) (float64, float64, error) {
gMapClient, err := maps.NewClient(maps.WithAPIKey(os.Getenv("GOOGLE_MAPS_API")))
if err != nil {
return float64(0), float64(0), err
}
r := &maps.GeocodingRequest{
Address: address,
}
resp, err := gMapClient.Geocode(context.Background(), r)
if err != nil {
return float64(0), float64(0), err
}
return resp[0].Geometry.Location.Lat, resp[0].Geometry.Location.Lng, nil
}
func getCurrentTemperature(lat float64, lng float64) (float64, error) {
c, err := omgo.NewClient()
if err != nil {
return float64(0), err
}
loc, err := omgo.NewLocation(lat, lng)
if err != nil {
return float64(0), err
}
res, err := c.CurrentWeather(context.Background(), loc, nil)
if err != nil {
return float64(0), err
}
return res.Temperature, nil
}
func main() {
address := "Jurong Town, Singapore"
// Start Gemini API function definition
geocodeTool := &genai.Tool{
FunctionDeclarations: []*genai.FunctionDeclaration{{
Name: "getGeoCode",
Description: "Gets the latitude and longitude for a given address. Returns a location value of latitude and longitude.",
Parameters: &genai.Schema{
Type: genai.TypeObject,
Properties: map[string]*genai.Schema{
"address": {
Type: 1,
Description: "address to geocode",
},
},
Required: []string{"address"},
},
}},
}
temperatureTool := &genai.Tool{
FunctionDeclarations: []*genai.FunctionDeclaration{{
Name: "getCurrentTemperature",
Description: "Gets the current weather from the Open-Meteo API with given latitude and longitude parameters.",
Parameters: &genai.Schema{
Type: genai.TypeObject,
Properties: map[string]*genai.Schema{
"lat": {
Type: 2,
Description: "latitude of location",
},
"lng": {
Type: 2,
Description: "longitude of location",
},
},
Required: []string{"lat", "lng"},
},
}},
}
ctx := context.Background()
client, err := genai.NewClient(ctx, option.WithAPIKey(os.Getenv("API_KEY")))
if err != nil {
log.Fatal(err)
}
defer client.Close()
// Use a model that supports function calling, like a Gemini 2.0 model
model := client.GenerativeModel("gemini-2.0-flash")
// Set the temperature to 0 to reduce hallucination
model.SetTemperature(0.0)
// Specify the function declaration.
model.Tools = []*genai.Tool{geocodeTool, temperatureTool}
// Start new chat session.
session := model.StartChat()
prompt := fmt.Sprintf(`
You are a helpful assistant that use tools to access and retrieve information from a weather API with a given address.
Only use the following set of tools available to you:
- getGeoCode
- getCurrentTemperature
Use the given address to get the geolocation first. Use the geolocation to get the current temperature.
For each function call, show the parameters you use.
The address to get the weather for is %s.`, address)
// Send the message to the generative model.
resp, err := session.SendMessage(ctx, genai.Text(prompt))
if err != nil {
log.Fatalf("Error sending message: %v\n", err)
}
// Check that you got the expected function call back.
part := resp.Candidates[0].Content.Parts[0]
funcall, ok := part.(genai.FunctionCall)
if !ok {
log.Fatalf("Expected type FunctionCall, got %T", part)
}
if g, e := funcall.Name, geocodeTool.FunctionDeclarations[0].Name; g != e {
log.Fatalf("Expected FunctionCall.Name %q, got %q", e, g)
}
fmt.Printf("Received function call response:\n%v\n\n", part)
// Calls the getGeoCode function here
// In real world usage, we would have some validation / checks here...
lat, lng, err := getGeoCode(address)
if err != nil {
log.Fatalf("Geocode err: %w", err)
}
log.Printf("LOCATION: %f %f\n", lat, lng)
apiResult := map[string]any{
"lat": lat,
"lng": lng,
}
fmt.Printf("Sending API result:\n%v\n\n", apiResult)
resp, err = session.SendMessage(ctx, genai.FunctionResponse{
Name: geocodeTool.FunctionDeclarations[0].Name,
Response: apiResult,
})
if err != nil {
log.Fatalf("Error sending message: %v\n", err)
}
// Show the model's response, which is to be the next expected function call
part = resp.Candidates[0].Content.Parts[0]
funcall, ok = part.(genai.FunctionCall)
if !ok {
log.Fatalf("Expected type FunctionCall, got %T", part)
}
if g, e := funcall.Name, temperatureTool.FunctionDeclarations[0].Name; g != e {
log.Fatalf("Expected FunctionCall.Name %q, got %q", e, g)
}
fmt.Printf("Received function call response:\n%q\n\n", part)
// Calls getCurrentTemperature here
currentTemp, err := getCurrentTemperature(lat, lng)
if err != nil {
log.Fatalf("error with calling getCurrentTemperature - ", err)
}
apiResult = map[string]any{
"temperature": currentTemp,
}
fmt.Printf("Sending 2nd API result:\n%v\n\n", apiResult)
resp, err = session.SendMessage(ctx, genai.FunctionResponse{
Name: temperatureTool.FunctionDeclarations[0].Name,
Response: apiResult,
})
if err != nil {
log.Fatalf("Error sending message: %v\n", err)
}
// The task would have ended here. The response should only be text
// Show the model's response, which is expected to be text.
for _, part := range resp.Candidates[0].Content.Parts {
fmt.Printf("%v\n", part)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment