From 6b9b778d821728427eea07b1b97ba07378137d0b Mon Sep 17 00:00:00 2001 From: Daniel Weis Date: Fri, 8 May 2026 15:01:24 -0400 Subject: [PATCH] fix: resolve "function response turn must come immediately after function call" error (#26691) Co-authored-by: Tommaso Sciortino --- packages/core/src/core/geminiChat.test.ts | 67 +++++++++++++++++++++++ packages/core/src/core/geminiChat.ts | 10 +++- 2 files changed, 76 insertions(+), 1 deletion(-) diff --git a/packages/core/src/core/geminiChat.test.ts b/packages/core/src/core/geminiChat.test.ts index 1a54821f52..f689d86a1c 100644 --- a/packages/core/src/core/geminiChat.test.ts +++ b/packages/core/src/core/geminiChat.test.ts @@ -2796,4 +2796,71 @@ describe('GeminiChat', () => { ]); }); }); + + describe('getHistory with curated: true', () => { + it('should not drop model turns with function calls and empty text', () => { + const history: Content[] = [ + { role: 'user', parts: [{ text: 'Hello' }] }, + { + role: 'model', + parts: [{ functionCall: { name: 'test_tool', args: {} }, text: '' }], + }, + { + role: 'user', + parts: [{ functionResponse: { name: 'test_tool', response: {} } }], + }, + ]; + const chatWithHistory = new GeminiChat(mockConfig, '', [], history); + + const curatedHistory = chatWithHistory.getHistory(true); + + expect(curatedHistory.length).toBe(3); + expect(curatedHistory[1].role).toBe('model'); + expect(curatedHistory[1].parts![0].functionCall).toBeDefined(); + }); + + it('should not drop model turns with inlineData and empty text', () => { + const history: Content[] = [ + { role: 'user', parts: [{ text: 'Hello' }] }, + { + role: 'model', + parts: [ + { + inlineData: { mimeType: 'image/jpeg', data: 'base64...' }, + text: '', + }, + ], + }, + ]; + const chatWithHistory = new GeminiChat(mockConfig, '', [], history); + + const curatedHistory = chatWithHistory.getHistory(true); + + expect(curatedHistory.length).toBe(2); + expect(curatedHistory[1].role).toBe('model'); + expect(curatedHistory[1].parts![0].inlineData).toBeDefined(); + }); + + it('should not drop model turns with fileData and empty text', () => { + const history: Content[] = [ + { role: 'user', parts: [{ text: 'Hello' }] }, + { + role: 'model', + parts: [ + { + fileData: { mimeType: 'image/jpeg', fileUri: 'https://...' }, + text: '', + }, + ], + }, + ]; + const chatWithHistory = new GeminiChat(mockConfig, '', [], history); + + const curatedHistory = chatWithHistory.getHistory(true); + + expect(curatedHistory.length).toBe(2); + expect(curatedHistory[1].role).toBe('model'); + expect(curatedHistory[1].parts![0].fileData).toBeDefined(); + }); + }); }); diff --git a/packages/core/src/core/geminiChat.ts b/packages/core/src/core/geminiChat.ts index 398214a028..daa6e9d11b 100644 --- a/packages/core/src/core/geminiChat.ts +++ b/packages/core/src/core/geminiChat.ts @@ -146,7 +146,15 @@ function isValidContent(content: Content): boolean { if (part === undefined || Object.keys(part).length === 0) { return false; } - if (!part.thought && part.text !== undefined && part.text === '') { + if ( + !part.thought && + !part.functionCall && + !part.functionResponse && + !part.inlineData && + !part.fileData && + part.text !== undefined && + part.text === '' + ) { return false; } }