From a098dbdcd5121b059fbd51a82f3e4557fe137623 Mon Sep 17 00:00:00 2001 From: "rosetta-livekit-bot[bot]" <282703043+rosetta-livekit-bot[bot]@users.noreply.github.com> Date: Thu, 25 Jun 2026 10:07:43 +0000 Subject: [PATCH 1/5] fix(google): broaden Gemini 3 Flash detection --- .changeset/gemini-3-flash-thinking.md | 5 +++++ plugins/google/src/llm.ts | 29 +++++++++++++++++++++++++-- 2 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 .changeset/gemini-3-flash-thinking.md diff --git a/.changeset/gemini-3-flash-thinking.md b/.changeset/gemini-3-flash-thinking.md new file mode 100644 index 000000000..b84a13d06 --- /dev/null +++ b/.changeset/gemini-3-flash-thinking.md @@ -0,0 +1,5 @@ +--- +"@livekit/agents-plugin-google": patch +--- + +fix(plugin-google): default Gemini 3 Flash variants to minimal thinking. diff --git a/plugins/google/src/llm.ts b/plugins/google/src/llm.ts index a4284aeef..e227b07d3 100644 --- a/plugins/google/src/llm.ts +++ b/plugins/google/src/llm.ts @@ -2,7 +2,12 @@ // // SPDX-License-Identifier: Apache-2.0 import type * as types from '@google/genai'; -import { FunctionCallingConfigMode, type GenerateContentConfig, GoogleGenAI } from '@google/genai'; +import { + FunctionCallingConfigMode, + type GenerateContentConfig, + GoogleGenAI, + ThinkingLevel, +} from '@google/genai'; import type { APIConnectOptions } from '@livekit/agents'; import { APIConnectionError, @@ -19,6 +24,16 @@ interface GoogleFormatData { systemMessages: string[] | null; } +function isGemini3Model(model: string): boolean { + const modelLower = model.toLowerCase(); + return modelLower.includes('gemini-3') || modelLower.startsWith('gemini-3'); +} + +function isGemini3FlashModel(model: string): boolean { + const modelLower = model.toLowerCase(); + return modelLower.startsWith('gemini-3') && modelLower.includes('flash'); +} + export interface LLMOptions { model: string | ChatModels; apiKey?: string; @@ -275,7 +290,17 @@ export class LLM extends llm.LLM { } if (this.#opts.thinkingConfig !== undefined) { - extras.thinkingConfig = this.#opts.thinkingConfig; + if (isGemini3Model(this.#opts.model)) { + const { includeThoughts, thinkingLevel } = this.#opts.thinkingConfig; + extras.thinkingConfig = { + includeThoughts, + thinkingLevel: + thinkingLevel ?? + (isGemini3FlashModel(this.#opts.model) ? ThinkingLevel.MINIMAL : ThinkingLevel.LOW), + }; + } else { + extras.thinkingConfig = this.#opts.thinkingConfig; + } } if (this.#opts.automaticFunctionCallingConfig !== undefined) { From 781953434852b23384cf60714f812960d5e0bc88 Mon Sep 17 00:00:00 2001 From: Chenghao Mou Date: Thu, 25 Jun 2026 15:23:34 +0100 Subject: [PATCH 2/5] add config checks for parity and log silent errors --- agents/src/voice/generation.ts | 3 +++ plugins/google/src/llm.ts | 24 ++++++++++++++++++++++-- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/agents/src/voice/generation.ts b/agents/src/voice/generation.ts index 6b66e034f..6cf12d6ea 100644 --- a/agents/src/voice/generation.ts +++ b/agents/src/voice/generation.ts @@ -476,6 +476,7 @@ export function performLLMInference( model?: string, provider?: string, ): [Task, _LLMGenerationData] { + const logger = log(); const textStream = new IdentityTransform(); const toolCallStream = new IdentityTransform(); @@ -584,6 +585,8 @@ export function performLLMInference( // Abort signal was triggered, handle gracefully return; } + // surface inference silent errors even when this task's rejection is never awaited + logger.error({ error }, 'error in llm node'); throw error; } finally { llmStreamReader?.releaseLock(); diff --git a/plugins/google/src/llm.ts b/plugins/google/src/llm.ts index e227b07d3..56882d739 100644 --- a/plugins/google/src/llm.ts +++ b/plugins/google/src/llm.ts @@ -14,6 +14,7 @@ import { APIStatusError, DEFAULT_API_CONNECT_OPTIONS, llm, + log, shortuuid, } from '@livekit/agents'; import type { ChatModels } from './models.js'; @@ -290,8 +291,16 @@ export class LLM extends llm.LLM { } if (this.#opts.thinkingConfig !== undefined) { + const { includeThoughts, thinkingBudget, thinkingLevel } = this.#opts.thinkingConfig; + if (isGemini3Model(this.#opts.model)) { - const { includeThoughts, thinkingLevel } = this.#opts.thinkingConfig; + // Gemini 3: only supports thinkingLevel + if (thinkingBudget !== undefined && thinkingLevel === undefined) { + log().warn( + `Model ${this.#opts.model} is Gemini 3 which does not support thinkingBudget. ` + + `Please use thinkingLevel ('low' or 'high') instead. Ignoring thinkingBudget.`, + ); + } extras.thinkingConfig = { includeThoughts, thinkingLevel: @@ -299,7 +308,18 @@ export class LLM extends llm.LLM { (isGemini3FlashModel(this.#opts.model) ? ThinkingLevel.MINIMAL : ThinkingLevel.LOW), }; } else { - extras.thinkingConfig = this.#opts.thinkingConfig; + // Gemini 2.5 and earlier: only supports thinkingBudget + if (thinkingLevel !== undefined && thinkingBudget === undefined) { + throw new Error( + `Model ${this.#opts.model} does not support thinkingLevel. ` + + `Please use thinkingBudget (number) instead for Gemini 2.5 and earlier models.`, + ); + } + if (thinkingBudget !== undefined) { + extras.thinkingConfig = { thinkingBudget }; + } else { + extras.thinkingConfig = this.#opts.thinkingConfig; + } } } From 7d0169387be9c89a8e3456b6c58d5c98e05ff671 Mon Sep 17 00:00:00 2001 From: Chenghao Mou Date: Thu, 25 Jun 2026 15:24:55 +0100 Subject: [PATCH 3/5] Update error logging in performLLMInference --- .changeset/neat-crabs-watch.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/neat-crabs-watch.md diff --git a/.changeset/neat-crabs-watch.md b/.changeset/neat-crabs-watch.md new file mode 100644 index 000000000..dc9b825a5 --- /dev/null +++ b/.changeset/neat-crabs-watch.md @@ -0,0 +1,6 @@ +--- +"@livekit/agents": patch +"@livekit/agents-plugin-google": patch +--- + +fix(llm): log silent error from performLLMInference From e1b7d9479da7c041fbafb66a51f00e6b904b2fc3 Mon Sep 17 00:00:00 2001 From: Chenghao Mou Date: Thu, 25 Jun 2026 15:27:24 +0100 Subject: [PATCH 4/5] clean up --- plugins/google/src/llm.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/google/src/llm.ts b/plugins/google/src/llm.ts index 56882d739..3e14e0727 100644 --- a/plugins/google/src/llm.ts +++ b/plugins/google/src/llm.ts @@ -27,12 +27,12 @@ interface GoogleFormatData { function isGemini3Model(model: string): boolean { const modelLower = model.toLowerCase(); - return modelLower.includes('gemini-3') || modelLower.startsWith('gemini-3'); + return modelLower.includes('gemini-3'); } function isGemini3FlashModel(model: string): boolean { const modelLower = model.toLowerCase(); - return modelLower.startsWith('gemini-3') && modelLower.includes('flash'); + return modelLower.includes('gemini-3') && modelLower.includes('flash'); } export interface LLMOptions { From 6fb3f9f339eb303ea8a0eedfab830cd125ad289c Mon Sep 17 00:00:00 2001 From: Chenghao Mou Date: Thu, 25 Jun 2026 16:03:00 +0100 Subject: [PATCH 5/5] address comment: skip dropping includeThoughts --- plugins/google/src/llm.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/plugins/google/src/llm.ts b/plugins/google/src/llm.ts index 3e14e0727..701d7d975 100644 --- a/plugins/google/src/llm.ts +++ b/plugins/google/src/llm.ts @@ -310,13 +310,15 @@ export class LLM extends llm.LLM { } else { // Gemini 2.5 and earlier: only supports thinkingBudget if (thinkingLevel !== undefined && thinkingBudget === undefined) { - throw new Error( + log().warn( `Model ${this.#opts.model} does not support thinkingLevel. ` + - `Please use thinkingBudget (number) instead for Gemini 2.5 and earlier models.`, + `Please use thinkingBudget (number) instead for Gemini 2.5 and earlier models. ` + + `Ignoring thinkingLevel.`, ); - } - if (thinkingBudget !== undefined) { - extras.thinkingConfig = { thinkingBudget }; + extras.thinkingConfig = { includeThoughts }; + } else if (thinkingBudget !== undefined) { + // Preserve includeThoughts so callers relying on visible reasoning keep receiving it. + extras.thinkingConfig = { thinkingBudget, includeThoughts }; } else { extras.thinkingConfig = this.#opts.thinkingConfig; }