You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Support conditional approval from inside AIFunction (trigger approval flow at runtime) #7449
Feature request: Support conditional approval inside AIFunction
Problem:
Currently, using ApprovalRequiredAIFunction in Microsoft.Extensions.AI requires approval every time the function is called, because approval is checked before the function is executed. This does not allow for approval checks that need to be determined by logic inside the function (e.g., only require approval for sensitive arguments).
Proposal:
Add official support for conditional approval, where the function implementation itself can determine (at runtime, based on input) whether approval is required, and trigger the approval flow only in those cases.
Motivation:
Enables more flexible, real-world use cases (e.g. operations on sensitive data, approval for write operation based params etc)
Reduces friction and avoids unnecessary approvals for low-risk operations
Better aligns with user expectations for how function invocation and approval UX should work
Suggested approaches:
Add an API or return type that allows the function to indicate approval is required after inspecting the input, pausing execution until user approval is obtained.
Alternatively, allow returning a special result to trigger the approval flow from inside the function and pass the approval result as the param if approved for the same funcation call with same params as before
Nice to have
Allowing users to edit the param before approving
Reacted by Daniel Roth, Joseph Tossell and Vijay Nirmal
Same need, reached from a different direction: a tool that learns during its invocation that the call needs the user's approval. Adding what we traced in v10.9.0, since the issue describes the goal and not yet why nothing available from inside an invocation gets there.
The approval decision is taken from the model's response before any invocation, streaming or not:
Once a call has been invoked, every processed call is marked informational and a FunctionResultContent is created unconditionally, before Terminate is considered:
So a function that throws, returns a ToolApprovalRequestContent, or sets FunctionInvocationContext.Terminate still ends up with a recorded result, and the user can give the approval and get nothing back: there is no request for a ToolApprovalResponseContent to resume. Rewriting the history afterwards to look like a pre-invocation approval works for client-managed history but depends on internals (the ficc_ request id, the informational flag, the response processor's dedupe), and has no equivalent when the conversation is service-managed.
The shape that would cover this and the cases above: a dedicated exception (say ToolApprovalRequiredException) thrown from the function or from a FunctionInvocationContext middleware, which FunctionInvokingChatClient converts into the same ToolApprovalRequestContent it would have emitted pre-invocation, records no FunctionResultContent for the call, and ends the iteration — in both the client-managed and ConversationId branches.
Activity
findings: conditional tool approval is supported (UseToolApproval + A…
joetossell commentedon Sep 17, 2026
Same need, reached from a different direction: a tool that learns during its invocation that the call needs the user's approval. Adding what we traced in v10.9.0, since the issue describes the goal and not yet why nothing available from inside an invocation gets there.
The approval decision is taken from the model's response before any invocation, streaming or not:
https://github.com/dotnet/extensions/blob/v10.9.0/src/Libraries/Microsoft.Extensions.AI/ChatCompletion/FunctionInvokingChatClient.cs#L362
https://github.com/dotnet/extensions/blob/v10.9.0/src/Libraries/Microsoft.Extensions.AI/ChatCompletion/FunctionInvokingChatClient.cs#L584
Once a call has been invoked, every processed call is marked informational and a
FunctionResultContentis created unconditionally, beforeTerminateis considered:https://github.com/dotnet/extensions/blob/v10.9.0/src/Libraries/Microsoft.Extensions.AI/ChatCompletion/FunctionInvokingChatClient.cs#L1001
https://github.com/dotnet/extensions/blob/v10.9.0/src/Libraries/Microsoft.Extensions.AI/ChatCompletion/FunctionInvokingChatClient.cs#L1203
So a function that throws, returns a
ToolApprovalRequestContent, or setsFunctionInvocationContext.Terminatestill ends up with a recorded result, and the user can give the approval and get nothing back: there is no request for aToolApprovalResponseContentto resume. Rewriting the history afterwards to look like a pre-invocation approval works for client-managed history but depends on internals (theficc_request id, the informational flag, the response processor's dedupe), and has no equivalent when the conversation is service-managed.The shape that would cover this and the cases above: a dedicated exception (say
ToolApprovalRequiredException) thrown from the function or from aFunctionInvocationContextmiddleware, whichFunctionInvokingChatClientconverts into the sameToolApprovalRequestContentit would have emitted pre-invocation, records noFunctionResultContentfor the call, and ends the iteration — in both the client-managed andConversationIdbranches.