Skip to content

feat(bidi): support cancellation from custom tools - #4664

Merged
mehtarac merged 8 commits into
strands-agents:mainfrom
pgrayy:agent-tasks/bidi-stop-conversation
Sep 29, 2026
Merged

mehtarac merged 8 commits into
strands-agents:mainfrom
pgrayy:agent-tasks/bidi-stop-conversation

Conversation

@pgrayy

@pgrayy pgrayy commented Sep 28, 2026 •

Copy link
Copy Markdown
Member

Description

Bidi tools need a supported way to end conversations without a packaged tool or special handling of its name. BidiAgent.cancel() provides the same API as Agent.cancel(), and exposing it on Python's LocalAgent makes it available to shared tools and hooks.

A documented end_conversation example replaces the packaged stop_conversation tool. This avoids coupling Bidi to the experimental generic stop tool or the deprecated strands_tools package. request_state remains initialized for compatibility with shared tools; using it for shutdown is discouraged.

Public API Changes

BidiAgent.cancel() is synchronous, thread-safe, and idempotent. Cancellation currently takes effect only after a tool group completes. Requests from other contexts remain pending until then. run() handles cleanup, and the agent can be reused.

from strands import LocalAgent, ToolContext, tool
from strands.experimental.bidi.agent import BidiAgent


@tool(context=True)
def end_conversation(tool_context: ToolContext[LocalAgent]) -> str:
    """End the conversation when the user asks to stop."""
    tool_context.agent.cancel()
    return "Ending conversation"


agent = BidiAgent(tools=[end_conversation])

Breaking Changes

Remove strands.experimental.bidi.tools.stop_conversation. Define a custom tool that calls agent.cancel() instead. Setting request_state["stop_event_loop"] no longer stops Bidi.

Related Issues

None.

Documentation PR

Included in this PR under site/.

Type of Change

Breaking change

Testing

  • I ran hatch run prepare

Verified cancellation through a custom tool on a live OpenAI Realtime connection using a generic system prompt. The tool result was recorded and run() returned cleanly. Reused the same agent and repeated tool-triggered cancellation successfully.

Ran the Bidi agent and loop unit suites on Python 3.10 and 3.14 after the final test refinements. Local checks cover cancellation, retry suppression, pending requests, cleanup, reuse, and the shared typing contract.

After syncing with upstream/main, all 829 Bidi and tool-executor unit tests passed. All 20 Bidi integration tests passed on Python 3.12 with Nova in us-west-2, alongside Google and OpenAI. Lint, formatting, and type checks passed.

Checklist

  • I have read the CONTRIBUTING document
  • I have reviewed and understand every line of code in this PR, including any generated by AI tools, and I can explain why it works
  • My change is focused and reasonably small; I have split unrelated work into separate PRs
  • I have added any necessary tests that prove my fix is effective or my feature works
  • I have updated the documentation accordingly
  • I have added an appropriate example to the documentation to outline the feature, or no new docs are needed
  • My changes generate no new warnings
  • Any dependent changes have been merged and published

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

@github-actions github-actions Bot added python Pull requests that update python code area-bidirectional-streaming Related to bidirectional streaming area-tool Tool behavior/api bug Something isn't working complexity/low Touched functions have low cognitive complexity (<=10) size/m labels Sep 28, 2026
@pgrayy
pgrayy marked this pull request as ready for review September 28, 2026 18:50
@pgrayy
pgrayy requested a review from a team as a code owner September 28, 2026 18:50
@codecov

codecov Bot commented Sep 28, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

Comment thread strands-py/src/strands/experimental/bidi/agent/loop.py
@github-actions

Copy link
Copy Markdown
Contributor

Assessment: Approve (with minor suggestions)

Clean, well-scoped change that replaces the deprecated dual stop mechanism (request_state["stop_event_loop"] + name-based deprecation shim) with a single, clear name-based stop against the vended stop_conversation tool. Docs and tests are updated consistently, and no stale references to the old bidi.tools path remain.

Review Categories
  • Structure/Naming: Moving to vended_tools/ aligns with the existing top-level strands.vended_tools convention—good consistency.
  • Code Quality: A couple of optional refinements (gating stop on tool success, restoring the explanatory comment for the request_state compatibility shim). No blocking issues.
  • Testing: Tests were refactored to assert full event/message objects via single equality checks rather than per-field—exactly the preferred pattern.
  • Process: Verify API-review labeling for this experimental public-API/breaking change (description already contains the needed materials).

Nicely simplified—removing the deprecation branching makes the stop path much easier to follow.

