Skip to content

HostedToolSearchTool with DeferredTools in the type#7471

Merged
jozkee merged 28 commits intomainfrom
copilot/tool-search-v2
Apr 23, 2026
Merged

HostedToolSearchTool with DeferredTools in the type#7471
jozkee merged 28 commits intomainfrom
copilot/tool-search-v2

Conversation

@jozkee
Copy link
Copy Markdown
Member

@jozkee jozkee commented Apr 16, 2026

@rogerbarreto

Alternative to #7377.

Propose HostedToolSearchTool with the tools specified in the type as an IList<string>? DeferredTools.

  • This should allow us to nicely support MCP servers too by shoving the Server Name in the lists.
  • We lose the ability to specify defer_loading with a decorator, defer_loading is really not important for local tool search since returned tools are better not included at all until tool search finds it.
  • Namespace grouping would be handled by passing multiple HostedToolSearchTools. That seems like a rough edge but not terrible, I'm wondering if it would be better to wait until we have a built-in NamespaceTool.
Microsoft Reviewers: Open in CodeFlow

Copilot AI and others added 20 commits April 3, 2026 14:37
…rredTools/NonDeferredTools

- Redesign HostedToolSearchTool with DeferredTools/NonDeferredTools properties
- Remove SearchableAIFunctionDeclaration (no longer needed)
- Revert DelegatingAIFunctionDeclaration to internal
- Update OpenAI provider: use HostedToolSearchTool enable/disable logic for defer_loading
- Add ChatOptions parameter to AsOpenAIResponseTool extension method
- Use AOT-safe ModelReaderWriter.Read with OpenAIContext.Default
- Update API baselines and tests

Co-authored-by: stephentoub <[email protected]>
…tor ToResponseTool signature

- Fix O(N²) by finding HostedToolSearchTool once before the tools loop
  instead of scanning the list for each tool
