Skip to content

fix: block preemptive generation during tool execution#1166

Open
Raysharr wants to merge 2 commits intolivekit:mainfrom
Raysharr:fix/preemptive-generation-tool-execution-race
Open

fix: block preemptive generation during tool execution#1166
Raysharr wants to merge 2 commits intolivekit:mainfrom
Raysharr:fix/preemptive-generation-tool-execution-race

Conversation

@Raysharr
Copy link
Copy Markdown
Contributor

Description

Fix a race condition where preemptive generation can fire while tool execution is still in progress, causing LLM chat context corruption and session crashes.

Changes Made

  • Add _toolExecutionInProgress flag to AgentActivity that is set true when performToolExecutions starts and false when tool execution completes (both normal and interrupt paths)
  • Add guard check for _toolExecutionInProgress in onPreemptiveGeneration() to block preemptive generation while tools are executing
  • Cover both reply task code paths (legacy and new)
  • Add unit tests for the new guard behavior

Root Cause

When _markGenerationDone() fires after TTS playout (line 2107) but before await executeToolsTask.result (line 2108), mainTask clears _currentSpeech. This opens a window where onPreemptiveGeneration() passes all guards and snapshots chatCtx while it contains orphaned FunctionCall items (no matching FunctionCallOutput).

removeInvalidToolCalls() in utils.ts strips the orphans, producing "function call missing the corresponding function output, ignoring" warnings. The corrupted context then causes consecutive LLM generation failures.

Addresses TODO(AJS-272).

Pre-Review Checklist

  • Build passes: All builds (lint, typecheck, tests) pass locally
  • AI-generated code reviewed: Removed unnecessary comments and ensured code quality
  • Changes explained: All changes are properly documented and justified above
  • Scope appropriate: All changes relate to the PR title

Testing

  • Automated tests added/updated
  • All tests pass
  • 2 new unit tests verify the _toolExecutionInProgress guard in onPreemptiveGeneration()
  • Observed in production: session outbound-1774267632790 crashed with this exact sequence

Additional Notes

Timeline from production logs:

12:08:05 | available_slots API call started
12:08:06 | API completed, filler still playing
12:08:06 | [STT] patient speaks during tool execution
12:08:07 | preemptive generation starts ← should have been blocked
12:08:07 | ⚠️ function call missing the corresponding function output, ignoring
12:08:10–14 | ❌ failed to generate LLM completion (3x) → session crash

The fix is minimal and surgical — a single boolean flag checked in the existing guard clause. No changes to tool execution flow, scheduling, or _markGenerationDone() ordering.

When _markGenerationDone() fires after TTS playout but before tool
execution completes, _currentSpeech is cleared in mainTask. This allows
onPreemptiveGeneration() to snapshot chatCtx while it contains orphaned
FunctionCall items (no matching FunctionCallOutput). The corrupted
context causes LLM generation failures and session crashes.

Add _toolExecutionInProgress flag set around performToolExecutions in
both reply task code paths. The flag is checked in
onPreemptiveGeneration() guard to prevent preemptive generation while
tools are still executing.

Addresses TODO(AJS-272).
@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Mar 23, 2026

🦋 Changeset detected

Latest commit: 4bc2a97

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 22 packages
Name Type
@livekit/agents Patch
@livekit/agents-plugin-anam Patch
@livekit/agents-plugin-baseten Patch
@livekit/agents-plugin-bey Patch
@livekit/agents-plugin-cartesia Patch
@livekit/agents-plugin-deepgram Patch
@livekit/agents-plugin-elevenlabs Patch
@livekit/agents-plugin-google Patch
@livekit/agents-plugin-hedra Patch
@livekit/agents-plugin-inworld Patch
@livekit/agents-plugin-lemonslice Patch
@livekit/agents-plugin-livekit Patch
@livekit/agents-plugin-neuphonic Patch
@livekit/agents-plugin-openai Patch
@livekit/agents-plugin-phonic Patch
@livekit/agents-plugin-resemble Patch
@livekit/agents-plugin-rime Patch
@livekit/agents-plugin-sarvam Patch
@livekit/agents-plugin-silero Patch
@livekit/agents-plugins-test Patch
@livekit/agents-plugin-trugen Patch
@livekit/agents-plugin-xai Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

devin-ai-integration[bot]

This comment was marked as resolved.

If an await between setting the flag true and the explicit false
assignment throws, the flag stays true forever and permanently blocks
preemptive generation. Wrap both pipeline and realtime tool execution
sections in try/finally to guarantee cleanup.

Addresses Devin review feedback on PR livekit#1166.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant