Skip to content

Instantly share code, notes, and snippets.

@cheeyeo
Created March 14, 2025 14:35
Show Gist options
  • Select an option

  • Save cheeyeo/9e21eb7f35ecad1f3b1f62a1680b5d0f to your computer and use it in GitHub Desktop.

Select an option

Save cheeyeo/9e21eb7f35ecad1f3b1f62a1680b5d0f to your computer and use it in GitHub Desktop.

Revisions

  1. cheeyeo created this gist Mar 14, 2025.
    196 changes: 196 additions & 0 deletions testfunctioncalling.go
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,196 @@
    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)
    }
    }