@@ -38,6 +38,7 @@ interface ToolCallback {
3838 readonly id : string ;
3939 readonly index : number ;
4040 args : string ;
41+ thoughtSignature ?: string ;
4142}
4243const convertMessageToPart = ( message : LanguageModelMessage ) : Part [ ] | undefined => {
4344 if ( LanguageModelMessage . isTextMessage ( message ) && message . text . length > 0 ) {
@@ -52,7 +53,7 @@ const convertMessageToPart = (message: LanguageModelMessage): Part[] | undefined
5253 return [ { functionResponse : { id : message . tool_use_id , name : message . name , response : { output : message . content } } } ] ;
5354
5455 } else if ( LanguageModelMessage . isThinkingMessage ( message ) ) {
55- return [ { thought : true } , { text : message . thinking } ] ;
56+ return [ { thought : true , text : message . thinking } ] ;
5657 } else if ( LanguageModelMessage . isImageMessage ( message ) && ImageContent . isBase64 ( message . image ) ) {
5758 return [ { inlineData : { data : message . image . base64data , mimeType : message . image . mimeType } } ] ;
5859 }
@@ -101,6 +102,29 @@ function transformToGeminiMessages(
101102 } ;
102103}
103104
105+ /**
106+ * Validates that all items in the array are proper Content objects with role and parts
107+ * @param contents Array to validate
108+ * @throws Error if validation fails
109+ */
110+ function validateContents ( contents : Content [ ] ) : void {
111+ for ( let i = 0 ; i < contents . length ; i ++ ) {
112+ const content = contents [ i ] ;
113+ if ( ! content || typeof content !== 'object' ) {
114+ throw new Error ( `Invalid content at index ${ i } : not an object` ) ;
115+ }
116+ if ( ! content . role || ( content . role !== 'user' && content . role !== 'model' ) ) {
117+ throw new Error ( `Invalid content at index ${ i } : missing or invalid role (got ${ content . role } )` ) ;
118+ }
119+ if ( ! content . parts || ! Array . isArray ( content . parts ) ) {
120+ throw new Error ( `Invalid content at index ${ i } : missing or invalid parts array` ) ;
121+ }
122+ if ( content . parts . length === 0 ) {
123+ throw new Error ( `Invalid content at index ${ i } : parts array is empty` ) ;
124+ }
125+ }
126+ }
127+
104128export const GoogleModelIdentifier = Symbol ( 'GoogleModelIdentifier' ) ;
105129
106130/**
@@ -160,7 +184,7 @@ export class GoogleModel implements LanguageModel {
160184 toolMessages ?: Content [ ]
161185 ) : Promise < LanguageModelStreamResponse > {
162186 const settings = this . getSettings ( request ) ;
163- const { contents : parts , systemMessage } = transformToGeminiMessages ( request . messages ) ;
187+ const { contents, systemMessage } = transformToGeminiMessages ( request . messages ) ;
164188 const functionDeclarations = this . createFunctionDeclarations ( request ) ;
165189
166190 const toolConfig : ToolConfig = { } ;
@@ -171,6 +195,12 @@ export class GoogleModel implements LanguageModel {
171195 } ;
172196 }
173197
198+ // Merge contents and tool messages
199+ const allContents = [ ...contents , ...( toolMessages ?? [ ] ) ] ;
200+
201+ // Validate all contents before making API call
202+ validateContents ( allContents ) ;
203+
174204 // Wrap the API call in the retry mechanism
175205 const stream = await this . withRetry ( async ( ) =>
176206 genAI . models . generateContentStream ( {
@@ -187,7 +217,7 @@ export class GoogleModel implements LanguageModel {
187217 temperature : 1 ,
188218 ...settings
189219 } ,
190- contents : [ ... parts , ... ( toolMessages ?? [ ] ) ]
220+ contents : allContents
191221 } ) ) ;
192222
193223 const that = this ;
@@ -228,11 +258,16 @@ export class GoogleModel implements LanguageModel {
228258 const callId = functionCall . id ?? crypto . randomUUID ( ) . replace ( / - / g, '' ) ;
229259 let toolCall = toolCallMap [ callId ] ;
230260 if ( toolCall === undefined ) {
261+ const candidateParts = chunk . candidates ?. [ 0 ] ?. content ?. parts ;
262+ const matchingPart = candidateParts ?. find ( p =>
263+ p . functionCall ?. id === callId && p . thoughtSignature
264+ ) ;
231265 toolCall = {
232266 name : functionCall . name ?? '' ,
233267 args : functionCall . args ? JSON . stringify ( functionCall . args ) : '{}' ,
234268 id : callId ,
235- index : functionIndex ++
269+ index : functionIndex ++ ,
270+ thoughtSignature : matchingPart ?. thoughtSignature
236271 } ;
237272 toolCallMap [ callId ] = toolCall ;
238273
@@ -310,18 +345,33 @@ export class GoogleModel implements LanguageModel {
310345 yield { tool_calls : calls } ;
311346
312347 // Format tool responses for Gemini
313- const toolResponses : Part [ ] = toolResult . map ( call => ( {
314- functionResponse : {
315- id : call . id ,
316- name : call . name ,
317- response : that . formatToolCallResult ( call . result )
348+ const toolResponses : Part [ ] = toolResult . map ( call => {
349+ const toolCall = toolCallMap [ call . id ] ;
350+ const part : Part = {
351+ functionResponse : {
352+ id : call . id ,
353+ name : call . name ,
354+ response : that . formatToolCallResult ( call . result )
355+ }
356+ } ;
357+ if ( toolCall ?. thoughtSignature ) {
358+ part . thoughtSignature = toolCall . thoughtSignature ;
318359 }
319- } ) ) ;
360+ return part ;
361+ } ) ;
320362 const responseMessage : Content = { role : 'user' , parts : toolResponses } ;
321363
322364 const messages = [ ...( toolMessages ?? [ ] ) ] ;
323365 if ( currentContent ) {
324- messages . push ( currentContent ) ;
366+ // Ensure currentContent has proper structure
367+ if ( ! currentContent . role ) {
368+ currentContent . role = 'model' ;
369+ }
370+ if ( ! currentContent . parts || currentContent . parts . length === 0 ) {
371+ console . debug ( 'currentContent has no parts, skipping' ) ;
372+ } else {
373+ messages . push ( currentContent ) ;
374+ }
325375 }
326376 messages . push ( responseMessage ) ;
327377 // Continue the conversation with tool results
@@ -371,9 +421,12 @@ export class GoogleModel implements LanguageModel {
371421 request : UserRequest
372422 ) : Promise < LanguageModelTextResponse > {
373423 const settings = this . getSettings ( request ) ;
374- const { contents : parts , systemMessage } = transformToGeminiMessages ( request . messages ) ;
424+ const { contents, systemMessage } = transformToGeminiMessages ( request . messages ) ;
375425 const functionDeclarations = this . createFunctionDeclarations ( request ) ;
376426
427+ // Validate contents before making API call
428+ validateContents ( contents ) ;
429+
377430 // Wrap the API call in the retry mechanism
378431 const model = await this . withRetry ( async ( ) => genAI . models . generateContent ( {
379432 model : this . model ,
@@ -389,7 +442,7 @@ export class GoogleModel implements LanguageModel {
389442 } ) ,
390443 ...settings
391444 } ,
392- contents : parts
445+ contents
393446 } ) ) ;
394447
395448 try {
0 commit comments