55 type GenerateContentParameters ,
66 type GenerateContentConfig ,
77 type GroundingMetadata ,
8+ FunctionCallingConfigMode ,
89} from "@google/genai"
910import type { JWTInput } from "google-auth-library"
1011
@@ -92,9 +93,9 @@ export class GeminiHandler extends BaseProvider implements SingleCompletionHandl
9293 return true
9394 } )
9495
95- const contents = geminiMessages . map ( ( message ) =>
96- convertAnthropicMessageToGemini ( message , { includeThoughtSignatures } ) ,
97- )
96+ const contents : Content [ ] = geminiMessages
97+ . map ( ( message ) => convertAnthropicMessageToGemini ( message , { includeThoughtSignatures } ) )
98+ . flat ( )
9899
99100 const tools : GenerateContentConfig [ "tools" ] = [ ]
100101 if ( this . options . enableUrlContext ) {
@@ -105,6 +106,16 @@ export class GeminiHandler extends BaseProvider implements SingleCompletionHandl
105106 tools . push ( { googleSearch : { } } )
106107 }
107108
109+ if ( metadata ?. tools && metadata . tools . length > 0 ) {
110+ tools . push ( {
111+ functionDeclarations : metadata . tools . map ( ( tool ) => ( {
112+ name : ( tool as any ) . function . name ,
113+ description : ( tool as any ) . function . description ,
114+ parametersJsonSchema : ( tool as any ) . function . parameters ,
115+ } ) ) ,
116+ } )
117+ }
118+
108119 // Determine temperature respecting model capabilities and defaults:
109120 // - If supportsTemperature is explicitly false, ignore user overrides
110121 // and pin to the model's defaultTemperature (or omit if undefined).
@@ -124,6 +135,25 @@ export class GeminiHandler extends BaseProvider implements SingleCompletionHandl
124135 ...( tools . length > 0 ? { tools } : { } ) ,
125136 }
126137
138+ if ( metadata ?. tool_choice ) {
139+ config . toolConfig = {
140+ functionCallingConfig : {
141+ mode :
142+ metadata . tool_choice === "auto"
143+ ? FunctionCallingConfigMode . AUTO
144+ : metadata . tool_choice === "required"
145+ ? FunctionCallingConfigMode . ANY
146+ : metadata . tool_choice === "none"
147+ ? FunctionCallingConfigMode . NONE
148+ : FunctionCallingConfigMode . ANY ,
149+ allowedFunctionNames :
150+ typeof metadata . tool_choice === "object" && "function" in metadata . tool_choice
151+ ? [ metadata . tool_choice . function . name ]
152+ : undefined ,
153+ } ,
154+ }
155+ }
156+
127157 const params : GenerateContentParameters = { model, contents, config }
128158
129159 try {
@@ -151,6 +181,7 @@ export class GeminiHandler extends BaseProvider implements SingleCompletionHandl
151181 thought ?: boolean
152182 text ?: string
153183 thoughtSignature ?: string
184+ functionCall ?: { name : string ; args : Record < string , unknown > }
154185 } > ) {
155186 // Capture thought signatures so they can be persisted into API history.
156187 const thoughtSignature = part . thoughtSignature
@@ -166,6 +197,13 @@ export class GeminiHandler extends BaseProvider implements SingleCompletionHandl
166197 if ( part . text ) {
167198 yield { type : "reasoning" , text : part . text }
168199 }
200+ } else if ( part . functionCall ) {
201+ yield {
202+ type : "tool_call" ,
203+ id : part . functionCall . name , // Gemini doesn't provide call IDs, so we use the function name
204+ name : part . functionCall . name ,
205+ arguments : JSON . stringify ( part . functionCall . args ) ,
206+ }
169207 } else {
170208 // This is regular content
171209 if ( part . text ) {
@@ -343,7 +381,10 @@ export class GeminiHandler extends BaseProvider implements SingleCompletionHandl
343381 const countTokensRequest = {
344382 model,
345383 // Token counting does not need encrypted continuation; always drop thoughtSignature.
346- contents : convertAnthropicContentToGemini ( content , { includeThoughtSignatures : false } ) ,
384+ contents : convertAnthropicMessageToGemini (
385+ { role : "user" , content } ,
386+ { includeThoughtSignatures : false } ,
387+ ) ,
347388 }
348389
349390 const response = await this . client . models . countTokens ( countTokensRequest )
0 commit comments