Skip to content

Commit 9a02934

Browse files
feat(bidi): add configurable event_queue_size to BidiAgent
The internal event queue between the model receive loop and the output handler was hardcoded to maxsize=1. This causes the model receive loop to block whenever the output handler performs any async I/O (e.g., websocket.send_json()), resulting in audio chunks piling up and delivering in bursts — perceived as choppy audio. This is particularly noticeable with Gemini Live which sends many small audio chunks rapidly (~50/sec), unlike Nova Sonic which sends fewer, larger chunks. Add a configurable event_queue_size parameter to BidiAgent (default: 1 to preserve existing behavior). Users experiencing choppy audio with fast-delivering models can increase this value to provide buffering between the model receive loop and the output handler.
1 parent 980bc91 commit 9a02934

2 files changed

Lines changed: 11 additions & 1 deletion

File tree

src/strands/experimental/bidi/agent/agent.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ def __init__(
7474
state: AgentState | dict | None = None,
7575
session_manager: "SessionManager | None" = None,
7676
tool_executor: ToolExecutor | None = None,
77+
event_queue_size: int = 1,
7778
**kwargs: Any,
7879
):
7980
"""Initialize bidirectional agent.
@@ -93,6 +94,11 @@ def __init__(
9394
session_manager: Manager for handling agent sessions including conversation history and state.
9495
If provided, enables session-based persistence and state management.
9596
tool_executor: Definition of tool execution strategy (e.g., sequential, concurrent, etc.).
97+
event_queue_size: Maximum size of the internal event queue between the model receive loop
98+
and the output handler. Higher values provide more buffer for bursty audio delivery
99+
(e.g., Gemini Live) at the cost of slightly higher memory usage. Lower values apply
100+
more backpressure. Default of 32 provides ~640ms of audio buffer at typical chunk rates.
101+
Set to 1 for legacy behavior (may cause choppy audio with fast-delivering models).
96102
**kwargs: Additional configuration for future extensibility.
97103
98104
Raises:
@@ -108,6 +114,7 @@ def __init__(
108114

109115
self.system_prompt = system_prompt
110116
self.messages = messages or []
117+
self._event_queue_size = event_queue_size
111118

112119
# Agent identification
113120
self.agent_id = _identifier.validate(agent_id or _DEFAULT_AGENT_ID, _identifier.Identifier.AGENT)

src/strands/experimental/bidi/agent/loop.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ class _BidiAgentLoop:
4949
_invocation_state: Optional context to pass to tools during execution.
5050
This allows passing custom data (user_id, session_id, database connections, etc.)
5151
that tools can access via their invocation_state parameter.
52+
_event_queue_size: Maximum size of the internal event queue. Controls backpressure
53+
between the model receive loop and the output handler.
5254
_send_gate: Gate the sending of events to the model.
5355
Blocks when agent is reseting the model connection after timeout.
5456
"""
@@ -65,6 +67,7 @@ def __init__(self, agent: "BidiAgent") -> None:
6567
self._started = False
6668
self._task_pool = _TaskPool()
6769
self._event_queue: asyncio.Queue
70+
self._event_queue_size = agent._event_queue_size
6871
self._invocation_state: dict[str, Any]
6972

7073
self._send_gate = asyncio.Event()
@@ -94,7 +97,7 @@ async def start(self, invocation_state: dict[str, Any] | None = None) -> None:
9497
messages=self._agent.messages,
9598
)
9699

97-
self._event_queue = asyncio.Queue(maxsize=1)
100+
self._event_queue = asyncio.Queue(maxsize=self._event_queue_size)
98101

99102
self._task_pool = _TaskPool()
100103
self._task_pool.create(self._run_model())

0 commit comments

Comments
 (0)