Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,17 @@ public static class MicrosoftExtensionsAIResponsesExtensions
public static FunctionTool AsOpenAIResponseTool(this AIFunctionDeclaration function) =>
OpenAIResponsesChatClient.ToResponseTool(Throw.IfNull(function));

/// <summary>Creates an OpenAI <see cref="ResponseTool"/> from an <see cref="AITool"/>.</summary>
/// <param name="tool">The tool to convert.</param>
/// <returns>An OpenAI <see cref="ResponseTool"/> representing <paramref name="tool"/> or <see langword="null"/> if there is no mapping.</returns>
/// <exception cref="ArgumentNullException"><paramref name="tool"/> is <see langword="null"/>.</exception>
/// <remarks>
/// This method is only able to create <see cref="ResponseTool"/>s for <see cref="AITool"/> types
/// it's aware of, namely all of those available from the Microsoft.Extensions.AI.Abstractions library.
/// </remarks>
public static ResponseTool? AsOpenAIResponseTool(this AITool tool) =>
OpenAIResponsesChatClient.ToResponseTool(Throw.IfNull(tool));

/// <summary>
/// Creates an OpenAI <see cref="ResponseTextFormat"/> from a <see cref="ChatResponseFormat"/>.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,97 @@ void IDisposable.Dispose()
// Nothing to dispose. Implementation required for the IChatClient interface.
}

internal static ResponseTool? ToResponseTool(AITool tool, ChatOptions? options = null)
{
switch (tool)
{
case ResponseToolAITool rtat:
return rtat.Tool;

case AIFunctionDeclaration aiFunction:
return ToResponseTool(aiFunction, options);

case HostedWebSearchTool webSearchTool:
WebSearchToolLocation? location = null;
if (webSearchTool.AdditionalProperties.TryGetValue(nameof(WebSearchToolLocation), out object? objLocation))
{
location = objLocation as WebSearchToolLocation;
}

WebSearchToolContextSize? size = null;
if (webSearchTool.AdditionalProperties.TryGetValue(nameof(WebSearchToolContextSize), out object? objSize) &&
objSize is WebSearchToolContextSize)
{
size = (WebSearchToolContextSize)objSize;
}

return ResponseTool.CreateWebSearchTool(location, size);

case HostedFileSearchTool fileSearchTool:
return ResponseTool.CreateFileSearchTool(
fileSearchTool.Inputs?.OfType<HostedVectorStoreContent>().Select(c => c.VectorStoreId) ?? [],
fileSearchTool.MaximumResultCount);

case HostedCodeInterpreterTool codeTool:
return ResponseTool.CreateCodeInterpreterTool(
new CodeInterpreterToolContainer(codeTool.Inputs?.OfType<HostedFileContent>().Select(f => f.FileId).ToList() is { Count: > 0 } ids ?
CodeInterpreterToolContainerConfiguration.CreateAutomaticContainerConfiguration(ids) :
new()));

case HostedMcpServerTool mcpTool:
McpTool responsesMcpTool = Uri.TryCreate(mcpTool.ServerAddress, UriKind.Absolute, out Uri? url) ?
ResponseTool.CreateMcpTool(
mcpTool.ServerName,
url,
mcpTool.AuthorizationToken,
mcpTool.ServerDescription) :
ResponseTool.CreateMcpTool(
mcpTool.ServerName,
new McpToolConnectorId(mcpTool.ServerAddress),
mcpTool.AuthorizationToken,
mcpTool.ServerDescription);

if (mcpTool.AllowedTools is not null)
{
responsesMcpTool.AllowedTools = new();
AddAllMcpFilters(mcpTool.AllowedTools, responsesMcpTool.AllowedTools);
}

switch (mcpTool.ApprovalMode)
{
case HostedMcpServerToolAlwaysRequireApprovalMode:
responsesMcpTool.ToolCallApprovalPolicy = new McpToolCallApprovalPolicy(GlobalMcpToolCallApprovalPolicy.AlwaysRequireApproval);
break;

case HostedMcpServerToolNeverRequireApprovalMode:
responsesMcpTool.ToolCallApprovalPolicy = new McpToolCallApprovalPolicy(GlobalMcpToolCallApprovalPolicy.NeverRequireApproval);
break;

case HostedMcpServerToolRequireSpecificApprovalMode specificMode:
responsesMcpTool.ToolCallApprovalPolicy = new McpToolCallApprovalPolicy(new CustomMcpToolCallApprovalPolicy());

if (specificMode.AlwaysRequireApprovalToolNames is { Count: > 0 } alwaysRequireToolNames)
{
responsesMcpTool.ToolCallApprovalPolicy.CustomPolicy.ToolsAlwaysRequiringApproval = new();
AddAllMcpFilters(alwaysRequireToolNames, responsesMcpTool.ToolCallApprovalPolicy.CustomPolicy.ToolsAlwaysRequiringApproval);
}

if (specificMode.NeverRequireApprovalToolNames is { Count: > 0 } neverRequireToolNames)
{
responsesMcpTool.ToolCallApprovalPolicy.CustomPolicy.ToolsNeverRequiringApproval = new();
AddAllMcpFilters(neverRequireToolNames, responsesMcpTool.ToolCallApprovalPolicy.CustomPolicy.ToolsNeverRequiringApproval);
}

break;
}

return responsesMcpTool;

default:
return null;
}
}

internal static FunctionTool ToResponseTool(AIFunctionDeclaration aiFunction, ChatOptions? options = null)
{
bool? strict =
Expand Down Expand Up @@ -492,96 +583,9 @@ private ResponseCreationOptions ToOpenAIResponseCreationOptions(ChatOptions? opt
{
foreach (AITool tool in tools)
{
switch (tool)
if (ToResponseTool(tool, options) is { } responseTool)
{
case ResponseToolAITool rtat:
result.Tools.Add(rtat.Tool);
break;

case AIFunctionDeclaration aiFunction:
result.Tools.Add(ToResponseTool(aiFunction, options));
break;

case HostedWebSearchTool webSearchTool:
WebSearchToolLocation? location = null;
if (webSearchTool.AdditionalProperties.TryGetValue(nameof(WebSearchToolLocation), out object? objLocation))
{
location = objLocation as WebSearchToolLocation;
}

WebSearchToolContextSize? size = null;
if (webSearchTool.AdditionalProperties.TryGetValue(nameof(WebSearchToolContextSize), out object? objSize) &&
objSize is WebSearchToolContextSize)
{
size = (WebSearchToolContextSize)objSize;
}

result.Tools.Add(ResponseTool.CreateWebSearchTool(location, size));
break;

case HostedFileSearchTool fileSearchTool:
result.Tools.Add(ResponseTool.CreateFileSearchTool(
fileSearchTool.Inputs?.OfType<HostedVectorStoreContent>().Select(c => c.VectorStoreId) ?? [],
fileSearchTool.MaximumResultCount));
break;

case HostedCodeInterpreterTool codeTool:
result.Tools.Add(
ResponseTool.CreateCodeInterpreterTool(
new CodeInterpreterToolContainer(codeTool.Inputs?.OfType<HostedFileContent>().Select(f => f.FileId).ToList() is { Count: > 0 } ids ?
CodeInterpreterToolContainerConfiguration.CreateAutomaticContainerConfiguration(ids) :
new())));
break;

case HostedMcpServerTool mcpTool:
McpTool responsesMcpTool = Uri.TryCreate(mcpTool.ServerAddress, UriKind.Absolute, out Uri? url) ?
ResponseTool.CreateMcpTool(
mcpTool.ServerName,
url,
mcpTool.AuthorizationToken,
mcpTool.ServerDescription) :
ResponseTool.CreateMcpTool(
mcpTool.ServerName,
new McpToolConnectorId(mcpTool.ServerAddress),
mcpTool.AuthorizationToken,
mcpTool.ServerDescription);

if (mcpTool.AllowedTools is not null)
{
responsesMcpTool.AllowedTools = new();
AddAllMcpFilters(mcpTool.AllowedTools, responsesMcpTool.AllowedTools);
}

switch (mcpTool.ApprovalMode)
{
case HostedMcpServerToolAlwaysRequireApprovalMode:
responsesMcpTool.ToolCallApprovalPolicy = new McpToolCallApprovalPolicy(GlobalMcpToolCallApprovalPolicy.AlwaysRequireApproval);
break;

case HostedMcpServerToolNeverRequireApprovalMode:
responsesMcpTool.ToolCallApprovalPolicy = new McpToolCallApprovalPolicy(GlobalMcpToolCallApprovalPolicy.NeverRequireApproval);
break;

case HostedMcpServerToolRequireSpecificApprovalMode specificMode:
responsesMcpTool.ToolCallApprovalPolicy = new McpToolCallApprovalPolicy(new CustomMcpToolCallApprovalPolicy());

if (specificMode.AlwaysRequireApprovalToolNames is { Count: > 0 } alwaysRequireToolNames)
{
responsesMcpTool.ToolCallApprovalPolicy.CustomPolicy.ToolsAlwaysRequiringApproval = new();
AddAllMcpFilters(alwaysRequireToolNames, responsesMcpTool.ToolCallApprovalPolicy.CustomPolicy.ToolsAlwaysRequiringApproval);
}

if (specificMode.NeverRequireApprovalToolNames is { Count: > 0 } neverRequireToolNames)
{
responsesMcpTool.ToolCallApprovalPolicy.CustomPolicy.ToolsNeverRequiringApproval = new();
AddAllMcpFilters(neverRequireToolNames, responsesMcpTool.ToolCallApprovalPolicy.CustomPolicy.ToolsNeverRequiringApproval);
}

break;
}

result.Tools.Add(responsesMcpTool);
break;
result.Tools.Add(responseTool);
}
}

Expand Down
Loading
Loading