diff --git a/.changeset/fair-google-turns.md b/.changeset/fair-google-turns.md new file mode 100644 index 000000000..928d1e9fb --- /dev/null +++ b/.changeset/fair-google-turns.md @@ -0,0 +1,5 @@ +--- +'@livekit/agents': patch +--- + +Ensure Google chat formatting ends with a user turn when an empty trailing user message is omitted. diff --git a/agents/src/llm/provider_format/google.test.ts b/agents/src/llm/provider_format/google.test.ts index c895a4bde..bd07487e2 100644 --- a/agents/src/llm/provider_format/google.test.ts +++ b/agents/src/llm/provider_format/google.test.ts @@ -536,6 +536,31 @@ describe('Google Provider Format - toChatCtx', () => { expect(formatData.systemMessages).toBeNull(); }); + it('should inject dummy user message when trailing user message is empty', async () => { + const ctx = ChatContext.empty(); + ctx.addMessage({ role: 'user', content: 'Hello' }); + ctx.addMessage({ role: 'assistant', content: 'Partial reply' }); + ctx.addMessage({ role: 'user', content: '' }); + + const [result, formatData] = await toChatCtx(ctx, true); + + expect(result).toEqual([ + { + role: 'user', + parts: [{ text: 'Hello' }], + }, + { + role: 'model', + parts: [{ text: 'Partial reply' }], + }, + { + role: 'user', + parts: [{ text: '.' }], + }, + ]); + expect(formatData.systemMessages).toBeNull(); + }); + it('should not inject dummy user message when disabled', async () => { const ctx = ChatContext.empty(); ctx.addMessage({ role: 'user', content: 'Hello' }); diff --git a/agents/src/llm/provider_format/google.ts b/agents/src/llm/provider_format/google.ts index d7d2197d2..98b356c82 100644 --- a/agents/src/llm/provider_format/google.ts +++ b/agents/src/llm/provider_format/google.ts @@ -101,7 +101,7 @@ export async function toChatCtx( } // Gemini requires the last message to end with user's turn before they can generate - if (injectDummyUserMessage && currentRole !== 'user') { + if (injectDummyUserMessage && turns[turns.length - 1]?.role !== 'user') { turns.push({ role: 'user', parts: [{ text: '.' }] }); }