Python: Fix as_tool() swallowing user_input_request events#4568
Open
giles17 wants to merge 4 commits intomicrosoft:mainfrom
Open
Python: Fix as_tool() swallowing user_input_request events#4568giles17 wants to merge 4 commits intomicrosoft:mainfrom
as_tool() swallowing user_input_request events#4568giles17 wants to merge 4 commits intomicrosoft:mainfrom
Conversation
…#4499) When a sub-agent wrapped with as_tool() emits user_input_request content (e.g. oauth_consent_request), the event was silently dropped because as_tool() only returns .text. This fix propagates user_input_request Content through the tool invocation pipeline to the parent response. Changes: - Add UserInputRequiredException to bridge the str-return boundary - Check response.user_input_requests in as_tool() before returning .text - Add re-raise guards in _auto_invoke_function to prevent the generic except Exception from swallowing the exception - Catch in invoke_with_termination_handling to extract Content items - Expand _handle_function_call_results to detect user_input_request Content Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Member
Python Test Coverage Report •
Python Unit Test Overview
|
||||||||||||||||||||||||||||||||||||||||
Contributor
There was a problem hiding this comment.
Pull request overview
Fixes a gap in the Python tool-wrapping pipeline where Agent.as_tool() would collapse sub-agent output to .text and silently drop user_input_request content (e.g., oauth_consent_request), preventing parent agents/workflows from pausing for required user action.
Changes:
- Introduces
UserInputRequiredExceptionto carry sub-agentuser_input_requestcontents through tool invocation. - Updates
Agent.as_tool()(streaming + non-streaming) to raiseUserInputRequiredExceptionwhenuser_input_requestsare present. - Updates tool invocation handling to re-raise/catch
UserInputRequiredException, surface the requestContentin the parent response, and stop the function loop; adds tests for both as_tool and end-to-end invocation behavior.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| python/packages/core/agent_framework/exceptions.py | Adds UserInputRequiredException to represent “user action required” as a structured control-flow signal. |
| python/packages/core/agent_framework/_agents.py | Changes as_tool() to detect user_input_requests and raise instead of returning collapsed .text. |
| python/packages/core/agent_framework/_tools.py | Ensures the exception isn’t swallowed, converts it into surfaced Content, and stops tool-loop processing when user input is required. |
| python/packages/core/agent_framework/init.py | Exports the new exception from the package surface. |
| python/packages/core/tests/core/test_agents.py | Adds coverage for as_tool() raising in streaming/non-streaming and normal text behavior. |
| python/packages/core/tests/core/test_function_invocation_logic.py | Adds an end-to-end test ensuring user_input_request content reaches the parent response via tool invocation. |
You can also share your feedback on Copilot code review. Take the survey.
…t.id, suppress logging - Propagate all Content items from UserInputRequiredException, not just the first - Set content.id for AgentExecutor request tracking - Change log_level to None (control-flow signal, not an error) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
as_tool() swallowing user_input_request events
as_tool() swallowing user_input_request eventsas_tool() swallowing user_input_request events
eavanvalkenburg
added a commit
to eavanvalkenburg/agent-framework
that referenced
this pull request
Mar 10, 2026
…ons (microsoft#3642) Audit and refactor public **kwargs usage across core agents, chat clients, tools, sessions, and provider packages per the migration strategy codified in CODING_STANDARD.md. Key changes: - Add explicit runtime buckets: function_invocation_kwargs and client_kwargs on RawAgent.run() and chat client get_response() layers. - Refactor FunctionTool to prefer explicit ctx: FunctionInvocationContext injection; legacy **kwargs tools still work via _forward_runtime_kwargs. - Refactor Agent.as_tool() to use direct JSON schema, always-streaming wrapper, approval_mode parameter, and UserInputRequiredException propagation (integrates PR microsoft#4568 behavior). - Remove implicit session bleeding into FunctionInvocationContext; tools that need a session must receive it via function_invocation_kwargs. - Lower chat-client layers after FunctionInvocationLayer accept only compatibility **kwargs (client_kwargs flattened, function_invocation_kwargs ignored). - Add layered docstring composition from Raw... implementations via _docstrings.py helper. - Clean up provider constructors to use explicit additional_properties. - Deprecation warnings on legacy direct kwargs paths. - Update samples, tests, and typing across all 23 packages. Resolves microsoft#3642 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
4 tasks
eavanvalkenburg
added a commit
to eavanvalkenburg/agent-framework
that referenced
this pull request
Mar 10, 2026
…ons (microsoft#3642) Audit and refactor public **kwargs usage across core agents, chat clients, tools, sessions, and provider packages per the migration strategy codified in CODING_STANDARD.md. Key changes: - Add explicit runtime buckets: function_invocation_kwargs and client_kwargs on RawAgent.run() and chat client get_response() layers. - Refactor FunctionTool to prefer explicit ctx: FunctionInvocationContext injection; legacy **kwargs tools still work via _forward_runtime_kwargs. - Refactor Agent.as_tool() to use direct JSON schema, always-streaming wrapper, approval_mode parameter, and UserInputRequiredException propagation (integrates PR microsoft#4568 behavior). - Remove implicit session bleeding into FunctionInvocationContext; tools that need a session must receive it via function_invocation_kwargs. - Lower chat-client layers after FunctionInvocationLayer accept only compatibility **kwargs (client_kwargs flattened, function_invocation_kwargs ignored). - Add layered docstring composition from Raw... implementations via _docstrings.py helper. - Clean up provider constructors to use explicit additional_properties. - Deprecation warnings on legacy direct kwargs paths. - Update samples, tests, and typing across all 23 packages. Resolves microsoft#3642 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
eavanvalkenburg
added a commit
to eavanvalkenburg/agent-framework
that referenced
this pull request
Mar 10, 2026
…ons (microsoft#3642) Audit and refactor public **kwargs usage across core agents, chat clients, tools, sessions, and provider packages per the migration strategy codified in CODING_STANDARD.md. Key changes: - Add explicit runtime buckets: function_invocation_kwargs and client_kwargs on RawAgent.run() and chat client get_response() layers. - Refactor FunctionTool to prefer explicit ctx: FunctionInvocationContext injection; legacy **kwargs tools still work via _forward_runtime_kwargs. - Refactor Agent.as_tool() to use direct JSON schema, always-streaming wrapper, approval_mode parameter, and UserInputRequiredException propagation (integrates PR microsoft#4568 behavior). - Remove implicit session bleeding into FunctionInvocationContext; tools that need a session must receive it via function_invocation_kwargs. - Lower chat-client layers after FunctionInvocationLayer accept only compatibility **kwargs (client_kwargs flattened, function_invocation_kwargs ignored). - Add layered docstring composition from Raw... implementations via _docstrings.py helper. - Clean up provider constructors to use explicit additional_properties. - Deprecation warnings on legacy direct kwargs paths. - Update samples, tests, and typing across all 23 packages. Resolves microsoft#3642 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Contributor
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.
You can also share your feedback on Copilot code review. Take the survey.
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.
Motivation and Context
Fixes #4499.
When a sub-agent wrapped with
as_tool()emitsuser_input_requestcontent (e.g.oauth_consent_requestfrom PR #4197), the event is silently dropped. Both code paths inas_tool()collapse the response to.text, so the caller receives an empty string with no indication that user action is required.Description
This fix propagates
user_input_requestContent items from sub-agents through the tool invocation pipeline to the parent agent's response, leveraging the existinguser_input_request→AgentExecutor→ctx.request_info()mechanism.New exception:
UserInputRequiredException(exceptions.py)ToolException; carriescontents: list[Any]from the sub-agentMiddlewareTermination— a structured exception that escapes the genericexcept Exceptioncatch-all to alter control flowas_tool()changes (_agents.py)response.user_input_requestsbefore returning.textUserInputRequiredExceptionif non-emptyTool invocation pipeline changes (
_tools.py)except UserInputRequiredException: raiseguards in_auto_invoke_function(direct + middleware paths) prevent the genericexcept Exceptionfrom swallowing the signalinvoke_with_termination_handlingcatches the exception and extracts the Content items_handle_function_call_resultsexpanded to detect any Content withuser_input_request=True, returning"action": "return"to stop the function invocation loop and surface the Content in the parent responseEvent flow after fix:
Contribution Checklist