Skip to content
This repository was archived by the owner on Jul 29, 2025. It is now read-only.

Commit cd04c44

Browse files
replace github.com/google/generative-ai-go with github.com/googleapis/go-genai (#138)
* replace to github.com/googleapis/go-genai * fix history logic * small fixes --------- Co-authored-by: Kujtim Hoxha <kujtimii.h@gmail.com>
1 parent 88711db commit cd04c44

File tree

3 files changed

+76
-97
lines changed

3 files changed

+76
-97
lines changed

go.mod

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ require (
1818
github.com/charmbracelet/x/ansi v0.8.0
1919
github.com/fsnotify/fsnotify v1.8.0
2020
github.com/go-logfmt/logfmt v0.6.0
21-
github.com/google/generative-ai-go v0.19.0
2221
github.com/google/uuid v1.6.0
2322
github.com/lrstanley/bubblezone v0.0.0-20250315020633-c249a3fe1231
2423
github.com/mark3labs/mcp-go v0.17.0
@@ -32,16 +31,14 @@ require (
3231
github.com/spf13/cobra v1.9.1
3332
github.com/spf13/viper v1.20.0
3433
github.com/stretchr/testify v1.10.0
35-
google.golang.org/api v0.215.0
3634
)
3735

3836
require (
3937
cloud.google.com/go v0.116.0 // indirect
40-
cloud.google.com/go/ai v0.8.0 // indirect
38+
github.com/google/go-cmp v0.7.0 // indirect
39+
github.com/gorilla/websocket v1.5.3 // indirect
4140
cloud.google.com/go/auth v0.13.0 // indirect
42-
cloud.google.com/go/auth/oauth2adapt v0.2.6 // indirect
4341
cloud.google.com/go/compute/metadata v0.6.0 // indirect
44-
cloud.google.com/go/longrunning v0.5.7 // indirect
4542
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0 // indirect
4643
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect
4744
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 // indirect
@@ -111,7 +108,6 @@ require (
111108
github.com/yuin/goldmark v1.7.8 // indirect
112109
github.com/yuin/goldmark-emoji v1.0.5 // indirect
113110
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
114-
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0 // indirect
115111
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 // indirect
116112
go.opentelemetry.io/otel v1.35.0 // indirect
117113
go.opentelemetry.io/otel/metric v1.35.0 // indirect
@@ -120,13 +116,11 @@ require (
120116
golang.org/x/crypto v0.37.0 // indirect
121117
golang.org/x/image v0.26.0 // indirect
122118
golang.org/x/net v0.39.0 // indirect
123-
golang.org/x/oauth2 v0.25.0 // indirect
124119
golang.org/x/sync v0.13.0 // indirect
125120
golang.org/x/sys v0.32.0 // indirect
126121
golang.org/x/term v0.31.0 // indirect
127122
golang.org/x/text v0.24.0 // indirect
128-
golang.org/x/time v0.8.0 // indirect
129-
google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422 // indirect
123+
google.golang.org/genai v1.3.0
130124
google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 // indirect
131125
google.golang.org/grpc v1.71.0 // indirect
132126
google.golang.org/protobuf v1.36.6 // indirect

go.sum

Lines changed: 4 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,9 @@
11
cloud.google.com/go v0.116.0 h1:B3fRrSDkLRt5qSHWe40ERJvhvnQwdZiHu0bJOpldweE=
22
cloud.google.com/go v0.116.0/go.mod h1:cEPSRWPzZEswwdr9BxE6ChEn01dWlTaF05LiC2Xs70U=
3-
cloud.google.com/go/ai v0.8.0 h1:rXUEz8Wp2OlrM8r1bfmpF2+VKqc1VJpafE3HgzRnD/w=
4-
cloud.google.com/go/ai v0.8.0/go.mod h1:t3Dfk4cM61sytiggo2UyGsDVW3RF1qGZaUKDrZFyqkE=
53
cloud.google.com/go/auth v0.13.0 h1:8Fu8TZy167JkW8Tj3q7dIkr2v4cndv41ouecJx0PAHs=
64
cloud.google.com/go/auth v0.13.0/go.mod h1:COOjD9gwfKNKz+IIduatIhYJQIc0mG3H102r/EMxX6Q=
7-
cloud.google.com/go/auth/oauth2adapt v0.2.6 h1:V6a6XDu2lTwPZWOawrAa9HUK+DB2zfJyTuciBG5hFkU=
8-
cloud.google.com/go/auth/oauth2adapt v0.2.6/go.mod h1:AlmsELtlEBnaNTL7jCj8VQFLy6mbZv0s4Q7NGBeQ5E8=
95
cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I=
106
cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg=
11-
cloud.google.com/go/longrunning v0.5.7 h1:WLbHekDbjK1fVFD3ibpFFVoyizlLRl73I7YKuAKilhU=
12-
cloud.google.com/go/longrunning v0.5.7/go.mod h1:8GClkudohy1Fxm3owmBGid8W0pSgodEMwEAztp38Xng=
137
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0 h1:g0EZJwz7xkXQiZAI5xi9f3WWFYBlX1CPTrR+NDToRkQ=
148
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0/go.mod h1:XCW7KnZet0Opnr7HccfUw1PLc4CjHqpcaxW8DHklNkQ=
159
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0 h1:tfLQ34V6F7tVSwoTf/4lH5sE0o6eCJuNDTmH09nDpbc=
@@ -123,8 +117,6 @@ github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeD
123117
github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
124118
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
125119
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
126-
github.com/google/generative-ai-go v0.19.0 h1:R71szggh8wHMCUlEMsW2A/3T+5LdEIkiaHSYgSpUgdg=
127-
github.com/google/generative-ai-go v0.19.0/go.mod h1:JYolL13VG7j79kM5BtHz4qwONHkeJQzOCkKXnpqtS/E=
128120
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
129121
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
130122
github.com/google/s2a-go v0.1.8 h1:zZDs9gcbt9ZPLV0ndSyQk6Kacx2g/X+SKYovpnz3SMM=
@@ -137,6 +129,8 @@ github.com/googleapis/gax-go/v2 v2.14.1 h1:hb0FFeiPaQskmvakKu5EbCbpntQn48jyHuvrk
137129
github.com/googleapis/gax-go/v2 v2.14.1/go.mod h1:Hb/NubMaVM88SrNkvl8X/o8XWwDJEPqouaLeN2IUxoA=
138130
github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=
139131
github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0=
132+
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
133+
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
140134
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
141135
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
142136
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
@@ -254,8 +248,6 @@ github.com/yuin/goldmark-emoji v1.0.5 h1:EMVWyCGPlXJfUXBXpuMu+ii3TIaxbVBnEX9uaDC
254248
github.com/yuin/goldmark-emoji v1.0.5/go.mod h1:tTkZEbwu5wkPmgTcitqddVxY9osFZiavD+r4AzQrh1U=
255249
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
256250
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
257-
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0 h1:r6I7RJCN86bpD/FQwedZ0vSixDpwuWREjW9oRMsmqDc=
258-
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0/go.mod h1:B9yO6b04uB80CzjedvewuqDhxJxi11s7/GtiGa8bAjI=
259251
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 h1:TT4fX+nBOA/+LUkobKGW1ydGcn+G3vRw9+g5HwCphpk=
260252
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0/go.mod h1:L7UH0GbB0p47T4Rri3uHjbpCFYrVrwc1I25QhNPiGK8=
261253
go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
@@ -295,8 +287,6 @@ golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
295287
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
296288
golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
297289
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
298-
golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70=
299-
golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
300290
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
301291
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
302292
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -337,17 +327,13 @@ golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
337327
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
338328
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
339329
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
340-
golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg=
341-
golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
342330
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
343331
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
344332
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
345333
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
346334
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
347-
google.golang.org/api v0.215.0 h1:jdYF4qnyczlEz2ReWIsosNLDuzXyvFHJtI5gcr0J7t0=
348-
google.golang.org/api v0.215.0/go.mod h1:fta3CVtuJYOEdugLNWm6WodzOS8KdFckABwN4I40hzY=
349-
google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422 h1:GVIKPyP/kLIyVOgOnTwFOrvQaQUzOzGMCxgFUOEmm24=
350-
google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422/go.mod h1:b6h1vNKhxaSoEI+5jc3PJUCustfli/mRab7295pY7rw=
335+
google.golang.org/genai v1.3.0 h1:tXhPJF30skOjnnDY7ZnjK3q7IKy4PuAlEA0fk7uEaEI=
336+
google.golang.org/genai v1.3.0/go.mod h1:TyfOKRz/QyCaj6f/ZDt505x+YreXnY40l2I6k8TvgqY=
351337
google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 h1:e0AIkUUhxyBKh6ssZNrAMeqhA7RKUj42346d1y02i2g=
352338
google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
353339
google.golang.org/grpc v1.71.0 h1:kF77BGdPTQ4/JZWMlb9VpJ5pa25aqvVqogsxNHHdeBg=

internal/llm/provider/gemini.go

Lines changed: 69 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,12 @@ import (
99
"strings"
1010
"time"
1111

12-
"github.com/google/generative-ai-go/genai"
1312
"github.com/google/uuid"
1413
"github.com/opencode-ai/opencode/internal/config"
1514
"github.com/opencode-ai/opencode/internal/llm/tools"
1615
"github.com/opencode-ai/opencode/internal/logging"
1716
"github.com/opencode-ai/opencode/internal/message"
18-
"google.golang.org/api/iterator"
19-
"google.golang.org/api/option"
17+
"google.golang.org/genai"
2018
)
2119

2220
type geminiOptions struct {
@@ -39,7 +37,7 @@ func newGeminiClient(opts providerClientOptions) GeminiClient {
3937
o(&geminiOpts)
4038
}
4139

42-
client, err := genai.NewClient(context.Background(), option.WithAPIKey(opts.apiKey))
40+
client, err := genai.NewClient(context.Background(), &genai.ClientConfig{APIKey: opts.apiKey, Backend: genai.BackendGeminiAPI})
4341
if err != nil {
4442
logging.Error("Failed to create Gemini client", "error", err)
4543
return nil
@@ -57,11 +55,14 @@ func (g *geminiClient) convertMessages(messages []message.Message) []*genai.Cont
5755
for _, msg := range messages {
5856
switch msg.Role {
5957
case message.User:
60-
var parts []genai.Part
61-
parts = append(parts, genai.Text(msg.Content().String()))
58+
var parts []*genai.Part
59+
parts = append(parts, &genai.Part{Text: msg.Content().String()})
6260
for _, binaryContent := range msg.BinaryContent() {
6361
imageFormat := strings.Split(binaryContent.MIMEType, "/")
64-
parts = append(parts, genai.ImageData(imageFormat[1], binaryContent.Data))
62+
parts = append(parts, &genai.Part{InlineData: &genai.Blob{
63+
MIMEType: imageFormat[1],
64+
Data: binaryContent.Data,
65+
}})
6566
}
6667
history = append(history, &genai.Content{
6768
Parts: parts,
@@ -70,19 +71,21 @@ func (g *geminiClient) convertMessages(messages []message.Message) []*genai.Cont
7071
case message.Assistant:
7172
content := &genai.Content{
7273
Role: "model",
73-
Parts: []genai.Part{},
74+
Parts: []*genai.Part{},
7475
}
7576

7677
if msg.Content().String() != "" {
77-
content.Parts = append(content.Parts, genai.Text(msg.Content().String()))
78+
content.Parts = append(content.Parts, &genai.Part{Text: msg.Content().String()})
7879
}
7980

8081
if len(msg.ToolCalls()) > 0 {
8182
for _, call := range msg.ToolCalls() {
8283
args, _ := parseJsonToMap(call.Input)
83-
content.Parts = append(content.Parts, genai.FunctionCall{
84-
Name: call.Name,
85-
Args: args,
84+
content.Parts = append(content.Parts, &genai.Part{
85+
FunctionCall: &genai.FunctionCall{
86+
Name: call.Name,
87+
Args: args,
88+
},
8689
})
8790
}
8891
}
@@ -110,10 +113,14 @@ func (g *geminiClient) convertMessages(messages []message.Message) []*genai.Cont
110113
}
111114

112115
history = append(history, &genai.Content{
113-
Parts: []genai.Part{genai.FunctionResponse{
114-
Name: toolCall.Name,
115-
Response: response,
116-
}},
116+
Parts: []*genai.Part{
117+
{
118+
FunctionResponse: &genai.FunctionResponse{
119+
Name: toolCall.Name,
120+
Response: response,
121+
},
122+
},
123+
},
117124
Role: "function",
118125
})
119126
}
@@ -157,18 +164,6 @@ func (g *geminiClient) finishReason(reason genai.FinishReason) message.FinishRea
157164
}
158165

159166
func (g *geminiClient) send(ctx context.Context, messages []message.Message, tools []tools.BaseTool) (*ProviderResponse, error) {
160-
model := g.client.GenerativeModel(g.providerOptions.model.APIModel)
161-
model.SetMaxOutputTokens(int32(g.providerOptions.maxTokens))
162-
model.SystemInstruction = &genai.Content{
163-
Parts: []genai.Part{
164-
genai.Text(g.providerOptions.systemMessage),
165-
},
166-
}
167-
// Convert tools
168-
if len(tools) > 0 {
169-
model.Tools = g.convertTools(tools)
170-
}
171-
172167
// Convert messages
173168
geminiMessages := g.convertMessages(messages)
174169

@@ -178,16 +173,26 @@ func (g *geminiClient) send(ctx context.Context, messages []message.Message, too
178173
logging.Debug("Prepared messages", "messages", string(jsonData))
179174
}
180175

176+
history := geminiMessages[:len(geminiMessages)-1] // All but last message
177+
lastMsg := geminiMessages[len(geminiMessages)-1]
178+
chat, _ := g.client.Chats.Create(ctx, g.providerOptions.model.APIModel, &genai.GenerateContentConfig{
179+
MaxOutputTokens: int32(g.providerOptions.maxTokens),
180+
SystemInstruction: &genai.Content{
181+
Parts: []*genai.Part{{Text: g.providerOptions.systemMessage}},
182+
},
183+
Tools: g.convertTools(tools),
184+
}, history)
185+
181186
attempts := 0
182187
for {
183188
attempts++
184189
var toolCalls []message.ToolCall
185-
chat := model.StartChat()
186-
chat.History = geminiMessages[:len(geminiMessages)-1] // All but last message
187-
188-
lastMsg := geminiMessages[len(geminiMessages)-1]
189190

190-
resp, err := chat.SendMessage(ctx, lastMsg.Parts...)
191+
var lastMsgParts []genai.Part
192+
for _, part := range lastMsg.Parts {
193+
lastMsgParts = append(lastMsgParts, *part)
194+
}
195+
resp, err := chat.SendMessage(ctx, lastMsgParts...)
191196
// If there is an error we are going to see if we can retry the call
192197
if err != nil {
193198
retry, after, retryErr := g.shouldRetry(attempts, err)
@@ -210,15 +215,15 @@ func (g *geminiClient) send(ctx context.Context, messages []message.Message, too
210215

211216
if len(resp.Candidates) > 0 && resp.Candidates[0].Content != nil {
212217
for _, part := range resp.Candidates[0].Content.Parts {
213-
switch p := part.(type) {
214-
case genai.Text:
215-
content = string(p)
216-
case genai.FunctionCall:
218+
switch {
219+
case part.Text != "":
220+
content = string(part.Text)
221+
case part.FunctionCall != nil:
217222
id := "call_" + uuid.New().String()
218-
args, _ := json.Marshal(p.Args)
223+
args, _ := json.Marshal(part.FunctionCall.Args)
219224
toolCalls = append(toolCalls, message.ToolCall{
220225
ID: id,
221-
Name: p.Name,
226+
Name: part.FunctionCall.Name,
222227
Input: string(args),
223228
Type: "function",
224229
Finished: true,
@@ -244,18 +249,6 @@ func (g *geminiClient) send(ctx context.Context, messages []message.Message, too
244249
}
245250

246251
func (g *geminiClient) stream(ctx context.Context, messages []message.Message, tools []tools.BaseTool) <-chan ProviderEvent {
247-
model := g.client.GenerativeModel(g.providerOptions.model.APIModel)
248-
model.SetMaxOutputTokens(int32(g.providerOptions.maxTokens))
249-
model.SystemInstruction = &genai.Content{
250-
Parts: []genai.Part{
251-
genai.Text(g.providerOptions.systemMessage),
252-
},
253-
}
254-
// Convert tools
255-
if len(tools) > 0 {
256-
model.Tools = g.convertTools(tools)
257-
}
258-
259252
// Convert messages
260253
geminiMessages := g.convertMessages(messages)
261254

@@ -265,6 +258,16 @@ func (g *geminiClient) stream(ctx context.Context, messages []message.Message, t
265258
logging.Debug("Prepared messages", "messages", string(jsonData))
266259
}
267260

261+
history := geminiMessages[:len(geminiMessages)-1] // All but last message
262+
lastMsg := geminiMessages[len(geminiMessages)-1]
263+
chat, _ := g.client.Chats.Create(ctx, g.providerOptions.model.APIModel, &genai.GenerateContentConfig{
264+
MaxOutputTokens: int32(g.providerOptions.maxTokens),
265+
SystemInstruction: &genai.Content{
266+
Parts: []*genai.Part{{Text: g.providerOptions.systemMessage}},
267+
},
268+
Tools: g.convertTools(tools),
269+
}, history)
270+
268271
attempts := 0
269272
eventChan := make(chan ProviderEvent)
270273

@@ -273,23 +276,19 @@ func (g *geminiClient) stream(ctx context.Context, messages []message.Message, t
273276

274277
for {
275278
attempts++
276-
chat := model.StartChat()
277-
chat.History = geminiMessages[:len(geminiMessages)-1]
278-
lastMsg := geminiMessages[len(geminiMessages)-1]
279-
280-
iter := chat.SendMessageStream(ctx, lastMsg.Parts...)
281279

282280
currentContent := ""
283281
toolCalls := []message.ToolCall{}
284282
var finalResp *genai.GenerateContentResponse
285283

286284
eventChan <- ProviderEvent{Type: EventContentStart}
287285

288-
for {
289-
resp, err := iter.Next()
290-
if err == iterator.Done {
291-
break
292-
}
286+
var lastMsgParts []genai.Part
287+
288+
for _, part := range lastMsg.Parts {
289+
lastMsgParts = append(lastMsgParts, *part)
290+
}
291+
for resp, err := range chat.SendMessageStream(ctx, lastMsgParts...) {
293292
if err != nil {
294293
retry, after, retryErr := g.shouldRetry(attempts, err)
295294
if retryErr != nil {
@@ -318,22 +317,22 @@ func (g *geminiClient) stream(ctx context.Context, messages []message.Message, t
318317

319318
if len(resp.Candidates) > 0 && resp.Candidates[0].Content != nil {
320319
for _, part := range resp.Candidates[0].Content.Parts {
321-
switch p := part.(type) {
322-
case genai.Text:
323-
delta := string(p)
320+
switch {
321+
case part.Text != "":
322+
delta := string(part.Text)
324323
if delta != "" {
325324
eventChan <- ProviderEvent{
326325
Type: EventContentDelta,
327326
Content: delta,
328327
}
329328
currentContent += delta
330329
}
331-
case genai.FunctionCall:
330+
case part.FunctionCall != nil:
332331
id := "call_" + uuid.New().String()
333-
args, _ := json.Marshal(p.Args)
332+
args, _ := json.Marshal(part.FunctionCall.Args)
334333
newCall := message.ToolCall{
335334
ID: id,
336-
Name: p.Name,
335+
Name: part.FunctionCall.Name,
337336
Input: string(args),
338337
Type: "function",
339338
Finished: true,
@@ -421,12 +420,12 @@ func (g *geminiClient) toolCalls(resp *genai.GenerateContentResponse) []message.
421420

422421
if len(resp.Candidates) > 0 && resp.Candidates[0].Content != nil {
423422
for _, part := range resp.Candidates[0].Content.Parts {
424-
if funcCall, ok := part.(genai.FunctionCall); ok {
423+
if part.FunctionCall != nil {
425424
id := "call_" + uuid.New().String()
426-
args, _ := json.Marshal(funcCall.Args)
425+
args, _ := json.Marshal(part.FunctionCall.Args)
427426
toolCalls = append(toolCalls, message.ToolCall{
428427
ID: id,
429-
Name: funcCall.Name,
428+
Name: part.FunctionCall.Name,
430429
Input: string(args),
431430
Type: "function",
432431
})

0 commit comments

Comments
 (0)