Skip to content

tool_choice parameter persists across turns when passed to generate_reply() #4671

@enrique-quadrivia

Description

@enrique-quadrivia

Description

When calling session.generate_reply(tool_choice="none") during agent initialization (e.g., in on_enter), the tool_choice setting appears to persist for all subsequent conversation turns, preventing tools from ever being called even when the user responds.

Expected Behavior

The tool_choice parameter passed to generate_reply() should only affect that specific generation, not subsequent turns. Tools should be callable on subsequent turns when the user responds.

Actual Behavior

After calling generate_reply(tool_choice="none") once, extraction/function tools are never called on subsequent user turns, even though tool_choice is not passed on those turns.

Reproduction Steps

  1. Create an agent with function tools (e.g., extraction tools)
  2. In the agent's on_enter or initial greeting, call:
    self.session.generate_reply(tool_choice="none", allow_interruptions=True)
  3. User responds with information that should trigger tool extraction
  4. Observe that tools are never called on subsequent turns

Code Analysis

Looking at agent_activity.py, the logic at lines ~900-903 in _generate_reply:

model_settings=ModelSettings(
    tool_choice=tool_choice
    if utils.is_given(tool_choice) or self._tool_choice is None
    else self._tool_choice
),

This uses the persisted self._tool_choice when:

  • tool_choice is NOT explicitly given, AND
  • self._tool_choice has been previously set

The self._tool_choice is set via update_options() at line 352, but it's unclear how generate_reply() triggers this persistence path.

Environment

  • livekit-agents version: 1.3.10
  • Python version: 3.13
  • LLM: OpenAI (non-realtime)

Workaround

Don't pass tool_choice="none" to generate_reply() even when you don't want tools called on the greeting. The worst case (LLM calls extraction tool on greeting before user responds) results in empty extraction with no side effects.

# Instead of:
if self.tools:
    self.session.generate_reply(tool_choice="none", allow_interruptions=True)
else:
    self.session.generate_reply(allow_interruptions=True)

# Use:
self.session.generate_reply(allow_interruptions=True)

Related Issues

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions