@@ -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
2220type 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
159166func (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
246251func (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