- Remove HostedToolSearchTool from json baseline (experimental types
  don't need entries)
- Refactor ToResponseTool(AITool, ...) to take HostedToolSearchTool?
  directly instead of extracting from ChatOptions each time
- Remove FindToolSearchTool helper method (inlined into callers)

Co-authored-by: stephentoub <[email protected]>
…gTools from HostedMcpServerTool, add interaction tests

- Remove Namespace property and namespaceName parameter from SearchableAIFunctionDeclaration
- Remove DeferLoadingTools property from HostedMcpServerTool
- Update OpenAIResponsesChatClient to drop namespace patching and MCP defer_loading patching
- Update RemoteMCP_DeferLoadingTools integration test to use AsOpenAIResponseTool() + Patch.Set + AsAITool()
- Add tool_search_call/tool_search_output assertions to integration test
- Add SearchableAIFunctionDeclaration + ApprovalRequiredAIFunction interaction tests
- Add FunctionInvokingChatClient test for approval detection through searchable wrapper

Co-authored-by: Copilot <[email protected]>
Remove [Experimental(AIToolSearch)] attribute from DelegatingAIFunctionDeclaration,
SearchableAIFunctionDeclaration, and HostedToolSearchTool. Remove the AIToolSearch
diagnostic constant from DiagnosticIds. Add API baseline entries for all three types
in Microsoft.Extensions.AI.Abstractions.json. Clean up unused usings.

Co-authored-by: Copilot <[email protected]>
Add tests verifying that the OpenAI API returns HTTP 400 when
HostedToolSearchTool is used without any deferred tools:
- UseToolSearch_OnlyToolSearchNoFunctions
- UseToolSearch_WithNonDeferredFunctionsOnly

Co-authored-by: Copilot <[email protected]>
…arch-support

# Conflicts:
#	src/Libraries/Microsoft.Extensions.AI.Abstractions/Microsoft.Extensions.AI.Abstractions.json
…e grouping in OpenAIResponsesChatClient

- Add Namespace property and namespaceName parameter to SearchableAIFunctionDeclaration
- Add namespaceName parameter to CreateToolSet for bulk namespace assignment
- Add namespace grouping logic in OpenAIResponsesChatClient tools loop
- Add ToNamespaceResponseTool helper using ModelReaderWriter for AOT-safe JSON
- Add namespace unit tests in SearchableAIFunctionDeclarationTests
- Add namespace VerbatimHttpHandler tests in OpenAIResponseClientTests
- Add UseToolSearch_WithNamespace integration test with tool_search assertions
- Add tool_search_call/tool_search_output assertions to existing integration test
- Update API baseline

Co-authored-by: Copilot <[email protected]>
…espace grouping from OpenAI provider"

This reverts commit 89d8df4.

Co-authored-by: jozkee <[email protected]>
- Replace per-tool SearchableAIFunctionDeclaration with collections-based
  DeferredTools and Namespace properties on HostedToolSearchTool
- Support multiple HostedToolSearchTool instances with different/same
  namespaces (same namespace merges, first-claims wins)
- Patch defer_loading on both FunctionTool and McpTool when claimed by
  a HostedToolSearchTool
- Group deferred FunctionTool and McpTool into namespace containers
- Widen ToNamespaceResponseTool to accept any ResponseTool
- Add AsOpenAIResponseTool ChatOptions parameter for defer context
- Delete SearchableAIFunctionDeclaration and its tests
- Make DelegatingAIFunctionDeclaration internal (no external consumers)
- Add conversion, VerbatimHttpHandler, and integration tests

Co-authored-by: Copilot <[email protected]>
@jozkee jozkee requested a review from a team as a code owner April 16, 2026 17:37
Copilot AI review requested due to automatic review settings April 16, 2026 17:37
@github-actions github-actions Bot added the area-ai Microsoft.Extensions.AI libraries label Apr 16, 2026
@jozkee jozkee changed the title Copilot/tool search v2 HostedToolSearchTool with DeferredTools in the type Apr 16, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a revised “tool_search” implementation for OpenAI Responses that enables deferred tool loading and optional namespace grouping via a new HostedToolSearchTool abstraction, with extensive unit/integration test coverage.

Changes:

  • Introduces HostedToolSearchTool with IList<string>? DeferredTools and optional Namespace for grouping deferred tools.
  • Updates OpenAI Responses tool conversion to emit tool_search, patch defer_loading, and wrap deferred tools in {"type":"namespace"} containers when configured.
  • Expands unit/integration tests to validate request JSON and runtime behavior for deferred loading, namespaces, and MCP scenarios.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
test/Libraries/Microsoft.Extensions.AI.OpenAI.Tests/OpenAIResponseClientTests.cs Adds VerbatimHttpHandler request-shape tests covering tool_search, deferred loading, namespaces, and MCP grouping.
test/Libraries/Microsoft.Extensions.AI.OpenAI.Tests/OpenAIResponseClientIntegrationTests.cs Adds live integration coverage for tool_search behavior (including MCP + RawRepresentation assertions).
test/Libraries/Microsoft.Extensions.AI.OpenAI.Tests/OpenAIConversionTests.cs Adds conversion tests for tool_search emission and defer-loading patching based on ChatOptions.
test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/Tools/HostedToolSearchToolTests.cs Adds basic round-trip tests for HostedToolSearchTool properties.
src/Libraries/Microsoft.Extensions.AI.OpenAI/OpenAIResponsesChatClient.cs Implements HostedToolSearchTool conversion + defer-loading patching + namespace tool container construction.
src/Libraries/Microsoft.Extensions.AI.OpenAI/MicrosoftExtensionsAIResponsesExtensions.cs Extends AsOpenAIResponseTool(AITool) to accept optional ChatOptions for conversion context.
src/Libraries/Microsoft.Extensions.AI.OpenAI/Microsoft.Extensions.AI.OpenAI.json Updates API contract for the changed extension method signature (optional ChatOptions).
src/Libraries/Microsoft.Extensions.AI.Abstractions/Tools/HostedToolSearchTool.cs Adds the new marker tool type and public surface (DeferredTools, Namespace).
src/Libraries/Microsoft.Extensions.AI.Abstractions/Microsoft.Extensions.AI.Abstractions.json Adds the new public API surface for HostedToolSearchTool.

Comment thread src/Libraries/Microsoft.Extensions.AI.Abstractions/Tools/HostedToolSearchTool.cs Outdated
Comment thread src/Libraries/Microsoft.Extensions.AI.Abstractions/Tools/HostedToolSearchTool.cs Outdated
Comment thread src/Libraries/Microsoft.Extensions.AI.OpenAI/OpenAIResponsesChatClient.cs Outdated
@jozkee
Copy link
Copy Markdown
Member Author

jozkee commented Apr 16, 2026

@copilot address the feedback

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 16, 2026

@copilot address the feedback

Addressed in 9a3e39d (docs wording) and on current branch tip for the remaining reviewer feedback. No UI changes to screenshot.

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 16, 2026

@copilot address the feedback

Addressed in cc2e021: updated HostedToolSearchTool XML docs to match deferred-loading behavior (including MCP/deferred tools wording), and refactored OpenAIResponsesChatClient to precompute deferred/namespace lookups once per request instead of repeatedly scanning tools. Targeted tool-search tests are passing.

Verify that tools like HostedCodeInterpreterTool are unaffected by
DeferredTools and namespace grouping: no defer_loading is patched
and the tool stays top-level in the request.

Co-authored-by: Copilot <[email protected]>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 3 comments.

@jozkee jozkee requested a review from a team as a code owner April 21, 2026 17:51
jozkee and others added 3 commits April 21, 2026 15:36
The ChatOptions? parameter was added so the standalone extension method could honor HostedToolSearchTool.DeferredTools when converting an AITool outside the chat client. In practice, this introduces an O(N^2) hazard if called in a loop (each call rebuilds the ToolSearchLookup) and adds API surface for a niche escape-hatch scenario. The primary path (GetResponseAsync) shares a single lookup and is unaffected.

Future per-tool deferral mechanisms (e.g. a SearchableAITool decorator or a DeferLoading bool on AITool) would obsolete this parameter entirely.

Callers building ResponseTool lists manually can still set defer_loading via Patch.Set as needed.

Removes 11 conversion-level unit tests whose coverage is already provided end-to-end by VerbatimHttpHandler-based tests in OpenAIResponseClientTests.cs.

Co-authored-by: Copilot <[email protected]>
Copy link
Copy Markdown
Member

@jeffhandley jeffhandley left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. New experimental APIs, room to get feedback, and reasonable workarounds for OpenAI gaps.

@jozkee jozkee merged commit bc2258f into main Apr 23, 2026
6 checks passed
@jozkee jozkee deleted the copilot/tool-search-v2 branch April 23, 2026 21:32
jeffhandley pushed a commit that referenced this pull request May 1, 2026
* Implement HostedToolSearchTool and SearchableAIFunctionDeclaration for tool search support

Co-authored-by: stephentoub <[email protected]>

* Redesign: consolidate tool search into HostedToolSearchTool with DeferredTools/NonDeferredTools

- Redesign HostedToolSearchTool with DeferredTools/NonDeferredTools properties
- Remove SearchableAIFunctionDeclaration (no longer needed)
- Revert DelegatingAIFunctionDeclaration to internal
- Update OpenAI provider: use HostedToolSearchTool enable/disable logic for defer_loading
- Add ChatOptions parameter to AsOpenAIResponseTool extension method
- Use AOT-safe ModelReaderWriter.Read with OpenAIContext.Default
- Update API baselines and tests

Co-authored-by: stephentoub <[email protected]>

* Address review feedback: fix O(N²), remove json baseline entry, refactor ToResponseTool signature

- Fix O(N²) by finding HostedToolSearchTool once before the tools loop
  instead of scanning the list for each tool
- Remove HostedToolSearchTool from json baseline (experimental types
  don't need entries)
- Refactor ToResponseTool(AITool, ...) to take HostedToolSearchTool?
  directly instead of extracting from ChatOptions each time
- Remove FindToolSearchTool helper method (inlined into callers)

Co-authored-by: stephentoub <[email protected]>

* Extract shared FindToolSearchTool helper to deduplicate lookup code

Co-authored-by: stephentoub <[email protected]>

* Simplify ToResponseTool: add ChatOptions-only overload, make FindToolSearchTool private

Co-authored-by: stephentoub <[email protected]>

* Add unit tests for HostedToolSearchTool JSON serialization and integration test

Co-authored-by: stephentoub <[email protected]>

* Revert to SearchableAIFunctionDeclaration design, remove DeferredTools/NonDeferredTools from HostedToolSearchTool

Co-authored-by: stephentoub <[email protected]>
Agent-Logs-Url: https://github.com/dotnet/extensions/sessions/7a29d49e-c422-4fe7-81f4-366bd781b460

* Address review feedback: rename namespaceName to @namespace, add openai-dotnet#1053 comment, add DeferLoadingTools to HostedMcpServerTool

Agent-Logs-Url: https://github.com/dotnet/extensions/sessions/08f652ed-169c-43c3-a247-829ebd0b3e4f

Co-authored-by: jozkee <[email protected]>

* Update tests

* revert namespace param rename

* Remove Namespace from SearchableAIFunctionDeclaration and DeferLoadingTools from HostedMcpServerTool, add interaction tests

- Remove Namespace property and namespaceName parameter from SearchableAIFunctionDeclaration
- Remove DeferLoadingTools property from HostedMcpServerTool
- Update OpenAIResponsesChatClient to drop namespace patching and MCP defer_loading patching
- Update RemoteMCP_DeferLoadingTools integration test to use AsOpenAIResponseTool() + Patch.Set + AsAITool()
- Add tool_search_call/tool_search_output assertions to integration test
- Add SearchableAIFunctionDeclaration + ApprovalRequiredAIFunction interaction tests
- Add FunctionInvokingChatClient test for approval detection through searchable wrapper

Co-authored-by: Copilot <[email protected]>

* Remove [Experimental] from tool search types and add API baselines

Remove [Experimental(AIToolSearch)] attribute from DelegatingAIFunctionDeclaration,
SearchableAIFunctionDeclaration, and HostedToolSearchTool. Remove the AIToolSearch
diagnostic constant from DiagnosticIds. Add API baseline entries for all three types
in Microsoft.Extensions.AI.Abstractions.json. Clean up unused usings.

Co-authored-by: Copilot <[email protected]>

* Add integration tests for tool search edge cases

Add tests verifying that the OpenAI API returns HTTP 400 when
HostedToolSearchTool is used without any deferred tools:
- UseToolSearch_OnlyToolSearchNoFunctions
- UseToolSearch_WithNonDeferredFunctionsOnly

Co-authored-by: Copilot <[email protected]>

* Add namespace support to SearchableAIFunctionDeclaration and namespace grouping in OpenAIResponsesChatClient

- Add Namespace property and namespaceName parameter to SearchableAIFunctionDeclaration
- Add namespaceName parameter to CreateToolSet for bulk namespace assignment
- Add namespace grouping logic in OpenAIResponsesChatClient tools loop
- Add ToNamespaceResponseTool helper using ModelReaderWriter for AOT-safe JSON
- Add namespace unit tests in SearchableAIFunctionDeclarationTests
- Add namespace VerbatimHttpHandler tests in OpenAIResponseClientTests
- Add UseToolSearch_WithNamespace integration test with tool_search assertions
- Add tool_search_call/tool_search_output assertions to existing integration test
- Update API baseline

Co-authored-by: Copilot <[email protected]>

* Feedback

* Remove Namespace from SearchableAIFunctionDeclaration and namespace grouping from OpenAI provider

Agent-Logs-Url: https://github.com/dotnet/extensions/sessions/a0e8d299-9f73-4db3-be63-674f590aa717

Co-authored-by: jozkee <[email protected]>

* Revert "Remove Namespace from SearchableAIFunctionDeclaration and namespace grouping from OpenAI provider"

This reverts commit 89d8df4.

Co-authored-by: jozkee <[email protected]>

* Fix CI: replace KeyValuePair deconstruction for net462/netstandard2.0 and restore @namespace param name

Agent-Logs-Url: https://github.com/dotnet/extensions/sessions/1783bbad-9689-4b08-bfcc-79988248c2ff

Co-authored-by: jozkee <[email protected]>

* Add DeferredTools/Namespace on HostedToolSearchTool, support MCP tools

- Replace per-tool SearchableAIFunctionDeclaration with collections-based
  DeferredTools and Namespace properties on HostedToolSearchTool
- Support multiple HostedToolSearchTool instances with different/same
  namespaces (same namespace merges, first-claims wins)
- Patch defer_loading on both FunctionTool and McpTool when claimed by
  a HostedToolSearchTool
- Group deferred FunctionTool and McpTool into namespace containers
- Widen ToNamespaceResponseTool to accept any ResponseTool
- Add AsOpenAIResponseTool ChatOptions parameter for defer context
- Delete SearchableAIFunctionDeclaration and its tests
- Make DelegatingAIFunctionDeclaration internal (no external consumers)
- Add conversion, VerbatimHttpHandler, and integration tests

Co-authored-by: Copilot <[email protected]>

* Address tool-search reviewer feedback for docs and lookup performance

Agent-Logs-Url: https://github.com/dotnet/extensions/sessions/cc5b7c31-3c0f-448e-95c6-2474c2327715

Co-authored-by: jozkee <[email protected]>

* Refine HostedToolSearchTool docs to use deferrable tool wording

Agent-Logs-Url: https://github.com/dotnet/extensions/sessions/8f9ae606-8c21-4af0-b43d-74a2b463ed33

Co-authored-by: jozkee <[email protected]>

* Optimize tool-search lookup precomputation in OpenAI responses client

Agent-Logs-Url: https://github.com/dotnet/extensions/sessions/cc5b7c31-3c0f-448e-95c6-2474c2327715

Co-authored-by: jozkee <[email protected]>

* Add tests for non-deferrable tools with HostedToolSearchTool

Verify that tools like HostedCodeInterpreterTool are unaffected by
DeferredTools and namespace grouping: no defer_loading is patched
and the tool stays top-level in the request.

Co-authored-by: Copilot <[email protected]>

* Bring back Experimental

* Address test feedback

* Drop ChatOptions parameter from AsOpenAIResponseTool(AITool)

The ChatOptions? parameter was added so the standalone extension method could honor HostedToolSearchTool.DeferredTools when converting an AITool outside the chat client. In practice, this introduces an O(N^2) hazard if called in a loop (each call rebuilds the ToolSearchLookup) and adds API surface for a niche escape-hatch scenario. The primary path (GetResponseAsync) shares a single lookup and is unaffected.

Future per-tool deferral mechanisms (e.g. a SearchableAITool decorator or a DeferLoading bool on AITool) would obsolete this parameter entirely.

Callers building ResponseTool lists manually can still set defer_loading via Patch.Set as needed.

Removes 11 conversion-level unit tests whose coverage is already provided end-to-end by VerbatimHttpHandler-based tests in OpenAIResponseClientTests.cs.

Co-authored-by: Copilot <[email protected]>

---------

Co-authored-by: copilot-swe-agent[bot] <[email protected]>
Co-authored-by: stephentoub <[email protected]>
Co-authored-by: Copilot <[email protected]>
@shouyuan-timesoftsg
Copy link
Copy Markdown

shouyuan-timesoftsg commented May 4, 2026

May I ask if this PR addresses this issue?

@jozkee
Copy link
Copy Markdown
Member Author

jozkee commented May 4, 2026

@shouyuan-timesoftsg it adds the tool search tool in its hosted/remote flavor to Microsoft.Extensions.AI.Abstractions and implements it in Microsoft.Extensions.AI.OpenAI.

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

Labels

area-ai Microsoft.Extensions.AI libraries

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants