feat(python): plumb AbortSignal through ToolInvocation for handler cancellation#1703
Open
gimenete wants to merge 4 commits into
Open
feat(python): plumb AbortSignal through ToolInvocation for handler cancellation#1703gimenete wants to merge 4 commits into
gimenete wants to merge 4 commits into
Conversation
…ncellation Add cooperative cancellation support to the Python SDK so tool handlers can detect and respond to session.abort() calls. Changes: - Add AbortSignal class with is_aborted property and wait() coroutine - Add AbortController class to manage and trigger AbortSignal - Add signal field to ToolInvocation (defaults to a fresh AbortSignal) - Add _tool_abort_controller to CopilotSession; thread signal through _execute_tool_and_respond so every handler receives the current signal - Update abort() to trigger in-flight handlers then reset the controller - Update abort() docstring to document handler cancellation behavior - Export AbortSignal from the top-level copilot package - Add tests: is_aborted initially False, True after abort, wait() resolves, ToolInvocation carries AbortSignal, handler receives correct signal Closes github#1433 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Contributor
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Adds cooperative cancellation support for tool handlers by introducing an abort signal propagated from CopilotSession.abort() into each ToolInvocation.
Changes:
- Introduces
AbortSignal/AbortControllerand addsToolInvocation.signalfor cooperative cancellation. - Propagates a session-scoped abort signal into tool invocations and triggers it on session abort (then resets for future tool calls).
- Adds unit tests validating abort signal behavior and propagation to tool handlers.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| python/copilot/tools.py | Adds AbortSignal/AbortController and attaches an abort signal to ToolInvocation. |
| python/copilot/session.py | Wires a session-level abort controller into tool invocation execution and triggers it from abort(). |
| python/copilot/init.py | Exposes AbortSignal in the public package API. |
| python/test_tools.py | Adds tests for abort signal semantics and propagation via ToolInvocation. |
…all() Replaces the single shared AbortController with per-call tracking in _in_flight_tool_calls so individual handlers can be cancelled without aborting the agentic loop. Changes: - Replace _tool_abort_controller with _in_flight_tool_calls dict keyed by tool_call_id; each _execute_tool_and_respond creates its own controller, stores it, and removes it in a finally block (with recycling guard) - abort() now aborts every in-flight controller via _abort_in_flight_tool_calls() - disconnect() aborts in-flight tool calls before sending session.destroy - Add cancel_tool_call(tool_call_id) -> bool: signals only the targeted handler, returns True if found, False otherwise - Add _abort_in_flight_tool_calls() private helper - Add TestCancelToolCall tests: returns False for unknown id, returns True and sets signal for known id, and aborts ONLY the targeted handler while leaving sibling handlers unaffected Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Address Copilot review feedback: the previous docstring implied the signal is always tied to CopilotSession.abort(), but manually-constructed ToolInvocation() instances receive an independent AbortSignal that the session never triggers. Updated docstring now explains: - Session-dispatched invocations: signal is set to the session's managed AbortSignal, triggered by abort() or cancel_tool_call() - Manually-constructed invocations (e.g. in tests): each instance gets its own independent signal unless one is injected explicitly Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ibility Change signal field type from AbortSignal (with default_factory) to AbortSignal | None = None so the new field is fully optional and does not change the construction contract for existing code. The session always sets a real AbortSignal when dispatching tools; manually-constructed invocations now default to None. Handlers that consume the signal should guard for None or inject one explicitly. Update tests accordingly: signal defaults to None, and an injected signal is honored and aborts correctly. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Port of the Node.js AbortSignal implementation (#1433) to the Python SDK.
Adds cooperative cancellation support so tool handlers can detect and respond to
session.abort()calls.Changes
AbortSignalclass withis_abortedproperty andawait signal.wait()coroutineAbortControllerclass that creates and manages anAbortSignalToolInvocation.signalfield — receives the session's currentAbortSignalCopilotSession.abort()triggers all in-flight handlers via the signal, then resets the controller for future tool callsAbortSignalexported from the top-levelcopilotpackageUsage
Backwards compatible
Handlers that don't consume the signal continue to work unchanged.
Closes #1433