@pgrayy
pgrayy deployed to auto-approve September 28, 2026 19:18 — with GitHub Actions Active
@strands-agents strands-agents deleted a comment from github-actions Bot Sep 28, 2026
@strands-agents strands-agents deleted a comment from github-actions Bot Sep 28, 2026
@strands-agents strands-agents deleted a comment from github-actions Bot Sep 28, 2026
@github-actions

github-actions Bot commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

@pgrayy
pgrayy deployed to auto-approve September 29, 2026 00:45 — with GitHub Actions Active
@pgrayy pgrayy changed the title fix(bidi): restore stop_conversation as the supported stop tool feat(bidi): support cancellation from custom tools Sep 29, 2026
Comment thread strands-py/src/strands/types/agent.py
Comment thread strands-py/src/strands/experimental/bidi/agent/loop.py Outdated
Comment thread strands-py/src/strands/experimental/bidi/agent/agent.py
@github-actions

Copy link
Copy Markdown
Contributor

Assessment: Comment (mostly Approve — one process item to confirm)

This is a solid rework of the earlier revision: replacing the name-based / request_state["stop_event_loop"] stop mechanism with a first-class agent.cancel() on LocalAgent is a cleaner, more discoverable contract, and the deprecated bidi.tools.stop_conversation package is fully removed with no dangling references. My earlier feedback (restoring the request_state compatibility comment; asserting full objects in tests) has been addressed.

Review Categories
  • API surface: cancel() now lands on the public LocalAgent protocol (strands.types.agent), a contract change beyond the experimental namespace — please confirm API review + api/needs-review labeling before merge.
  • Contract clarity: cancel() only takes effect after a tool call completes; for a tool-less BidiAgent it's a permanent no-op. Documented, but worth confirming this is the intended semantics or tracking a follow-up.
  • Concurrency: potential duplicate BidiConnectionStopEvent if concurrent tool batches observe the set signal (pre-existing pattern, low priority).
  • Testing: strong — full-object equality assertions, retry × cancel_source parametrization covers the _should_retry change, and reuse/cleanup is verified. Codecov reports all modified lines covered.

Nice simplification — the single cancel() entry point makes the stop path much easier to reason about.

Keep upstream's reconnect-result delivery behavior and retain the cancellation and shared-state tests.
mehtarac
mehtarac previously approved these changes Sep 29, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Assessment: Comment (mostly Approve — one process item remains)

Re-reviewed after the latest push. This revision improves on the prior one and resolves my concurrency feedback. The stop mechanism is now a clean, single group-level checkpoint, the deprecated stop_conversation tool and stop_event_loop path are fully removed with no dangling references, and reuse is handled by clearing the signal in stop(). Tests are strong.

Review Categories
  • Concurrency (resolved): Stop check moved from per-tool _run_tool to group-level _run_tools, firing once after the group's results are recorded — eliminates the duplicate BidiConnectionStopEvent risk.
  • API surface (open): cancel() on the public LocalAgent protocol plus the _executor._should_retry change (return True → return not agent.cancel_signal.is_set()) make cancel_signal meaningful in the generic executor for all LocalAgent implementations. Public-contract change still lacking api/needs-review; please label + get sign-off before merge.
  • Contract clarity: cancel() remains a no-op for a tool-less BidiAgent, now explicitly documented in the docstring and quickstart. Acceptable given experimental status; noted as a conscious decision.
  • Testing: strong — idempotent double-cancel(), cleanup + reuse (signal cleared, model/IO stopped), and retry × cancel_source (tool/hook) × tool_count parametrization that exercises the _should_retry change. Obsolete stop_event_loop/request_state tests removed; Codecov reports all modified lines covered.

The single group-level cancel() checkpoint makes the stop path much easier to reason about — nice cleanup. The only remaining blocker is the API-review label/sign-off.

@mehtarac
mehtarac merged commit 3e5e89e into strands-agents:main Sep 29, 2026
40 of 44 checks passed
@pgrayy
pgrayy deleted the agent-tasks/bidi-stop-conversation branch September 29, 2026 16:07

This branch was successfully deployed

1 active deployment
auto-approve — 661c6251 Deployed Sep 29, 2026 by pgrayy via Build and Deploy Preview #2160
manual-approval — e53d2443 Deployed Sep 28, 2026 by pgrayy via Build and Deploy Preview #2133
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-bidirectional-streaming Related to bidirectional streaming area-tool Tool behavior/api bug Something isn't working complexity/low Touched functions have low cognitive complexity (<=10) python Pull requests that update python code size/m

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants