Skip to content

feat(bidi): support request_state stop_event_loop flag#1954

Open
agent-of-mkmeral wants to merge 2 commits intostrands-agents:mainfrom
agent-of-mkmeral:feat/bidi-request-state-stop-event-loop
Open

feat(bidi): support request_state stop_event_loop flag#1954
agent-of-mkmeral wants to merge 2 commits intostrands-agents:mainfrom
agent-of-mkmeral:feat/bidi-request-state-stop-event-loop

Conversation

@agent-of-mkmeral
Copy link

@agent-of-mkmeral agent-of-mkmeral commented Mar 22, 2026

Summary

Replace the hard-coded stop_conversation tool name check with a generic request_state["stop_event_loop"] mechanism, while keeping stop_conversation with a deprecation warning for backward compatibility.

Motivation

The previous approach hard-coded a stop_conversation tool name check in the bidi agent loop. This was:

  • Inflexible: Only one specific tool could stop the conversation
  • Tightly coupled: The loop checked tool names instead of a generic signal
  • Redundant: strands_tools already has a stop tool that uses request_state

Changes

Deprecated (kept for backward compatibility)

  • stop_conversation tool now emits DeprecationWarning when called
  • Tool name check in loop also emits DeprecationWarning as a fallback
  • Both the tool and its export in bidi/__init__.py are preserved

Changed

  • agent/loop.py:
    • Initialize request_state in _invocation_state before tool execution
    • Primary: Check request_state.get("stop_event_loop", False) flag
    • Fallback: Check tool_use["name"] == "stop_conversation" with deprecation warning
  • tools/stop_conversation.py: Added DeprecationWarning pointing users to strands_tools.stop
  • tools/__init__.py: Updated docstring with deprecation notice
  • io/text.py: Updated log message

Added

  • test_bidi_agent_loop_stop_conversation_deprecated_but_works — verifies backward compatibility with deprecation
  • test_bidi_agent_loop_stop_event_loop_flag — verifies new flag mechanism
  • test_bidi_agent_loop_request_state_initialized_for_tools — verifies request_state availability
  • test_bidi_agent_loop_request_state_preserved_with_invocation_state — verifies existing state preserved

Migration Path

Before (deprecated):

from strands.experimental.bidi import stop_conversation, BidiAgent
agent = BidiAgent(tools=[stop_conversation, ...])

After (recommended):

from strands_tools import stop
from strands.experimental.bidi import BidiAgent
agent = BidiAgent(tools=[stop, ...])

Or any custom tool:

@tool
def my_stop_tool(request_state: dict) -> str:
    request_state["stop_event_loop"] = True
    return "Goodbye!"

Based on: 657ddcd

Replace the built-in stop_conversation tool with a generic
request_state['stop_event_loop'] mechanism. This allows any tool
(e.g. strands_tools.stop) to signal the BidiAgent to gracefully
close the connection by setting request_state['stop_event_loop'] = True.

Changes:
- Remove stop_conversation tool and its exports
- Add request_state initialization in _run_tool to ensure tools
  that access request_state work correctly
- Replace tool name-based check with stop_event_loop flag check
- Add comprehensive tests for request_state initialization,
  stop_event_loop flag, and invocation_state preservation

Based on: strands-agents@657ddcd
Copy link
Contributor

@mkmeral mkmeral left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also check the docs, if we need to update anything there, create a related PR. check if stop event loop is documented somewhere. also check if stop conversation tool is used, etc.

await self._event_queue.put(ToolResultMessageEvent(tool_result_message))

# Check for stop_conversation before sending to model
if tool_use["name"] == "stop_conversation":
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@agent-of-mkmeral we should probably keep it for now, but add a warning that we will remove in next versions. So we support both, and we can remove the tool later.

It gives users who depend on it some time

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done! Commit 1381ba1 keeps stop_conversation with backward compatibility:

What How
stop_conversation tool Restored — emits DeprecationWarning when called
bidi/__init__.py export Restored — from strands.experimental.bidi import stop_conversation works
Loop check Primary: request_state["stop_event_loop"] flag. Fallback: tool_use["name"] == "stop_conversation" with deprecation warning
Tests New test_bidi_agent_loop_stop_conversation_deprecated_but_works verifies both the tool works AND the warning is emitted

Users who depend on stop_conversation get a clear deprecation message pointing them to strands_tools.stop or request_state["stop_event_loop"].

Address review feedback from @mkmeral to keep backward compatibility:

- Restore stop_conversation tool with deprecation warning (DeprecationWarning
  emitted at runtime when the tool is called)
- Restore stop_conversation export in bidi/__init__.py
- Support both mechanisms in loop.py:
  1. request_state['stop_event_loop'] flag (new, recommended)
  2. tool name 'stop_conversation' check (deprecated, backward compatible)
- Add test_bidi_agent_loop_stop_conversation_deprecated_but_works
- Update existing tests to work with both mechanisms
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants