HostedToolSearchTool with DeferredTools in the type#7471
HostedToolSearchTool with DeferredTools in the type#7471
Conversation
…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>
There was a problem hiding this comment.
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
HostedToolSearchToolwithIList<string>? DeferredToolsand optionalNamespacefor grouping deferred tools. - Updates OpenAI Responses tool conversion to emit
tool_search, patchdefer_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. |
| /// 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. |
There was a problem hiding this comment.
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.
| /// 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 |
There was a problem hiding this comment.
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.
| /// 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 |
| /// <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; | ||
| } |
There was a problem hiding this comment.
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.
@rogerbarreto
Alternative to #7377.
Propose
HostedToolSearchToolwith the tools specified in the type as anIList<string>? DeferredTools.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