diff --git a/dotnet/Directory.Packages.props b/dotnet/Directory.Packages.props
index c60c6aedd669..22779c89ef7c 100644
--- a/dotnet/Directory.Packages.props
+++ b/dotnet/Directory.Packages.props
@@ -19,7 +19,7 @@
-
+
@@ -70,7 +70,7 @@
-
+
@@ -88,7 +88,7 @@
-
+
@@ -97,10 +97,10 @@
-
-
-
-
+
+
+
+
diff --git a/dotnet/samples/Demos/OpenAIRealtime/OpenAIRealtime.csproj b/dotnet/samples/Demos/OpenAIRealtime/OpenAIRealtime.csproj
index 7aaa8d7e8c4c..79a0672716e0 100644
--- a/dotnet/samples/Demos/OpenAIRealtime/OpenAIRealtime.csproj
+++ b/dotnet/samples/Demos/OpenAIRealtime/OpenAIRealtime.csproj
@@ -5,7 +5,7 @@
net8.0
enable
enable
- $(NoWarn);VSTHRD111,CA2007,CS8618,CS1591,CA1052,SKEXP0001
+ $(NoWarn);VSTHRD111,CA2007,CS8618,CS1591,CA1052,CA1810,SKEXP0001
5ee045b0-aea3-4f08-8d31-32d1a6f8fed0
diff --git a/dotnet/samples/Demos/OpenAIRealtime/Program.cs b/dotnet/samples/Demos/OpenAIRealtime/Program.cs
index fb17b4bbfd3e..2de269dd1609 100644
--- a/dotnet/samples/Demos/OpenAIRealtime/Program.cs
+++ b/dotnet/samples/Demos/OpenAIRealtime/Program.cs
@@ -8,7 +8,7 @@
using Microsoft.Extensions.Configuration;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Connectors.OpenAI;
-using OpenAI.RealtimeConversation;
+using OpenAI.Realtime;
namespace OpenAIRealtime;
@@ -16,7 +16,7 @@ namespace OpenAIRealtime;
///
/// Demonstrates the use of the OpenAI Realtime API with function calling and Semantic Kernel.
-/// For conversational experiences, it is recommended to use from the Azure/OpenAI SDK.
+/// For conversational experiences, it is recommended to use from the Azure/OpenAI SDK.
/// Since the OpenAI Realtime API supports function calling, the example shows how to combine it with Semantic Kernel plugins and functions.
///
internal sealed class Program
@@ -33,7 +33,7 @@ public static async Task Main(string[] args)
kernel.ImportPluginFromType();
// Start a new conversation session.
- using RealtimeConversationSession session = await realtimeConversationClient.StartConversationSessionAsync();
+ using RealtimeSession session = await realtimeConversationClient.StartConversationSessionAsync("gpt-4o-realtime-preview");
// Initialize session options.
// Session options control connection-wide behavior shared across all conversations,
@@ -41,8 +41,8 @@ public static async Task Main(string[] args)
ConversationSessionOptions sessionOptions = new()
{
Voice = ConversationVoice.Alloy,
- InputAudioFormat = ConversationAudioFormat.Pcm16,
- OutputAudioFormat = ConversationAudioFormat.Pcm16,
+ InputAudioFormat = RealtimeAudioFormat.Pcm16,
+ OutputAudioFormat = RealtimeAudioFormat.Pcm16,
InputTranscriptionOptions = new()
{
Model = "whisper-1",
@@ -62,13 +62,12 @@ public static async Task Main(string[] args)
}
// Configure session with defined options.
- await session.ConfigureSessionAsync(sessionOptions);
+ await session.ConfigureConversationSessionAsync(sessionOptions);
// Items such as user, assistant, or system messages, as well as input audio, can be sent to the session.
// An example of sending user message to the session.
// ConversationItem can be constructed from Microsoft.SemanticKernel.ChatMessageContent if needed by mapping the relevant fields.
- await session.AddItemAsync(
- ConversationItem.CreateUserMessage(["I'm trying to decide what to wear on my trip."]));
+ await session.AddItemAsync(RealtimeItem.CreateUserMessage(["I'm trying to decide what to wear on my trip."]));
// Use audio file that contains a recorded question: "What's the weather like in San Francisco, California?"
string inputAudioPath = FindFile("Assets\\realtime_whats_the_weather_pcm16_24khz_mono.wav");
@@ -82,7 +81,7 @@ await session.AddItemAsync(
Dictionary functionArgumentBuildersById = [];
// Define a loop to receive conversation updates in the session.
- await foreach (ConversationUpdate update in session.ReceiveUpdatesAsync())
+ await foreach (RealtimeUpdate update in session.ReceiveUpdatesAsync())
{
// Notification indicating the start of the conversation session.
if (update is ConversationSessionStartedUpdate sessionStartedUpdate)
@@ -92,21 +91,21 @@ await session.AddItemAsync(
}
// Notification indicating the start of detected voice activity.
- if (update is ConversationInputSpeechStartedUpdate speechStartedUpdate)
+ if (update is InputAudioSpeechStartedUpdate speechStartedUpdate)
{
Console.WriteLine(
$" -- Voice activity detection started at {speechStartedUpdate.AudioStartTime}");
}
// Notification indicating the end of detected voice activity.
- if (update is ConversationInputSpeechFinishedUpdate speechFinishedUpdate)
+ if (update is InputAudioSpeechFinishedUpdate speechFinishedUpdate)
{
Console.WriteLine(
$" -- Voice activity detection ended at {speechFinishedUpdate.AudioEndTime}");
}
// Notification indicating the start of item streaming, such as a function call or response message.
- if (update is ConversationItemStreamingStartedUpdate itemStreamingStartedUpdate)
+ if (update is OutputStreamingStartedUpdate itemStreamingStartedUpdate)
{
Console.WriteLine(" -- Begin streaming of new item");
if (!string.IsNullOrEmpty(itemStreamingStartedUpdate.FunctionName))
@@ -116,7 +115,7 @@ await session.AddItemAsync(
}
// Notification about item streaming delta, which may include audio transcript, audio bytes, or function arguments.
- if (update is ConversationItemStreamingPartDeltaUpdate deltaUpdate)
+ if (update is OutputDeltaUpdate deltaUpdate)
{
Console.Write(deltaUpdate.AudioTranscript);
Console.Write(deltaUpdate.Text);
@@ -148,7 +147,7 @@ await session.AddItemAsync(
// Notification indicating the end of item streaming, such as a function call or response message.
// At this point, audio transcript can be displayed on console, or a function can be called with aggregated arguments.
- if (update is ConversationItemStreamingFinishedUpdate itemStreamingFinishedUpdate)
+ if (update is OutputStreamingFinishedUpdate itemStreamingFinishedUpdate)
{
Console.WriteLine();
Console.WriteLine($" -- Item streaming finished, item_id={itemStreamingFinishedUpdate.ItemId}");
@@ -176,7 +175,7 @@ await session.AddItemAsync(
var resultContent = await functionCallContent.InvokeAsync(kernel);
// Create a function call output conversation item with function call result.
- ConversationItem functionOutputItem = ConversationItem.CreateFunctionCallOutput(
+ RealtimeItem functionOutputItem = RealtimeItem.CreateFunctionCallOutput(
callId: itemStreamingFinishedUpdate.FunctionCallId,
output: ProcessFunctionResult(resultContent.Result));
@@ -198,7 +197,7 @@ await session.AddItemAsync(
}
// Notification indicating the completion of transcription from input audio.
- if (update is ConversationInputTranscriptionFinishedUpdate transcriptionCompletedUpdate)
+ if (update is InputAudioTranscriptionFinishedUpdate transcriptionCompletedUpdate)
{
Console.WriteLine();
Console.WriteLine($" -- User audio transcript: {transcriptionCompletedUpdate.Transcript}");
@@ -206,7 +205,7 @@ await session.AddItemAsync(
}
// Notification about completed model response turn.
- if (update is ConversationResponseFinishedUpdate turnFinishedUpdate)
+ if (update is ResponseFinishedUpdate turnFinishedUpdate)
{
Console.WriteLine($" -- Model turn generation finished. Status: {turnFinishedUpdate.Status}");
@@ -226,7 +225,7 @@ await session.AddItemAsync(
}
// Notification about error in conversation session.
- if (update is ConversationErrorUpdate errorUpdate)
+ if (update is RealtimeErrorUpdate errorUpdate)
{
Console.WriteLine();
Console.WriteLine($"ERROR: {errorUpdate.Message}");
@@ -375,24 +374,22 @@ private static string FindFile(string fileName)
}
///
- /// Helper method to get an instance of based on provided
+ /// Helper method to get an instance of based on provided
/// OpenAI or Azure OpenAI configuration.
///
- private static RealtimeConversationClient GetRealtimeConversationClient()
+ private static RealtimeClient GetRealtimeConversationClient()
{
var config = new ConfigurationBuilder()
.AddUserSecrets()
.AddEnvironmentVariables()
.Build();
- var openAIOptions = config.GetSection(OpenAIOptions.SectionName).Get();
- var azureOpenAIOptions = config.GetSection(AzureOpenAIOptions.SectionName).Get();
+ var openAIOptions = config.GetSection(OpenAIOptions.SectionName).Get()!;
+ var azureOpenAIOptions = config.GetSection(AzureOpenAIOptions.SectionName).Get()!;
if (openAIOptions is not null && openAIOptions.IsValid)
{
- return new RealtimeConversationClient(
- model: "gpt-4o-realtime-preview",
- credential: new ApiKeyCredential(openAIOptions.ApiKey));
+ return new RealtimeClient(new ApiKeyCredential(openAIOptions.ApiKey));
}
else if (azureOpenAIOptions is not null && azureOpenAIOptions.IsValid)
{
@@ -400,7 +397,7 @@ private static RealtimeConversationClient GetRealtimeConversationClient()
endpoint: new Uri(azureOpenAIOptions.Endpoint),
credential: new ApiKeyCredential(azureOpenAIOptions.ApiKey));
- return client.GetRealtimeConversationClient(azureOpenAIOptions.DeploymentName);
+ return client.GetRealtimeClient();
}
else
{
diff --git a/dotnet/src/Agents/OpenAI/Extensions/OpenAIResponseExtensions.cs b/dotnet/src/Agents/OpenAI/Extensions/OpenAIResponseExtensions.cs
index d65e0f940fff..dc1ec795cae0 100644
--- a/dotnet/src/Agents/OpenAI/Extensions/OpenAIResponseExtensions.cs
+++ b/dotnet/src/Agents/OpenAI/Extensions/OpenAIResponseExtensions.cs
@@ -52,7 +52,7 @@ public static ChatMessageContent ToChatMessageContent(this OpenAIResponse respon
}
else if (item is ReasoningResponseItem reasoningResponseItem)
{
- if (reasoningResponseItem.SummaryTextParts is not null && reasoningResponseItem.SummaryTextParts.Count > 0)
+ if (reasoningResponseItem.SummaryParts is not null && reasoningResponseItem.SummaryParts.Count > 0)
{
return new ChatMessageContent(AuthorRole.Assistant, item.ToChatMessageContentItemCollection(), innerContent: reasoningResponseItem);
}
@@ -77,7 +77,7 @@ public static ChatMessageContentItemCollection ToChatMessageContentItemCollectio
}
else if (item is ReasoningResponseItem reasoningResponseItem)
{
- return reasoningResponseItem.SummaryTextParts.ToChatMessageContentItemCollection();
+ return reasoningResponseItem.SummaryParts.ToChatMessageContentItemCollection();
}
else if (item is FunctionCallResponseItem functionCallResponseItem)
{
@@ -195,12 +195,15 @@ private static ChatMessageContentItemCollection ToChatMessageContentItemCollecti
return collection;
}
- private static ChatMessageContentItemCollection ToChatMessageContentItemCollection(this IReadOnlyList texts)
+ private static ChatMessageContentItemCollection ToChatMessageContentItemCollection(this IReadOnlyList parts)
{
var collection = new ChatMessageContentItemCollection();
- foreach (var text in texts)
+ foreach (var part in parts)
{
- collection.Add(new TextContent(text, innerContent: null));
+ if (part is ReasoningSummaryTextPart text)
+ {
+ collection.Add(new TextContent(text.Text, innerContent: text));
+ }
}
return collection;
}
diff --git a/dotnet/src/Agents/UnitTests/Extensions/ResponseItemExtensionsTests.cs b/dotnet/src/Agents/UnitTests/Extensions/ResponseItemExtensionsTests.cs
index 8683a19f379f..d547b27be0fa 100644
--- a/dotnet/src/Agents/UnitTests/Extensions/ResponseItemExtensionsTests.cs
+++ b/dotnet/src/Agents/UnitTests/Extensions/ResponseItemExtensionsTests.cs
@@ -66,7 +66,7 @@ public void VerifyToChatMessageContentFromInputFile()
{
// Arrange
var fileBytes = new ReadOnlyMemory([1, 2, 3, 4, 5]);
- IEnumerable contentParts = [ResponseContentPart.CreateInputFilePart("fileId", "fileName", new(fileBytes))];
+ IEnumerable contentParts = [ResponseContentPart.CreateInputFilePart(BinaryData.FromBytes(fileBytes), "text/plain", "fileName")];
MessageResponseItem responseItem = ResponseItem.CreateUserMessageItem(contentParts);
// Act
@@ -102,7 +102,7 @@ public void VerifyToChatMessageContentFromRefusal()
public void VerifyToChatMessageContentFromReasoning()
{
// Arrange
- IEnumerable summaryParts = ["Foo"];
+ IEnumerable summaryParts = [ReasoningSummaryPart.CreateTextPart("Foo")];
ReasoningResponseItem responseItem = ResponseItem.CreateReasoningItem(summaryParts);
// Act
diff --git a/dotnet/src/Agents/UnitTests/OpenAI/Extensions/OpenAIResponseExtensionsTests.cs b/dotnet/src/Agents/UnitTests/OpenAI/Extensions/OpenAIResponseExtensionsTests.cs
index 6fa230534427..8bd86c950e7b 100644
--- a/dotnet/src/Agents/UnitTests/OpenAI/Extensions/OpenAIResponseExtensionsTests.cs
+++ b/dotnet/src/Agents/UnitTests/OpenAI/Extensions/OpenAIResponseExtensionsTests.cs
@@ -2,6 +2,7 @@
using System;
using System.Collections.Generic;
+using System.Linq;
using System.Reflection;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Agents.OpenAI;
@@ -178,25 +179,39 @@ private OpenAIResponse CreateMockOpenAIResponse(string model, IEnumerable tools, float topP, IDictionary metadata, ResponseIncompleteStatusDetails incompleteStatusDetails, IEnumerable outputItems, bool parallelToolCallsEnabled, ResponseToolChoice toolChoice)
{
Type type = typeof(OpenAIResponse);
+ var assembly = type.Assembly;
+ var internalServiceTierType = assembly.GetType("OpenAI.Internal.InternalServiceTier");
+ var nullableInternalServiceTierType = typeof(Nullable<>).MakeGenericType(internalServiceTierType!);
ConstructorInfo? constructor = type.GetConstructor(
BindingFlags.Instance | BindingFlags.NonPublic,
null,
[
+ typeof(IDictionary),
+ typeof(float?),
+ typeof(float?),
+ nullableInternalServiceTierType,
typeof(string),
- typeof(DateTimeOffset),
- typeof(ResponseError),
+ typeof(bool?),
typeof(string),
+ typeof(IList),
typeof(string),
+ typeof(ResponseStatus?),
+ typeof(DateTimeOffset),
+ typeof(ResponseError),
+ typeof(ResponseTokenUsage),
typeof(string),
- typeof(float),
- typeof(IEnumerable),
- typeof(float),
- typeof(IDictionary),
+ typeof(ResponseReasoningOptions),
+ typeof(int?),
+ typeof(ResponseTextOptions),
+ typeof(ResponseTruncationMode?),
typeof(ResponseIncompleteStatusDetails),
- typeof(IEnumerable),
+ typeof(IList),
typeof(bool),
- typeof(ResponseToolChoice)
+ typeof(ResponseToolChoice),
+ typeof(string),
+ typeof(string),
+ typeof(IDictionary)
],
null);
@@ -204,20 +219,31 @@ private OpenAIResponse CreateMockOpenAIResponse(string id, DateTimeOffset create
{
return (OpenAIResponse)constructor.Invoke(
[
- id,
- createdAt,
- error,
- instructions,
- model,
- previousResponseId,
- temperature,
- tools,
- topP,
metadata,
- incompleteStatusDetails,
- outputItems,
- parallelToolCallsEnabled,
- toolChoice
+ (float?)temperature,
+ (float?)topP,
+ null, // serviceTier
+ previousResponseId,
+ null, // background
+ instructions,
+ tools.ToList(),
+ id,
+ null, // status
+ createdAt,
+ error,
+ null, // usage
+ null, // endUserId
+ null, // reasoningOptions
+ null, // maxOutputTokenCount
+ null, // textOptions
+ null, // truncationMode
+ incompleteStatusDetails,
+ outputItems.ToList(),
+ parallelToolCallsEnabled,
+ toolChoice,
+ model,
+ "response",
+ null // additionalBinaryDataProperties
]
);
}
diff --git a/dotnet/src/Connectors/Connectors.AzureOpenAI.UnitTests/Connectors.AzureOpenAI.UnitTests.csproj b/dotnet/src/Connectors/Connectors.AzureOpenAI.UnitTests/Connectors.AzureOpenAI.UnitTests.csproj
index a0a695a6719c..efae2b241d3c 100644
--- a/dotnet/src/Connectors/Connectors.AzureOpenAI.UnitTests/Connectors.AzureOpenAI.UnitTests.csproj
+++ b/dotnet/src/Connectors/Connectors.AzureOpenAI.UnitTests/Connectors.AzureOpenAI.UnitTests.csproj
@@ -8,7 +8,7 @@
true
enable
false
- $(NoWarn);SKEXP0001;SKEXP0010;CA2007,CA1806,CA1869,CA1861,IDE0300,VSTHRD111,IDE1006
+ $(NoWarn);SKEXP0001;SKEXP0010;CA2007,CA1806,CA1869,CA1861,IDE0300,VSTHRD111,IDE1006,OPENAI001
diff --git a/dotnet/src/Connectors/Connectors.AzureOpenAI/Connectors.AzureOpenAI.csproj b/dotnet/src/Connectors/Connectors.AzureOpenAI/Connectors.AzureOpenAI.csproj
index d5e590afabbe..47d0ed0a85e5 100644
--- a/dotnet/src/Connectors/Connectors.AzureOpenAI/Connectors.AzureOpenAI.csproj
+++ b/dotnet/src/Connectors/Connectors.AzureOpenAI/Connectors.AzureOpenAI.csproj
@@ -5,7 +5,7 @@
Microsoft.SemanticKernel.Connectors.AzureOpenAI
$(AssemblyName)
net8.0;netstandard2.0
- $(NoWarn);NU5104;SKEXP0001,SKEXP0010
+ $(NoWarn);NU5104;SKEXP0001,SKEXP0010,OPENAI001
true
diff --git a/dotnet/src/Connectors/Connectors.Google.UnitTests/Extensions/KernelFunctionMetadataExtensionsTests.cs b/dotnet/src/Connectors/Connectors.Google.UnitTests/Extensions/KernelFunctionMetadataExtensionsTests.cs
index a87816bfb949..360d5173cab8 100644
--- a/dotnet/src/Connectors/Connectors.Google.UnitTests/Extensions/KernelFunctionMetadataExtensionsTests.cs
+++ b/dotnet/src/Connectors/Connectors.Google.UnitTests/Extensions/KernelFunctionMetadataExtensionsTests.cs
@@ -200,7 +200,7 @@ public void ItCanCreateValidGeminiFunctionManualForPlugin()
// Assert
Assert.NotNull(result);
Assert.Equal(
- """{"type":"object","required":["parameter1","parameter2","parameter3"],"properties":{"parameter1":{"description":"String parameter","type":"string"},"parameter2":{"description":"Enum parameter","type":"string","enum":["Value1","Value2"]},"parameter3":{"description":"DateTime parameter","type":"string"}}}""",
+ """{"type":"object","required":["parameter1","parameter2","parameter3"],"properties":{"parameter1":{"description":"String parameter","type":"string"},"parameter2":{"description":"Enum parameter","type":"string","enum":["Value1","Value2"]},"parameter3":{"description":"DateTime parameter","type":"string","format":"date-time"}}}""",
JsonSerializer.Serialize(result.Parameters)
);
}
diff --git a/dotnet/src/Connectors/Connectors.OpenAI.UnitTests/Connectors.OpenAI.UnitTests.csproj b/dotnet/src/Connectors/Connectors.OpenAI.UnitTests/Connectors.OpenAI.UnitTests.csproj
index 0a7171bbcd0d..0366175e98f8 100644
--- a/dotnet/src/Connectors/Connectors.OpenAI.UnitTests/Connectors.OpenAI.UnitTests.csproj
+++ b/dotnet/src/Connectors/Connectors.OpenAI.UnitTests/Connectors.OpenAI.UnitTests.csproj
@@ -7,7 +7,7 @@
true
enable
false
- $(NoWarn);SKEXP0001;SKEXP0010;CS1591;IDE1006;RCS1261;CA1031;CA1308;CA1861;CA2007;CA2234;VSTHRD111;CA1812
+ $(NoWarn);SKEXP0001;SKEXP0010;CS1591;IDE1006;RCS1261;CA1031;CA1308;CA1861;CA2007;CA2234;VSTHRD111;CA1812;OPENAI001
diff --git a/dotnet/src/Connectors/Connectors.OpenAI.UnitTests/Core/OpenAIJsonSchemaTransformerTests.cs b/dotnet/src/Connectors/Connectors.OpenAI.UnitTests/Core/OpenAIJsonSchemaTransformerTests.cs
index 2c15249a3ca6..e41408e2b36c 100644
--- a/dotnet/src/Connectors/Connectors.OpenAI.UnitTests/Core/OpenAIJsonSchemaTransformerTests.cs
+++ b/dotnet/src/Connectors/Connectors.OpenAI.UnitTests/Core/OpenAIJsonSchemaTransformerTests.cs
@@ -73,7 +73,7 @@ public void ItTransformsJsonSchemaCorrectly()
"null"
],
"items": {
- "type": "object",
+ "type": ["object","null"],
"properties": {
"TextProperty": {
"type": [
diff --git a/dotnet/src/Connectors/Connectors.OpenAI.UnitTests/Extensions/KernelFunctionMetadataExtensionsTests.cs b/dotnet/src/Connectors/Connectors.OpenAI.UnitTests/Extensions/KernelFunctionMetadataExtensionsTests.cs
index ec64801c51b0..3029777f56a1 100644
--- a/dotnet/src/Connectors/Connectors.OpenAI.UnitTests/Extensions/KernelFunctionMetadataExtensionsTests.cs
+++ b/dotnet/src/Connectors/Connectors.OpenAI.UnitTests/Extensions/KernelFunctionMetadataExtensionsTests.cs
@@ -208,7 +208,7 @@ public void ItCanCreateValidAzureOpenAIFunctionManualForPlugin(bool strict)
else
{
Assert.Equal(
- """{"type":"object","required":["parameter1","parameter2","parameter3"],"properties":{"parameter1":{"description":"String parameter","type":"string"},"parameter2":{"description":"Enum parameter","type":"string","enum":["Value1","Value2"]},"parameter3":{"description":"DateTime parameter","type":"string"}}}""",
+ """{"type":"object","required":["parameter1","parameter2","parameter3"],"properties":{"parameter1":{"description":"String parameter","type":"string"},"parameter2":{"description":"Enum parameter","type":"string","enum":["Value1","Value2"]},"parameter3":{"description":"DateTime parameter","type":"string","format":"date-time"}}}""",
parametersResult
);
}
diff --git a/dotnet/src/Connectors/Connectors.OpenAI.UnitTests/Helpers/OpenAIChatResponseFormatBuilderTests.cs b/dotnet/src/Connectors/Connectors.OpenAI.UnitTests/Helpers/OpenAIChatResponseFormatBuilderTests.cs
index 13a5862b19b7..419a97c6b500 100644
--- a/dotnet/src/Connectors/Connectors.OpenAI.UnitTests/Helpers/OpenAIChatResponseFormatBuilderTests.cs
+++ b/dotnet/src/Connectors/Connectors.OpenAI.UnitTests/Helpers/OpenAIChatResponseFormatBuilderTests.cs
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft. All rights reserved.
using System;
+using System.Reflection;
using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.SemanticKernel.Connectors.OpenAI;
@@ -34,11 +35,9 @@ public void GetJsonSchemaResponseFormatReturnsChatResponseFormatByDefault(
// Act
var chatResponseFormat = OpenAIChatResponseFormatBuilder.GetJsonSchemaResponseFormat(jsonElement);
- var responseFormat = this.GetResponseFormat(chatResponseFormat);
+ var (jsonSchema, schema) = this.GetResponseFormatJsonSchema(chatResponseFormat);
// Assert
- Assert.True(responseFormat.TryGetProperty("JsonSchema", out var jsonSchema));
- Assert.True(jsonSchema.TryGetProperty("Schema", out var schema));
Assert.True(jsonSchema.TryGetProperty("Name", out var name));
Assert.True(jsonSchema.TryGetProperty("Strict", out var strict));
@@ -145,10 +144,28 @@ public void GetJsonSchemaResponseFormatThrowsExceptionWhenSchemaDoesNotExist()
#region private
- private JsonElement GetResponseFormat(ChatResponseFormat chatResponseFormat)
+ private (JsonElement JsonSchema, JsonElement JsonSchemaSchema) GetResponseFormatJsonSchema(ChatResponseFormat chatResponseFormat)
{
- var settings = new OpenAIPromptExecutionSettings { ResponseFormat = chatResponseFormat };
- return JsonDocument.Parse(JsonSerializer.Serialize(settings, this._options)).RootElement.GetProperty("response_format");
+ var jsonSchemaProperty = chatResponseFormat.GetType().GetProperty("JsonSchema", BindingFlags.NonPublic | BindingFlags.Instance);
+
+ // Assert
+ Assert.NotNull(jsonSchemaProperty);
+ var jsonSchemaPropertyValue = jsonSchemaProperty.GetValue(chatResponseFormat);
+
+ Assert.NotNull(jsonSchemaPropertyValue);
+ var schemaProperty = jsonSchemaPropertyValue.GetType().GetProperty("Schema", BindingFlags.Public | BindingFlags.Instance);
+
+ Assert.NotNull(schemaProperty);
+ var schemaPropertyValue = schemaProperty.GetValue(jsonSchemaPropertyValue);
+
+ Assert.NotNull(schemaPropertyValue);
+
+ var jsonSchema = JsonSerializer.Deserialize(JsonSerializer.Serialize(jsonSchemaProperty.GetValue(chatResponseFormat)));
+
+ // Schema property gets serialized into a non-readable pattern in the jsonSchema JsonElement variable and needs to be returned separately.
+ var schema = JsonSerializer.Deserialize(schemaPropertyValue.ToString()!);
+
+ return (jsonSchema, schema);
}
private sealed class BinaryDataJsonConverter : JsonConverter
diff --git a/dotnet/src/Connectors/Connectors.OpenAI/Connectors.OpenAI.csproj b/dotnet/src/Connectors/Connectors.OpenAI/Connectors.OpenAI.csproj
index 2f280b843e10..aab81a532403 100644
--- a/dotnet/src/Connectors/Connectors.OpenAI/Connectors.OpenAI.csproj
+++ b/dotnet/src/Connectors/Connectors.OpenAI/Connectors.OpenAI.csproj
@@ -5,7 +5,7 @@
Microsoft.SemanticKernel.Connectors.OpenAI
$(AssemblyName)
net8.0;netstandard2.0
- $(NoWarn);NU5104;SKEXP0001,SKEXP0010
+ $(NoWarn);NU5104;SKEXP0001,SKEXP0010,OPENAI001
true
diff --git a/dotnet/src/SemanticKernel.Abstractions/AI/ChatCompletion/AIFunctionKernelFunction.cs b/dotnet/src/SemanticKernel.Abstractions/AI/ChatCompletion/AIFunctionKernelFunction.cs
index 374ddc1a5fe6..a319f6b1c85d 100644
--- a/dotnet/src/SemanticKernel.Abstractions/AI/ChatCompletion/AIFunctionKernelFunction.cs
+++ b/dotnet/src/SemanticKernel.Abstractions/AI/ChatCompletion/AIFunctionKernelFunction.cs
@@ -28,7 +28,7 @@ public AIFunctionKernelFunction(AIFunction aiFunction) :
{
Description = aiFunction.UnderlyingMethod?.ReturnParameter.GetCustomAttribute()?.Description,
ParameterType = aiFunction.UnderlyingMethod?.ReturnParameter.ParameterType,
- Schema = new KernelJsonSchema(AIJsonUtilities.CreateJsonSchema(aiFunction.UnderlyingMethod?.ReturnParameter.ParameterType)),
+ Schema = new KernelJsonSchema(aiFunction.ReturnJsonSchema ?? AIJsonUtilities.CreateJsonSchema(aiFunction.UnderlyingMethod?.ReturnParameter.ParameterType)),
})
{
// Kernel functions created from AI functions are always fully qualified
diff --git a/dotnet/src/SemanticKernel.UnitTests/SemanticKernel.UnitTests.csproj b/dotnet/src/SemanticKernel.UnitTests/SemanticKernel.UnitTests.csproj
index 512cbf00ad91..6c4d9765fd02 100644
--- a/dotnet/src/SemanticKernel.UnitTests/SemanticKernel.UnitTests.csproj
+++ b/dotnet/src/SemanticKernel.UnitTests/SemanticKernel.UnitTests.csproj
@@ -6,7 +6,7 @@
net8.0
true
false
- $(NoWarn);CA2007,CA1861,IDE1006,VSTHRD111,SKEXP0001,SKEXP0010,SKEXP0050,SKEXP0110,SKEXP0120,SKEXP0130,MEVD9000
+ $(NoWarn);CA2007,CA1861,IDE1006,VSTHRD111,SKEXP0001,SKEXP0010,SKEXP0050,SKEXP0110,SKEXP0120,SKEXP0130,MEVD9000,OPENAI001