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
@mkmeral
Copy link
Contributor

mkmeral commented Mar 23, 2026

@agent-of-mkmeral do we need docs update for this?

@agent-of-mkmeral
Copy link
Author

Yes — docs definitely need updating. I found stop_conversation referenced in 7 docs files with zero mention of stop_event_loop or request_state:

File What needs changing
agent.mdx Line 170: "The special stop_conversation tool triggers agent shutdown" → needs update to describe request_state["stop_event_loop"] as primary, stop_conversation as deprecated fallback
events.mdx Line 167: "user_request" reason references stop_conversation
quickstart.mdx Lines 417-429: Full "Graceful Shutdown" section imports and uses stop_conversation
gemini_live.mdx Example imports stop_conversation from strands.experimental.bidi.tools
openai_realtime.mdx Same pattern — imports stop_conversation
nova_sonic.mdx Same pattern — imports stop_conversation
io.mdx 6 separate code examples using stop_conversation

I'll create a docs PR now that:

  1. Updates examples to show strands_tools.stop as the recommended approach
  2. Adds deprecation notices for stop_conversation imports
  3. Documents the request_state["stop_event_loop"] mechanism for custom tools
  4. Keeps stop_conversation mentions with deprecation context (matching the SDK backward compat)

agent-of-mkmeral added a commit to agent-of-mkmeral/docs that referenced this pull request Mar 23, 2026
…st_state

Update all bidi streaming docs to reflect the new stop mechanism:
- Replace stop_conversation imports with strands_tools.stop
- Document request_state['stop_event_loop'] flag for custom stop tools
- Add deprecation notices for legacy stop_conversation tool
- Update 7 files: agent, events, quickstart, io, and 3 model pages

Related: strands-agents/sdk-python#1954
@agent-of-mkmeral
Copy link
Author

📝 Docs PR created: strands-agents/docs#696

Updated 7 files across the bidi streaming docs:

  • agent.mdx — primary shutdown mechanism now describes request_state["stop_event_loop"]
  • events.mdxuser_request close reason updated
  • quickstart.mdx — full "Graceful Shutdown" rewrite with migration examples
  • nova_sonic.mdx, gemini_live.mdx, openai_realtime.mdx — all examples migrated
  • io.mdx — all 6 code examples migrated

All from strands.experimental.bidi.tools import stop_conversationfrom strands_tools import stop, with deprecation notices added.

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