Created
March 14, 2025 14:35
-
-
Save cheeyeo/9e21eb7f35ecad1f3b1f62a1680b5d0f to your computer and use it in GitHub Desktop.
Example of Gemini Function Calling
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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