55 type GenerateContentParameters ,
66 type GenerateContentConfig ,
77 type GroundingMetadata ,
8+ FunctionCallingConfigMode ,
9+ Content ,
810} from "@google/genai"
911import type { JWTInput } from "google-auth-library"
1012
@@ -92,9 +94,9 @@ export class GeminiHandler extends BaseProvider implements SingleCompletionHandl
9294 return true
9395 } )
9496
95- const contents = geminiMessages . map ( ( message ) =>
96- convertAnthropicMessageToGemini ( message , { includeThoughtSignatures } ) ,
97- )
97+ const contents : Content [ ] = geminiMessages
98+ . map ( ( message ) => convertAnthropicMessageToGemini ( message , { includeThoughtSignatures } ) )
99+ . flat ( )
98100
99101 const tools : GenerateContentConfig [ "tools" ] = [ ]
100102 if ( this . options . enableUrlContext ) {
@@ -105,6 +107,16 @@ export class GeminiHandler extends BaseProvider implements SingleCompletionHandl
105107 tools . push ( { googleSearch : { } } )
106108 }
107109
110+ if ( metadata ?. tools && metadata . tools . length > 0 ) {
111+ tools . push ( {
112+ functionDeclarations : metadata . tools . map ( ( tool ) => ( {
113+ name : ( tool as any ) . function . name ,
114+ description : ( tool as any ) . function . description ,
115+ parametersJsonSchema : ( tool as any ) . function . parameters ,
116+ } ) ) ,
117+ } )
118+ }
119+
108120 // Determine temperature respecting model capabilities and defaults:
109121 // - If supportsTemperature is explicitly false, ignore user overrides
110122 // and pin to the model's defaultTemperature (or omit if undefined).
@@ -124,6 +136,25 @@ export class GeminiHandler extends BaseProvider implements SingleCompletionHandl
124136 ...( tools . length > 0 ? { tools } : { } ) ,
125137 }
126138
139+ if ( metadata ?. tool_choice ) {
140+ config . toolConfig = {
141+ functionCallingConfig : {
142+ mode :
143+ metadata . tool_choice === "auto"
144+ ? FunctionCallingConfigMode . AUTO
145+ : metadata . tool_choice === "required"
146+ ? FunctionCallingConfigMode . ANY
147+ : metadata . tool_choice === "none"
148+ ? FunctionCallingConfigMode . NONE
149+ : FunctionCallingConfigMode . ANY ,
150+ allowedFunctionNames :
151+ typeof metadata . tool_choice === "object" && "function" in metadata . tool_choice
152+ ? [ metadata . tool_choice . function . name ]
153+ : undefined ,
154+ } ,
155+ }
156+ }
157+
127158 const params : GenerateContentParameters = { model, contents, config }
128159
129160 try {
@@ -151,6 +182,7 @@ export class GeminiHandler extends BaseProvider implements SingleCompletionHandl
151182 thought ?: boolean
152183 text ?: string
153184 thoughtSignature ?: string
185+ functionCall ?: { name : string ; args : Record < string , unknown > }
154186 } > ) {
155187 // Capture thought signatures so they can be persisted into API history.
156188 const thoughtSignature = part . thoughtSignature
@@ -166,6 +198,13 @@ export class GeminiHandler extends BaseProvider implements SingleCompletionHandl
166198 if ( part . text ) {
167199 yield { type : "reasoning" , text : part . text }
168200 }
201+ } else if ( part . functionCall ) {
202+ yield {
203+ type : "tool_call" ,
204+ id : part . functionCall . name , // Gemini doesn't provide call IDs, so we use the function name
205+ name : part . functionCall . name ,
206+ arguments : JSON . stringify ( part . functionCall . args ) ,
207+ }
169208 } else {
170209 // This is regular content
171210 if ( part . text ) {
@@ -343,7 +382,10 @@ export class GeminiHandler extends BaseProvider implements SingleCompletionHandl
343382 const countTokensRequest = {
344383 model,
345384 // Token counting does not need encrypted continuation; always drop thoughtSignature.
346- contents : convertAnthropicContentToGemini ( content , { includeThoughtSignatures : false } ) ,
385+ contents : convertAnthropicMessageToGemini (
386+ { role : "user" , content } ,
387+ { includeThoughtSignatures : false } ,
388+ ) ,
347389 }
348390
349391 const response = await this . client . models . countTokens ( countTokensRequest )
0 commit comments