@@ -20,6 +20,7 @@ import {
2020 MessageData ,
2121 ModelReference ,
2222 Part ,
23+ TextPart ,
2324 ToolDefinition ,
2425} from 'genkit/model' ;
2526import {
@@ -131,34 +132,34 @@ function toGeminiMedia(part: Part): GeminiPart {
131132 media . videoMetadata = { ...videoMetadata } ;
132133 }
133134
134- return media ;
135+ return maybeAddGeminiThoughtSignature ( part , media ) ;
135136}
136137
137138function toGeminiToolRequest ( part : Part ) : GeminiPart {
138139 if ( ! part . toolRequest ?. input ) {
139140 throw Error ( 'Invalid ToolRequestPart: input was missing.' ) ;
140141 }
141- return {
142+ return maybeAddGeminiThoughtSignature ( part , {
142143 functionCall : {
143144 name : part . toolRequest . name ,
144145 args : part . toolRequest . input ,
145146 } ,
146- } ;
147+ } ) ;
147148}
148149
149150function toGeminiToolResponse ( part : Part ) : GeminiPart {
150151 if ( ! part . toolResponse ?. output ) {
151152 throw Error ( 'Invalid ToolResponsePart: output was missing.' ) ;
152153 }
153- return {
154+ return maybeAddGeminiThoughtSignature ( part , {
154155 functionResponse : {
155156 name : part . toolResponse . name ,
156157 response : {
157158 name : part . toolResponse . name ,
158159 content : part . toolResponse . output ,
159160 } ,
160161 } ,
161- } ;
162+ } ) ;
162163}
163164
164165function toGeminiReasoning ( part : Part ) : GeminiPart {
@@ -174,21 +175,38 @@ function toGeminiReasoning(part: Part): GeminiPart {
174175
175176function toGeminiCustom ( part : Part ) : GeminiPart {
176177 if ( part . custom ?. codeExecutionResult ) {
177- return {
178+ return maybeAddGeminiThoughtSignature ( part , {
178179 codeExecutionResult : part . custom . codeExecutionResult ,
179- } ;
180+ } ) ;
180181 }
181182 if ( part . custom ?. executableCode ) {
182- return {
183+ return maybeAddGeminiThoughtSignature ( part , {
183184 executableCode : part . custom . executableCode ,
184- } ;
185+ } ) ;
185186 }
186187 throw new Error ( 'Unsupported Custom Part type' ) ;
187188}
188189
190+ function toGeminiText ( part : Part ) : GeminiPart {
191+ return maybeAddGeminiThoughtSignature ( part , { text : part . text ?? '' } ) ;
192+ }
193+
194+ function maybeAddGeminiThoughtSignature (
195+ part : Part ,
196+ geminiPart : GeminiPart
197+ ) : GeminiPart {
198+ if ( part . metadata ?. thoughtSignature ) {
199+ return {
200+ ...geminiPart ,
201+ thoughtSignature : part . metadata . thoughtSignature as string ,
202+ } ;
203+ }
204+ return geminiPart ;
205+ }
206+
189207function toGeminiPart ( part : Part ) : GeminiPart {
190- if ( part . text ) {
191- return { text : part . text } ;
208+ if ( typeof part . text === 'string' ) {
209+ return toGeminiText ( part ) ;
192210 }
193211 if ( part . media ) {
194212 return toGeminiMedia ( part ) ;
@@ -314,13 +332,27 @@ function fromGeminiFinishReason(
314332 case 'SPII' : // blocked for potentially containing Sensitive Personally Identifiable Information
315333 return 'blocked' ;
316334 case 'MALFORMED_FUNCTION_CALL' :
335+ case 'MISSING_THOUGHT_SIGNATURE' :
317336 case 'OTHER' :
318337 return 'other' ;
319338 default :
320339 return 'unknown' ;
321340 }
322341}
323342
343+ function maybeAddThoughtSignature ( geminiPart : GeminiPart , part : Part ) : Part {
344+ if ( geminiPart . thoughtSignature ) {
345+ return {
346+ ...part ,
347+ metadata : {
348+ ...part ?. metadata ,
349+ thoughtSignature : geminiPart . thoughtSignature ,
350+ } ,
351+ } ;
352+ }
353+ return part ;
354+ }
355+
324356function fromGeminiThought ( part : GeminiPart ) : Part {
325357 return {
326358 reasoning : part . text || '' ,
@@ -340,12 +372,13 @@ function fromGeminiInlineData(part: GeminiPart): Part {
340372 const { mimeType, data } = part . inlineData ;
341373 // Combine data and mimeType into a data URL
342374 const dataUrl = `data:${ mimeType } ;base64,${ data } ` ;
343- return {
375+
376+ return maybeAddThoughtSignature ( part , {
344377 media : {
345378 url : dataUrl ,
346379 contentType : mimeType ,
347380 } ,
348- } ;
381+ } ) ;
349382}
350383
351384function fromGeminiFileData ( part : GeminiPart ) : Part {
@@ -359,12 +392,12 @@ function fromGeminiFileData(part: GeminiPart): Part {
359392 ) ;
360393 }
361394
362- return {
395+ return maybeAddThoughtSignature ( part , {
363396 media : {
364397 url : part . fileData ?. fileUri ,
365398 contentType : part . fileData ?. mimeType ,
366399 } ,
367- } ;
400+ } ) ;
368401}
369402
370403function fromGeminiFunctionCall ( part : GeminiPart , ref : string ) : Part {
@@ -373,13 +406,13 @@ function fromGeminiFunctionCall(part: GeminiPart, ref: string): Part {
373406 'Invalid Gemini Function Call Part: missing function call data'
374407 ) ;
375408 }
376- return {
409+ return maybeAddThoughtSignature ( part , {
377410 toolRequest : {
378411 name : part . functionCall . name ,
379412 input : part . functionCall . args ,
380413 ref,
381414 } ,
382- } ;
415+ } ) ;
383416}
384417
385418function fromGeminiFunctionResponse ( part : GeminiPart , ref ?: string ) : Part {
@@ -388,46 +421,50 @@ function fromGeminiFunctionResponse(part: GeminiPart, ref?: string): Part {
388421 'Invalid Gemini Function Call Part: missing function call data'
389422 ) ;
390423 }
391- return {
424+ return maybeAddThoughtSignature ( part , {
392425 toolResponse : {
393426 name : part . functionResponse . name . replace ( / _ _ / g, '/' ) , // restore slashes
394427 output : part . functionResponse . response ,
395428 ref,
396429 } ,
397- } ;
430+ } ) ;
398431}
399432
400433function fromExecutableCode ( part : GeminiPart ) : Part {
401434 if ( ! part . executableCode ) {
402435 throw new Error ( 'Invalid GeminiPart: missing executableCode' ) ;
403436 }
404- return {
437+ return maybeAddThoughtSignature ( part , {
405438 custom : {
406439 executableCode : {
407440 language : part . executableCode . language ,
408441 code : part . executableCode . code ,
409442 } ,
410443 } ,
411- } ;
444+ } ) ;
412445}
413446
414447function fromCodeExecutionResult ( part : GeminiPart ) : Part {
415448 if ( ! part . codeExecutionResult ) {
416449 throw new Error ( 'Invalid GeminiPart: missing codeExecutionResult' ) ;
417450 }
418- return {
451+ return maybeAddThoughtSignature ( part , {
419452 custom : {
420453 codeExecutionResult : {
421454 outcome : part . codeExecutionResult . outcome ,
422455 output : part . codeExecutionResult . output ,
423456 } ,
424457 } ,
425- } ;
458+ } ) ;
459+ }
460+
461+ function fromGeminiText ( part : GeminiPart ) : Part {
462+ return maybeAddThoughtSignature ( part , { text : part . text } as TextPart ) ;
426463}
427464
428465function fromGeminiPart ( part : GeminiPart , ref : string ) : Part {
429466 if ( part . thought ) return fromGeminiThought ( part as any ) ;
430- if ( typeof part . text === 'string' ) return { text : part . text } ;
467+ if ( typeof part . text === 'string' ) return fromGeminiText ( part ) ;
431468 if ( part . inlineData ) return fromGeminiInlineData ( part ) ;
432469 if ( part . fileData ) return fromGeminiFileData ( part ) ;
433470 if ( part . functionCall ) return fromGeminiFunctionCall ( part , ref ) ;
0 commit comments