Skip to content

Fix FunctionInvokingChatClient to respect ChatOptions.Tools modifications by function tools#7218

Merged
stephentoub merged 9 commits intomainfrom
copilot/fix-tool-updates-in-client
Jan 24, 2026
Merged

Fix FunctionInvokingChatClient to respect ChatOptions.Tools modifications by function tools#7218
stephentoub merged 9 commits intomainfrom
copilot/fix-tool-updates-in-client

Conversation

Copy link
Contributor

Copilot AI commented Jan 20, 2026

  • Understand the issue: FunctionInvokingChatClient caches toolMap at the start of the function loop, but functions can modify ChatOptions.Tools during invocation
  • Initial fix: Added NeedsToolMapRefresh method to detect when tools have changed
  • Reviewer feedback: @stephentoub suggested removing the map entirely and using linear search instead
  • Implement simplified approach:
    • Removed CreateToolsMap and NeedsToolMapRefresh methods
    • Added FindTool helper for linear search through tool lists
    • Added AnyToolsRequireApproval helper to check if any tool requires approval
    • Added HasAnyTools helper to check if there are any tools
    • Updated all callers to use direct tool list searches instead of map lookups
  • Run tests to validate changes (all 66 FunctionInvokingChatClient tests pass)
  • Address additional review feedback:
    • Removed unnecessary initial anyToolsRequireApproval assignment before loops
    • Simplified FindTool to use forward iteration (earlier lists take precedence)
    • Added AIFunctionDeclaration check in FindTool to only match describable functions
    • Simplified approval check using |= operator
    • Added tests for approval-required function add/replace scenarios
    • Changed FindTool return type from AITool? to AIFunctionDeclaration? for more specific typing
    • Moved approvalRequiredFunctions declaration inside the loop to ensure it reflects tool modifications
    • Updated test comment to remove outdated "tool map" terminology
Original prompt

This section details on the original issue you should resolve

<issue_title>FunctionInvokingChatClient should respect ChatOptions.Tools modifications by function tool</issue_title>
<issue_description>FunctionInvokingChatClient provides a FunctionInvocationContext that contains ChatOptions when invoking function tools.

If one of the executed functions modifies the Tools list on ChatOptions, this updated set of tools will be advertised to the downstream ChatClient. However, FunctionInvokingChatClient does not respect these updates when invoking subsequent functions, which can cause failures. FunctionInvokingChatClient currently caches a dictionary of tool name to tool, which is calculated at the start of the function loop, and before any modifications might happen.

Here is a step by step workflow explaining how a failure may happen:

  1. FICC is called, and it calls the downstream ChatClient.
  2. FICC gets a FCC back and FICC calls the appropriate Function tool (lets say Function A)
  3. Function A modifies ChatOptions.Tools on FunctionInvocationContext by adding Function B to the list.
  4. FICC calls the downstream ChatClient.
  5. FICC gets a FCC back for Function B.
  6. FICC fails to invoke Function B, saying that it cannot find Function B

Also see microsoft/agent-framework#3083</issue_description>

<agent_instructions>Consider fixing this by refreshing the tool map before processing functions if it's not the initial set of invocations (in which case the tool map will by definition be up to date) or if the current tools have become inconsistent with the tool map. I'm hoping this is as simple as an O(N) traversal of the current tools validating that they're all there and that there aren't other tools in the map that would indicate tools were removed from ChatOptions.Tools. Please make sure this is very well tested, including cases where tools are added, removed, reordered, etc. during function invocation, both when there are AdditionalTools and not. We want to make this as efficient as possible, so we don't want to recompute the tools map unnecessarily, but we also need it to be functionally correct, so the tools map must always be accurate when querying it.</agent_instructions>

Comments on the Issue (you are @copilot in this section)


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Microsoft Reviewers: Open in CodeFlow

Copilot AI and others added 2 commits January 20, 2026 13:35
…ions by function tools

Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com>
Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix FunctionInvokingChatClient to respect ChatOptions.Tools modifications Fix FunctionInvokingChatClient to respect ChatOptions.Tools modifications by function tools Jan 20, 2026
Copilot AI requested a review from stephentoub January 20, 2026 13:43
@stephentoub
Copy link
Member

@copilot, would it make more sense / be cleaner to just get rid of the map entirely?

I believe we added it originally out of concern for the case where there are lots of tools and lots of function requests, but it's rare to have more than a few function requests in a single response, if we consider the number of tools to be a constant controlled by the client then we're still O(N) in the number of tool requests, and having to keep the map up to date adds even more overhead that the 90% case is having to pay.

Per reviewer feedback, removed the Dictionary-based tool map entirely
and replaced it with linear search through AdditionalTools and
options.Tools. This simplifies the code and removes the overhead of
maintaining/refreshing the map, which is acceptable since:
- Most function invocations have only a few function calls
- Tool lists are typically small
- Linear search avoids dictionary allocation and synchronization overhead

Added helper methods:
- FindTool: Linear search for a tool by name
- AnyToolsRequireApproval: Checks if any tool requires approval
- HasAnyTools: Checks if there are any tools in the lists

Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Jan 21, 2026

@copilot, would it make more sense / be cleaner to just get rid of the map entirely?

I believe we added it originally out of concern for the case where there are lots of tools and lots of function ...

Good point. Removed the map entirely in fc8c227. Now using linear search through AdditionalTools and options.Tools via a FindTool helper method. This simplifies the code and naturally handles tool modifications since we search the current lists on each lookup.

@stephentoub
Copy link
Member

@copilot try again

… add approval tests

- Remove unnecessary initial anyToolsRequireApproval assignment before the loop (lines 289 and 431)
- Simplify FindTool to use forward iteration (earlier lists take precedence)
- Add AIFunctionDeclaration check in FindTool to only match tools that can be described as functions
- Simplify approval check in ReplaceFunctionCallsWithApprovalRequests using |= operator
- Update all call sites to pass options.Tools first, AdditionalTools second
- Add tests for approval-required functions being added and replaced during invocation

Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Jan 23, 2026

@copilot try again

Addressed all the review feedback in a835320:

  • Removed unnecessary initial anyToolsRequireApproval assignments before the loops
  • Simplified FindTool to use forward iteration with earlier lists taking precedence
  • Added AIFunctionDeclaration check in FindTool to only match describable function tools
  • Simplified the approval check using |= operator
  • Added tests for approval-required functions being added and replaced during invocation

All 70 FunctionInvokingChatClient tests pass.

Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com>
@stephentoub stephentoub marked this pull request as ready for review January 23, 2026 21:13
This was referenced Feb 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

FunctionInvokingChatClient should respect ChatOptions.Tools modifications by function tool

3 participants