Skip to content

HostedToolSearchTool with DeferredTools in the type#7471

Open
jozkee wants to merge 20 commits intomainfrom
copilot/tool-search-v2
Open

HostedToolSearchTool with DeferredTools in the type#7471
jozkee wants to merge 20 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
…r tool search support

Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com>
…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 <2642209+stephentoub@users.noreply.github.com>
…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 <2642209+stephentoub@users.noreply.github.com>
Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com>
…SearchTool private

Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com>
…ation test

Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com>
…s/NonDeferredTools from HostedToolSearchTool

Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com>
Agent-Logs-Url: https://github.com/dotnet/extensions/sessions/7a29d49e-c422-4fe7-81f4-366bd781b460
…ai-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 <16040868+jozkee@users.noreply.github.com>
…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 <223556219+Copilot@users.noreply.github.com>
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 <223556219+Copilot@users.noreply.github.com>
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 <223556219+Copilot@users.noreply.github.com>
…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 <223556219+Copilot@users.noreply.github.com>
…rouping from OpenAI provider

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

Co-authored-by: jozkee <16040868+jozkee@users.noreply.github.com>
…espace grouping from OpenAI provider"

This reverts commit 89d8df4.

Co-authored-by: jozkee <16040868+jozkee@users.noreply.github.com>
… and restore @namespace param name

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

Co-authored-by: jozkee <16040868+jozkee@users.noreply.github.com>
- 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 <223556219+Copilot@users.noreply.github.com>
@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 on lines +15 to +17
/// By default, when a <see cref="HostedToolSearchTool"/> is present in the tools list, all other tools are treated
/// as having deferred loading enabled. Use <see cref="DeferredTools"/> to control which tools have deferred loading
/// on a per-tool basis.
Copy link

Copilot AI Apr 16, 2026

Choose a reason for hiding this comment

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

The remarks say that when HostedToolSearchTool is present “all other tools are treated as having deferred loading enabled”, but the provider logic only applies defer_loading to function tools and MCP server tools (hosted tools like web_search aren’t deferred). Consider tightening the wording to “all function/MCP tools (or all tools that support deferred loading)” to avoid misleading API docs.

Copilot uses AI. Check for mistakes.
Comment on lines +61 to +65
/// When non-null, all deferred function tools are wrapped inside a <c>{"type":"namespace","name":"..."}</c>
/// container. Non-deferred tools remain as top-level tools.
/// </para>
/// <para>
/// When <see langword="null"/> (the default), deferred tools are sent as top-level function tools
Copy link

Copilot AI Apr 16, 2026

Choose a reason for hiding this comment

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

The Namespace property remarks say “all deferred function tools are wrapped” but the OpenAI provider also namespaces deferred MCP server tools (see McpToolNamespaceGrouping tests / responseTool is McpTool branch). Update the docs to include MCP tools (or more generally “deferred tools”) so behavior matches the documentation.

Suggested change
/// When non-null, all deferred function tools are wrapped inside a <c>{"type":"namespace","name":"..."}</c>
/// container. Non-deferred tools remain as top-level tools.
/// </para>
/// <para>
/// When <see langword="null"/> (the default), deferred tools are sent as top-level function tools
/// When non-null, all deferred tools are wrapped inside a <c>{"type":"namespace","name":"..."}</c>
/// container. Non-deferred tools remain as top-level tools.
/// </para>
/// <para>
/// When <see langword="null"/> (the default), deferred tools are sent as top-level tools

Copilot uses AI. Check for mistakes.
Comment on lines +863 to +878
/// <summary>Checks whether any <see cref="HostedToolSearchTool"/> in the options considers the tool deferred.</summary>
private static bool IsDeferredByAnyToolSearch(string toolName, ChatOptions? options)
{
if (options?.Tools is { } tools)
{
foreach (AITool t in tools)
{
if (t is HostedToolSearchTool toolSearch && IsDeferredLoading(toolName, toolSearch))
{
return true;
}
}
}

return false;
}
Copy link

Copilot AI Apr 16, 2026

Choose a reason for hiding this comment

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

The deferred-loading / namespace lookup currently scans options.Tools repeatedly (IsDeferredByAnyToolSearch loops tools for every function/MCP tool, and FindNamespaceForTool loops tools again for each tool during request construction), making tool processing O(n²). Consider precomputing a lookup (e.g., HashSet of deferred tool names + toolName→namespace map) once per request to reduce overhead and simplify the control flow.

Copilot uses AI. Check for mistakes.
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.

3 participants