Skip to content

.NET: automatic rejection of unanswered approvals tells the model the call "must not be retried" #9656

Open

Description

@tdoman

Since 1.25, ApprovalResponseBindingChatClient rejects each saved approval request that was left unanswered on the next run (#9152, ADR 0045). The ADR says: "An answer arriving after a successful automatic rejection cannot authorize the old call. The model can still request a new call and ask for approval again."

With Microsoft.Extensions.AI 10.11.0, FunctionInvokingChatClient prefixes every rejection with "Tool call invocation was rejected by the approver and must not be retried." (dotnet/extensions#7784, for dotnet/extensions#7783). The model then sees this for an automatic rejection:

Tool call invocation was rejected by the approver and must not be retried. No approval response was provided for this approval required tool call.

No approver made a decision, and the model is told not to do what the ADR says it may do. If the user's next message still asks for the action, a model following the result won't request it again, so the user can't get a fresh approval without rephrasing.

Repro: MAF 1.25.0 with Microsoft.Extensions.AI 10.11.0, using the default chat-client stack with an approval-required tool:

  1. The model calls the tool and the run returns the approval request.
  2. Send a plain user message instead of an approval response.
  3. The function result the model receives is the text above. With Microsoft.Extensions.AI 10.10.x it was "Tool call invocation rejected. No approval response was provided for this approval required tool call."

Possible directions:

  • Have the binding's generated rejections reach the model without the approver framing, for example as a function result MAF writes itself.
  • Or work with Microsoft.Extensions.AI on a way for a rejection to supply its own result text, so the "final decision" prefix applies only to an actual approver's decision.

We've held Microsoft.Extensions.AI at 10.10.x until there's a way to give automatic rejections a non-final message.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    .NETUsage: [Issues, PRs], Target: .NettriageUsage: [Issues], Target: All issues that still need to be triaged

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions