From 6956bf43916bc7948c20705f95d260efb47b4476 Mon Sep 17 00:00:00 2001 From: Kumima Date: Sat, 27 Jun 2026 15:52:49 +0800 Subject: [PATCH 1/2] Enable function approval response content DevUI Extension --- .../Converters/ItemContentConverter.cs | 36 +++++++++++++ .../Responses/Models/ItemResource.cs | 53 +++++++++++++++++++ 2 files changed, 89 insertions(+) diff --git a/dotnet/src/Microsoft.Agents.AI.Hosting.OpenAI/Responses/Converters/ItemContentConverter.cs b/dotnet/src/Microsoft.Agents.AI.Hosting.OpenAI/Responses/Converters/ItemContentConverter.cs index 2476ce2fbd3..11acb1faccb 100644 --- a/dotnet/src/Microsoft.Agents.AI.Hosting.OpenAI/Responses/Converters/ItemContentConverter.cs +++ b/dotnet/src/Microsoft.Agents.AI.Hosting.OpenAI/Responses/Converters/ItemContentConverter.cs @@ -1,6 +1,8 @@ // Copyright (c) Microsoft. All rights reserved. using System; +using System.Collections.Generic; +using System.Text.Json; using Microsoft.Agents.AI.Hosting.OpenAI.Responses.Models; using Microsoft.Extensions.AI; @@ -69,6 +71,13 @@ private static string MediaTypeToAudioFormat(string mediaType) => new DataContent(inputAudio.Data, AudioFormatToMediaType(inputAudio.Format)), ItemContentOutputAudio outputAudio => new DataContent(outputAudio.Data, "audio/*"), + ItemContentFunctionApprovalResponse functionApprovalResponse => new ToolApprovalResponseContent( + functionApprovalResponse.RequestId, + functionApprovalResponse.Approved, + new FunctionCallContent( + functionApprovalResponse.FunctionCall.Id, + functionApprovalResponse.FunctionCall.Name, + ParseArguments(functionApprovalResponse.FunctionCall.Arguments))), _ => null }; @@ -159,4 +168,31 @@ DataContent audioData when audioData.HasTopLevelMediaType("audio") => return null; } + + private static Dictionary? ParseArguments(JsonElement argumentsJson) + { + try + { + using var doc = JsonDocument.Parse(argumentsJson.GetRawText()); + var result = new Dictionary(); + foreach (var property in doc.RootElement.EnumerateObject()) + { + result[property.Name] = property.Value.ValueKind switch + { + JsonValueKind.String => property.Value.GetString(), + JsonValueKind.Number => property.Value.GetDouble(), + JsonValueKind.True => true, + JsonValueKind.False => false, + JsonValueKind.Null => null, + _ => property.Value.GetRawText() + }; + } + + return result; + } + catch (JsonException) + { + return null; + } + } } diff --git a/dotnet/src/Microsoft.Agents.AI.Hosting.OpenAI/Responses/Models/ItemResource.cs b/dotnet/src/Microsoft.Agents.AI.Hosting.OpenAI/Responses/Models/ItemResource.cs index 289bafbc437..0166bf3e3c3 100644 --- a/dotnet/src/Microsoft.Agents.AI.Hosting.OpenAI/Responses/Models/ItemResource.cs +++ b/dotnet/src/Microsoft.Agents.AI.Hosting.OpenAI/Responses/Models/ItemResource.cs @@ -266,6 +266,7 @@ internal enum FunctionToolCallOutputItemResourceStatus [JsonDerivedType(typeof(ItemContentOutputText), "output_text")] [JsonDerivedType(typeof(ItemContentOutputAudio), "output_audio")] [JsonDerivedType(typeof(ItemContentRefusal), "refusal")] +[JsonDerivedType(typeof(ItemContentFunctionApprovalResponse), "function_approval_response")] internal abstract class ItemContent { /// @@ -443,6 +444,58 @@ internal sealed class ItemContentRefusal : ItemContent public required string Refusal { get; init; } } +/// +/// Represents function call information for approval response item. +/// +internal sealed class FunctionCall +{ + /// + /// Gets or initializes the function call ID. + /// + [JsonPropertyName("id")] + public required string Id { get; init; } + + /// + /// Gets or initializes the function name. + /// + [JsonPropertyName("name")] + public required string Name { get; init; } + + /// + /// Gets or initializes the function arguments. + /// + [JsonPropertyName("arguments")] + public required JsonElement Arguments { get; init; } +} + +/// +/// Function approval response content. +/// +internal sealed class ItemContentFunctionApprovalResponse : ItemContent +{ + /// + [JsonIgnore] + public override string Type => "function_approval_response"; + + /// + /// Gets or initializes the value indicating whether the function call was approved for execution. + /// + [JsonPropertyName("approved")] + public required bool Approved { get; init; } + + /// + /// Gets or initializes the function call that was subject to approval. + /// + [JsonPropertyName("function_call")] + public required FunctionCall FunctionCall { get; init; } + + /// + /// Gets or initializes the unique identifier that correlates this response with its corresponding request. + /// + [JsonPropertyName("request_id")] + public required string RequestId { get; init; } +} + // Additional ItemResource types from TypeSpec /// From 641a71cbd854a7eb06cc063566f056699fdcfc6a Mon Sep 17 00:00:00 2001 From: Kumima Date: Sat, 27 Jun 2026 16:44:54 +0800 Subject: [PATCH 2/2] Check Argument Json value kind --- .../Responses/Converters/ItemContentConverter.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/dotnet/src/Microsoft.Agents.AI.Hosting.OpenAI/Responses/Converters/ItemContentConverter.cs b/dotnet/src/Microsoft.Agents.AI.Hosting.OpenAI/Responses/Converters/ItemContentConverter.cs index 11acb1faccb..564043f9e7a 100644 --- a/dotnet/src/Microsoft.Agents.AI.Hosting.OpenAI/Responses/Converters/ItemContentConverter.cs +++ b/dotnet/src/Microsoft.Agents.AI.Hosting.OpenAI/Responses/Converters/ItemContentConverter.cs @@ -171,6 +171,11 @@ DataContent audioData when audioData.HasTopLevelMediaType("audio") => private static Dictionary? ParseArguments(JsonElement argumentsJson) { + if (argumentsJson.ValueKind is not JsonValueKind.Object) + { + return null; + } + try { using var doc = JsonDocument.Parse(argumentsJson.GetRawText());