alert-guitar-55655
02/24/2026, 3:49 PMtall-airline-16756
02/24/2026, 3:49 PMtall-airline-16756
02/24/2026, 3:50 PMfrom google.genai import types
session = AgentSession(
llm=google.realtime.RealtimeModel(
thinking_config=types.ThinkingConfig(
include_thoughts=False,
),
),
)
This prevents the model from forwarding thought content.
1. Filter thinking tokens before TTS (defensive approach):
◦ Intercept and clean the LLM stream output and remove tags like <think> and </think> (or any other thought markers) before sending text to the TTS provider.
◦ Example approach (stream processing):
async for chunk in stream:
content = getattr(chunk.delta, 'content', None) if hasattr(chunk, 'delta') else str(chunk)
if content:
content = content.replace("<think>", "").replace("</think>", "")
# send cleaned content to TTS
1. Create a custom Agent llm_node to modify output:
◦ Override the LLM output node so all streamed chunks are cleaned of thinking tokens and any internal markers before they reach the voice pipeline.
Questions to pinpoint the root cause
• Which exact Gemini model are you using (for example gemini-2.5-flash or gemini-2.5-flash-native-audio-preview-12-2025)?
• Are you using the realtime Gemini Live API (realtime model) or a standard LLM endpoint?
• Do you currently set any thinking_config options? If so, what are they?
• Can you confirm the exact text markers you’re seeing in the spoken output (e.g., literal <think> tags, “(thinking)”, or free-form internal notes)?
• Is the behavior truly intermittent for the same prompt, or does it correlate with certain prompts or tools?
If you share the model name, a snippet showing how you create the model/session, and an example of the spoken output (the raw text before TTS if possible), I’ll suggest the exact code changes to stop thinking tokens from being vocalized.
Sources: Gemini Live API plugin | LiveKit Documentation | Modifying LLM output before TTS | LiveKit Documentationalert-guitar-55655
02/24/2026, 3:59 PMasync def entrypoint(ctx: JobContext):
await ctx.connect()
agent = Agent(
instructions=INSTRUCTIONS,
vad=silero.VAD.load(),
stt=deepgram.STT(model="nova-3", language="multi"),
llm=google.LLM(model="gemini-2.5-flash",
vertexai=True
),
tts=sarvam.TTS(
target_language_code="hi-IN",
speaker="shubh",
model="bulbul:v3",
pace=1.1,
),
)
session = AgentSession()
await session.start(agent=agent, room=ctx.room)
await session.say("Hello?", allow_interruptions=True)
HEre is my code, i dont want a real time model, and idont think livekit requires me to handle thinking token it should handle thinking token handling out of box in the gemini plugin?tall-airline-16756
02/24/2026, 4:00 PMgoogle.LLM(model="gemini-2.5-flash", vertexai=True) — not a realtime model), thinking output is not automatically filtered out for you. That means if the model emits thought markers (for example <think>...</think> or similar internal text), those tokens can reach your TTS unless you explicitly prevent or remove them.
What you can do right now (2 recommended options) 1) Disable thinking in the model session (preferred if available)
• If your LLM wrapper supports a thinking_config or similar option, set it to not include thoughts. This prevents the model from forwarding thought content into responses.
2) Filter model output before sending to TTS (defensive and reliable)
• Intercept the text returned by the LLM and remove thought markers before passing text to tts.
• Example approach (pseudo/async pattern matching your code style):
# after getting model text (sync or streamed), remove markers
clean_text = raw_text.replace("<think>", "").replace("</think>", "")
# send clean_text to TTS
• If your LLM returns streaming chunks, perform this cleaning per chunk before forwarding to the TTS pipeline.
Why the behavior is intermittent
• The model only produces thinking content for some responses, so you’ll see the TTS speak thoughts only when the model emits them.
• Not using a realtime model does not automatically guarantee that thinking tokens won’t be present; they must be suppressed or removed.
Concrete next steps I recommend
1. Confirm whether your google.LLM wrapper supports a thinking_config (or equivalent). If it does, set include_thoughts=False when constructing the LLM/session.
2. Add a simple post-processing step between LLM output and your tts call to strip out any <think> or other internal markers.
3. If you want, I can give a small, concrete code snippet that plugs into your current entrypoint flow showing where to filter the LLM output (non-realtime case). Do you receive the LLM output as a single string, or do you handle streaming chunks in your code?
If you share whether your google.LLM object has any thinking_config option or how your code receives the LLM response (single text vs stream), I’ll provide the exact code you can paste into your project.alert-guitar-55655
02/25/2026, 8:16 AMtall-airline-16756
02/25/2026, 8:17 AMgoogle.LLM plugin (non‑realtime) does not automatically strip thinking content for you. You need to prevent thinking tokens either by configuring the model to exclude thoughts (if the plugin exposes that option) or by filtering the LLM output before calling TTS. Since you only control the code you shared, the reliable fix is to filter the model output before passing it to tts.
Two practical options (pick one)
1) Preferred — disable thinking in the model if the plugin exposes a thinking config
• If the LiveKit google.LLM plugin supports a thinking_config or similar parameter, construct the LLM with thoughts disabled:
llm = google.LLM(
model="gemini-2.5-flash",
vertexai=True,
thinking_config={"include_thoughts": False} # if supported by the plugin wrapper
)
agent = Agent(
instructions=INSTRUCTIONS,
vad=silero.VAD.load(),
stt=deepgram.STT(model="nova-3", language="multi"),
llm=llm,
tts=sarvam.TTS(...),
)
• If that option exists in your LiveKit wrapper, this prevents the model from emitting thought content at the source.
2) Reliable — strip thinking markers right before you call TTS (works regardless of wrapper support)
• Post‑process the text returned by the LLM and remove common thinking tokens (for example <think>...</think>) before sending to session.say() or your TTS pipeline.
• Minimal change example that fits your entrypoint pattern. Insert a small helper and use it when you want the agent to speak:
def strip_thoughts(text: str) -> str:
if not text:
return text
# remove common thought markers; extend if you see different markers
return text.replace("<think>", "").replace("</think>", "").replace("(thinking)", "")
async def entrypoint(ctx: JobContext):
await ctx.connect()
agent = Agent(
instructions=INSTRUCTIONS,
vad=silero.VAD.load(),
stt=deepgram.STT(model="nova-3", language="multi"),
llm=google.LLM(model="gemini-2.5-flash", vertexai=True),
tts=sarvam.TTS(
target_language_code="hi-IN",
speaker="shubh",
model="bulbul:v3",
pace=1.1,
),
)
session = AgentSession()
await session.start(agent=agent, room=ctx.room)
# Example: clean text before sending to be spoken
raw = "Hello?" # if you get raw from LLM, apply strip_thoughts(raw)
cleaned = strip_thoughts(raw)
await session.say(cleaned, allow_interruptions=True)
If your speech flow uses streamed chunks (the agent streaming model output into TTS), apply strip_thoughts() to each chunk before forwarding to the TTS call.
What I need from you to give an exact patch
• Does the LiveKit google.LLM constructor accept a thinking_config or thinking_config={"include_thoughts": False}? If you’re not sure, tell me the exact LiveKit/Google plugin version or paste the attributes available on google.LLM in your environment and I’ll confirm the right parameter name.
• Do you receive the LLM output as a single string (synchronous) or as streaming chunks in your current agent flow? If streaming, paste the small snippet where you receive/send those chunks and I’ll show precisely where to filter.
Next steps I can help with
• Provide a one-line patch for your current integration if you confirm whether thinking_config exists.
• Give a streaming-safe filter snippet if your flow uses chunked output.
If you’d like, paste the exact spoken output you see (the raw text before TTS) so I can add any additional markers to strip.