Skip to content

Commit 48a2941

Browse files
authored
fix(go/plugins/googlegenai): content candidates improvements (#3714)
1 parent 4d0e367 commit 48a2941

File tree

3 files changed

+39
-25
lines changed

3 files changed

+39
-25
lines changed

go/go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ require (
4141
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0
4242
golang.org/x/tools v0.34.0
4343
google.golang.org/api v0.236.0
44-
google.golang.org/genai v1.24.0
44+
google.golang.org/genai v1.30.0
4545
)
4646

4747
require (
@@ -52,7 +52,7 @@ require (
5252
github.com/klauspost/cpuid/v2 v2.2.9 // indirect
5353
github.com/pierrec/lz4/v4 v4.1.18 // indirect
5454
github.com/spf13/cast v1.7.1 // indirect
55-
github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
55+
github.com/yosida95/uritemplate/v3 v3.0.2
5656
github.com/zeebo/xxh3 v1.0.2 // indirect
5757
golang.org/x/mod v0.25.0 // indirect
5858
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect

go/go.sum

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,6 @@ github.com/MicahParks/keyfunc v1.9.0 h1:lhKd5xrFHLNOWrDc4Tyb/Q1AJ4LCzQ48GVJyVIID
5454
github.com/MicahParks/keyfunc v1.9.0/go.mod h1:IdnCilugA0O/99dW+/MkvlyrsX8+L8+x95xuVNtM5jw=
5555
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
5656
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
57-
github.com/anthropics/anthropic-sdk-go v1.4.0 h1:fU1jKxYbQdQDiEXCxeW5XZRIOwKevn/PMg8Ay1nnUx0=
58-
github.com/anthropics/anthropic-sdk-go v1.4.0/go.mod h1:AapDW22irxK2PSumZiQXYUFvsdQgkwIWlpESweWZI/c=
5957
github.com/anthropics/anthropic-sdk-go v1.9.1 h1:raRhZKmayVSVZtLpLDd6IsMXvxLeeSU03/2IBTerWlg=
6058
github.com/anthropics/anthropic-sdk-go v1.9.1/go.mod h1:WTz31rIUHUHqai2UslPpw5CwXrQP3geYBioRV4WOLvE=
6159
github.com/apache/arrow/go/v15 v15.0.2 h1:60IliRbiyTWCWjERBCkO1W4Qun9svcYoZrSLcyOsMLE=
@@ -203,14 +201,6 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS
203201
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
204202
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
205203
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
206-
github.com/google/dotprompt/go v0.0.0-20250611200215-bb73406b05ca h1:LuQ8KS5N04c37jyaq6jelLdNi0GfI6QJb8lpnYaDW9Y=
207-
github.com/google/dotprompt/go v0.0.0-20250611200215-bb73406b05ca/go.mod h1:dnIk+MSMnipm9uZyPIgptq7I39aDxyjBiaev/OG0W0Y=
208-
github.com/google/dotprompt/go v0.0.0-20250922193017-eeb62744224e h1:AK/O+vHflqzTehlcUBV6pkwZHCWIc8PrpA6xiiRo9d0=
209-
github.com/google/dotprompt/go v0.0.0-20250922193017-eeb62744224e/go.mod h1:k8cjJAQWc//ac/bMnzItyOFbfT01tgRTZGgxELCuxEQ=
210-
github.com/google/dotprompt/go v0.0.0-20250922225138-cb24085a67ed h1:xPu5zXopXdj48LXZqU6DosoL0oT7nbddgTMkjWoyHqQ=
211-
github.com/google/dotprompt/go v0.0.0-20250922225138-cb24085a67ed/go.mod h1:k8cjJAQWc//ac/bMnzItyOFbfT01tgRTZGgxELCuxEQ=
212-
github.com/google/dotprompt/go v0.0.0-20250923103342-a8a91d1dff59 h1:EywQhHXdzYlMKD7Gxl9Ho34c8dQ0meph6FuRN9iENEY=
213-
github.com/google/dotprompt/go v0.0.0-20250923103342-a8a91d1dff59/go.mod h1:k8cjJAQWc//ac/bMnzItyOFbfT01tgRTZGgxELCuxEQ=
214204
github.com/google/dotprompt/go v0.0.0-20251014011017-8d056e027254 h1:okN800+zMJOGHLJCgry+OGzhhtH6YrjQh1rluHmOacE=
215205
github.com/google/dotprompt/go v0.0.0-20251014011017-8d056e027254/go.mod h1:k8cjJAQWc//ac/bMnzItyOFbfT01tgRTZGgxELCuxEQ=
216206
github.com/google/flatbuffers v23.5.26+incompatible h1:M9dgRyhJemaM4Sw8+66GHBu8ioaQmyPLg1b8VwK5WJg=
@@ -547,8 +537,8 @@ google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9Ywl
547537
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
548538
google.golang.org/appengine/v2 v2.0.6 h1:LvPZLGuchSBslPBp+LAhihBeGSiRh1myRoYK4NtuBIw=
549539
google.golang.org/appengine/v2 v2.0.6/go.mod h1:WoEXGoXNfa0mLvaH5sV3ZSGXwVmy8yf7Z1JKf3J3wLI=
550-
google.golang.org/genai v1.24.0 h1:j5lt+Qr7W0+OBxwwEPe4DQ+ygEqpvZuSBvYoHIuUjhg=
551-
google.golang.org/genai v1.24.0/go.mod h1:QPj5NGJw+3wEOHg+PrsWwJKvG6UC84ex5FR7qAYsN/M=
540+
google.golang.org/genai v1.30.0 h1:7021aneIvl24nEBLbtQFEWleHsMbjzpcQvkT4WcJ1dc=
541+
google.golang.org/genai v1.30.0/go.mod h1:7pAilaICJlQBonjKKJNhftDFv3SREhZcTe9F6nRcjbg=
552542
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
553543
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
554544
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=

go/plugins/googlegenai/gemini.go

Lines changed: 35 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,10 @@ func generate(
276276
if err != nil {
277277
return nil, err
278278
}
279-
r := translateResponse(resp)
279+
r, err := translateResponse(resp)
280+
if err != nil {
281+
return nil, err
282+
}
280283

281284
r.Request = input
282285
if cache != nil {
@@ -298,8 +301,11 @@ func generate(
298301
return nil, err
299302
}
300303
for i, c := range chunk.Candidates {
301-
tc := translateCandidate(c)
302-
err := cb(ctx, &ai.ModelResponseChunk{
304+
tc, err := translateCandidate(c)
305+
if err != nil {
306+
return nil, err
307+
}
308+
err = cb(ctx, &ai.ModelResponseChunk{
303309
Content: tc.Message.Content,
304310
})
305311
if err != nil {
@@ -322,7 +328,10 @@ func generate(
322328
},
323329
}
324330
resp.Candidates = merged
325-
r = translateResponse(resp)
331+
r, err = translateResponse(resp)
332+
if err != nil {
333+
return nil, fmt.Errorf("failed to generate contents: %w", err)
334+
}
326335
r.Request = input
327336
if cache != nil {
328337
r.Message.Metadata = cacheMetadata(r.Message.Metadata, cache)
@@ -339,11 +348,17 @@ func toGeminiRequest(input *ai.ModelRequest, cache *genai.CachedContent) (*genai
339348
return nil, err
340349
}
341350

342-
// only one candidate is supported by default
343-
gcc.CandidateCount = 1
351+
// candidate count might not be set to 1 and will keep its zero value if not set
352+
// e.g. default value from reflection server is 0
353+
if gcc.CandidateCount == 0 {
354+
gcc.CandidateCount = 1
355+
}
344356

345357
// Genkit primitive fields must be used instead of go-genai fields
346358
// i.e.: system prompt, tools, cached content, response schema, etc
359+
if gcc.CandidateCount != 1 {
360+
return nil, errors.New("multiple candidates is not supported")
361+
}
347362
if gcc.SystemInstruction != nil {
348363
return nil, errors.New("system instruction must be set using Genkit feature: ai.WithSystemPrompt()")
349364
}
@@ -657,7 +672,7 @@ func toGeminiToolChoice(toolChoice ai.ToolChoice, tools []*ai.ToolDefinition) (*
657672
}
658673

659674
// translateCandidate translates from a genai.GenerateContentResponse to an ai.ModelResponse.
660-
func translateCandidate(cand *genai.Candidate) *ai.ModelResponse {
675+
func translateCandidate(cand *genai.Candidate) (*ai.ModelResponse, error) {
661676
m := &ai.ModelResponse{}
662677
switch cand.FinishReason {
663678
case genai.FinishReasonStop:
@@ -673,6 +688,10 @@ func translateCandidate(cand *genai.Candidate) *ai.ModelResponse {
673688
default: // Unspecified
674689
m.FinishReason = ai.FinishReasonUnknown
675690
}
691+
692+
if cand.Content == nil {
693+
return nil, fmt.Errorf("no valid candidates were found in the generate response")
694+
}
676695
msg := &ai.Message{}
677696
msg.Role = ai.Role(cand.Content.Role)
678697

@@ -729,14 +748,19 @@ func translateCandidate(cand *genai.Candidate) *ai.ModelResponse {
729748
msg.Content = append(msg.Content, p)
730749
}
731750
m.Message = msg
732-
return m
751+
return m, nil
733752
}
734753

735754
// Translate from a genai.GenerateContentResponse to a ai.ModelResponse.
736-
func translateResponse(resp *genai.GenerateContentResponse) *ai.ModelResponse {
755+
func translateResponse(resp *genai.GenerateContentResponse) (*ai.ModelResponse, error) {
737756
var r *ai.ModelResponse
757+
var err error
758+
738759
if len(resp.Candidates) > 0 {
739-
r = translateCandidate(resp.Candidates[0])
760+
r, err = translateCandidate(resp.Candidates[0])
761+
if err != nil {
762+
return nil, err
763+
}
740764
} else {
741765
r = &ai.ModelResponse{}
742766
}
@@ -752,7 +776,7 @@ func translateResponse(resp *genai.GenerateContentResponse) *ai.ModelResponse {
752776
r.Usage.CachedContentTokens = int(u.CachedContentTokenCount)
753777
r.Usage.ThoughtsTokens = int(u.ThoughtsTokenCount)
754778
}
755-
return r
779+
return r, nil
756780
}
757781

758782
// toGeminiParts converts a slice of [ai.Part] to a slice of [genai.Part].

0 commit comments

Comments
 (0)