Problems with Gemini in Go

It seems that no matter how hard I try to program in Go, I have yet to actually make any Go script of mine work. I try to use it, everybody talks about its performance and whatnot, but I am always spending way more time debugging simple Go code than actually writing anything and making progress.

Anyways, I wanted to make a simple Go script to dump in a serverless architecture. Right now though, it’s just running on my computer. It’s pretty basic, almost straight from the docs:

func main() {
	ctx := context.Background()
	// Access your API key as an environment variable (see "Set up your API key" above)
	client, err := genai.NewClient(ctx, option.WithAPIKey(os.Getenv("GEMINI_API_KEY")))
	if err != nil {
		log.Fatal(err)
	}
	defer client.Close()

	// For text-only input, use the gemini-pro model
	model := client.GenerativeModel("gemini-pro")
	resp, err := model.GenerateContent(ctx, genai.Text("Write a story about a magic backpack."))
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(*resp)
}

It should return and print Gemini’s response. Instead, it returns this:

{[0xc0004eea00] <nil>}

I cannot understand why, nor if this is a me problem or an API problem.
If I print resp and not *resp, it just gives me the memory address of the exact thing above.

What’s interesting is that the code does wait for Gemini to run and complete the response. When it does, this is what it spits out.

I have also tried using the streaming version of it, and it does the same thing, but it’ll give 3-4 nil values. So, it’s gotta be something to do with the way it’s stored, handled, or passed.

I don’t have issues calling Gemini in languages like js with the API. This is specific to Go.

Anyone know what’s going on and/or what I did here?

This doesn’t have to do with Gemini’s API but with another API that can integrate with Gemini’s, but I spent hours debugging, doing resp, etc., until I finally decided to try to send a curl request with my exact same arguments where I got the message: “Request must be five characters.” :man_facepalming: The most difficult part was that I didn’t think of the prompt as a variable, so I just continued giving different requests which meant that sometimes it worked and sometimes it didn’t, which made it even more confusing.

2 Likes

Huh, interesting. What API? I figured it was something I did, I just wanted to double check.

So, how did you resolve it then? Cause even by trying to make the prompt a variable like so:

model := client.GenerativeModel("gemini-pro")
prompt := genai.Text("Write a story about a magic backpack.")
resp, err := model.GenerateContent(ctx, prompt)

Doesn’t fix it.
I clearly do not understand Go well enough to use it lol. This is what I get for telling myself “It’ll be fine! Go code can be short and sweet! I’ll get something to work this time! I can write a little bit of code and get back to the important parts.”

1 Like

Tavily, it has a 5-character limit for each prompt so Dog and Cat weren’t working.

I have never coded with Go before but it seems like a very interesting language.

You might as well just throw your script at gemini.google.com/app and see how it goes :laughing:

I changed dog to doggy.

1 Like

Fair :rofl:.

Welp, since something is clearly interfering with it, and I have no clue what, I am just gonna leave it be for now. If others come up with similar issues I can circle back.

Interesting is…an apt description for it :laughing:. It’s a good programming language when it works. For some reason though, my brain just does not click with it very well. It’s very antithetical to how I personally think about code. And I think a large part of that has to do with this snippet (from wikipedia):

Its designers were primarily motivated by their shared dislike of C++.

C++ has got plenty of problems, but for some reason the structure of C++ just makes more sense to me. This is why I stick with Rust lol.

EDIT:

I forgot to mention, the Gemini Code Assist in VS Code is quite nice. All it’s missing is a “fix this” button like Copilot and then it’s golden. I’m already starting to prefer the answers from Gemini over Copilot.

Even if it isn’t right, it’s much better at steering you in the right direction. It also is better at handling larger volumes of code.

Impressive as CoPilot is the BEST coding assistant I have ever used, and I have used quite a few, I might have to check this out.

Try changing the print statement to fmt.Println(*resp.Candidates[0].Content) or fmt.Println(resp.Candidates[0].Content.Parts[0]) if you want just the text output.

2 Likes

func usage() {
fmt.Fprintf(flag.CommandLine.Output(), “usage: web [options]\n”)
flag.PrintDefaults()
os.Exit(2)
}

var (
addr = flag.String(“addr”, “localhost:8080”, “address to serve”)
)

func generateHandler(w http.ResponseWriter, r *http.Request, model *genai.GenerativeModel) {
if apiKey == “TODO” {
http.Error(w, “Error: To get started, get an API key at https://makersuite.google.com/app/apikey and enter it in cmd/web/main.go and then hard restart the preview”, http.StatusInternalServerError)
return
}

image, prompt := r.FormValue("chosen-image"), r.FormValue("prompt")
contents, err := os.ReadFile(filepath.Join("static", "images", filepath.Base(image)))
if err != nil {
	log.Printf("Unable to read image %s: %v\n", image, err)
	http.Error(w, "Error: unable to generate content", http.StatusInternalServerError)
	return
}

// Generate the response and aggregate the streamed response.
iter := model.GenerateContentStream(r.Context(), genai.Text(prompt), genai.ImageData("jpeg", contents))
for {
	resp, err := iter.Next()
	if err == iterator.Done {
		break
	}
	if err != nil {
		log.Printf("Error generating content: %v\n", err)
		http.Error(w, "Error: unable to generate content", http.StatusInternalServerError)
		return
	}
	if resp == nil {
		continue
	}
	for _, cand := range resp.Candidates {
		if cand.Content != nil {
			for _, part := range cand.Content.Parts {
				fmt.Fprint(w, part)
			}
		}
	}
}

}

type Page struct {
Images string
}

var tmpl = template.Must(template.ParseFiles(“static/index.html”))

func indexHandler(w http.ResponseWriter, r http.Request) {
// Load all baked goods images from the static/images directory.
matches, err := filepath.Glob(filepath.Join(“static”, “images”, "baked_goods_
.jpeg"))
if err != nil {
log.Printf(“Error loading baked goods images: %v”, err)
}
var page = &Page{Images: make(string, len(matches))}
for i, match := range matches {
page.Images[i] = filepath.Base(match)
}
switch r.URL.Path {
case “/”:
err = tmpl.Execute(w, page)
if err != nil {
log.Printf(“Template execution error: %v”, err)
}
}
}

func main() {
// Parse flags.
flag.Usage = usage
flag.Parse()

// Parse and validate arguments (none).
args := flag.Args()
if len(args) != 0 {
	usage()
}

// Get the Gemini API key from the environment.
if key := os.Getenv("API_KEY"); key != "" {
	apiKey = key
}

client, err := genai.NewClient(context.Background(), option.WithAPIKey(apiKey))
if err != nil {
	log.Println(err)
}
defer client.Close()
model := client.GenerativeModel("gemini-pro-vision") // use 'gemini-pro' for text -> text
model.SafetySettings = []*genai.SafetySetting{
	{
		Category:  genai.HarmCategoryHarassment,
		Threshold: genai.HarmBlockOnlyHigh,
	},
}

// Serve static files and handle API requests.
fs := http.FileServer(http.Dir("static"))
http.Handle("/static/", http.StripPrefix("/static/", fs))
http.HandleFunc("/api/generate", func(w http.ResponseWriter, r *http.Request) { generateHandler(w, r, model) })
http.HandleFunc("/", indexHandler)
log.Fatal(http.ListenAndServe(*addr, nil))

}

Yep, that’s what was missing! Thank you!

1 Like