diff --git a/dotnet/agent-framework-dotnet.slnx b/dotnet/agent-framework-dotnet.slnx
index 8edd8bffcd..126efa7272 100644
--- a/dotnet/agent-framework-dotnet.slnx
+++ b/dotnet/agent-framework-dotnet.slnx
@@ -71,6 +71,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dotnet/samples/SemanticKernelMigration/AzureOpenAIResponses/Step01_Basics/AzureOpenAIResponses_Step01_Basics.csproj b/dotnet/samples/SemanticKernelMigration/AzureOpenAIResponses/Step01_Basics/AzureOpenAIResponses_Step01_Basics.csproj
new file mode 100644
index 0000000000..3f455d079f
--- /dev/null
+++ b/dotnet/samples/SemanticKernelMigration/AzureOpenAIResponses/Step01_Basics/AzureOpenAIResponses_Step01_Basics.csproj
@@ -0,0 +1,24 @@
+
+
+
+ Exe
+ net9.0
+ enable
+ enable
+ $(NoWarn);CA1812;RCS1102;CA1707
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/dotnet/samples/SemanticKernelMigration/AzureOpenAIResponses/Step01_Basics/Program.cs b/dotnet/samples/SemanticKernelMigration/AzureOpenAIResponses/Step01_Basics/Program.cs
new file mode 100644
index 0000000000..5801959df3
--- /dev/null
+++ b/dotnet/samples/SemanticKernelMigration/AzureOpenAIResponses/Step01_Basics/Program.cs
@@ -0,0 +1,68 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using Azure.AI.OpenAI;
+using Azure.Identity;
+using Microsoft.Extensions.AI.Agents;
+using Microsoft.SemanticKernel.Agents.OpenAI;
+using OpenAI;
+
+#pragma warning disable SKEXP0110 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
+#pragma warning disable OPENAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
+
+var endpoint = Environment.GetEnvironmentVariable("AZUREOPENAI_ENDPOINT") ?? throw new InvalidOperationException("AZUREOPENAI_ENDPOINT is not set.");
+var deploymentName = System.Environment.GetEnvironmentVariable("AZUREOPENAI_DEPLOYMENT_NAME") ?? "gpt-4o";
+var userInput = "Tell me a joke about a pirate.";
+
+Console.WriteLine($"User Input: {userInput}");
+
+await SKAgent();
+await AFAgent();
+
+async Task SKAgent()
+{
+ Console.WriteLine("\n=== SK Agent ===\n");
+
+ OpenAIResponseAgent agent = new(new AzureOpenAIClient(new Uri(endpoint), new AzureCliCredential())
+ .GetOpenAIResponseClient(deploymentName))
+ {
+ Name = "Joker",
+ Instructions = "You are good at telling jokes.",
+ };
+
+ var agentOptions = new OpenAIResponseAgentInvokeOptions() { ResponseCreationOptions = new() { MaxOutputTokenCount = 1000 } };
+
+ Microsoft.SemanticKernel.Agents.AgentThread? thread = null;
+ await foreach (var item in agent.InvokeAsync(userInput, thread, agentOptions))
+ {
+ Console.WriteLine(item.Message);
+ }
+
+ Console.WriteLine("---");
+ await foreach (var item in agent.InvokeStreamingAsync(userInput, thread, agentOptions))
+ {
+ // Thread need to be updated for subsequent calls
+ thread = item.Thread;
+ Console.Write(item.Message);
+ }
+}
+
+async Task AFAgent()
+{
+ Console.WriteLine("\n=== AF Agent ===\n");
+
+ var agent = new AzureOpenAIClient(new Uri(endpoint), new AzureCliCredential())
+ .GetOpenAIResponseClient(deploymentName)
+ .CreateAIAgent(name: "Joker", instructions: "You are good at telling jokes.");
+
+ var thread = agent.GetNewThread();
+ var agentOptions = new ChatClientAgentRunOptions(new() { MaxOutputTokens = 8000 });
+
+ var result = await agent.RunAsync(userInput, thread, agentOptions);
+ Console.WriteLine(result);
+
+ Console.WriteLine("---");
+ await foreach (var update in agent.RunStreamingAsync(userInput, thread, agentOptions))
+ {
+ Console.Write(update);
+ }
+}
diff --git a/dotnet/samples/SemanticKernelMigration/AzureOpenAIResponses/Step02_ReasoningModel/AzureOpenAIResponses_Step02_ReasoningModel.csproj b/dotnet/samples/SemanticKernelMigration/AzureOpenAIResponses/Step02_ReasoningModel/AzureOpenAIResponses_Step02_ReasoningModel.csproj
new file mode 100644
index 0000000000..3f455d079f
--- /dev/null
+++ b/dotnet/samples/SemanticKernelMigration/AzureOpenAIResponses/Step02_ReasoningModel/AzureOpenAIResponses_Step02_ReasoningModel.csproj
@@ -0,0 +1,24 @@
+
+
+
+ Exe
+ net9.0
+ enable
+ enable
+ $(NoWarn);CA1812;RCS1102;CA1707
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/dotnet/samples/SemanticKernelMigration/AzureOpenAIResponses/Step02_ReasoningModel/Program.cs b/dotnet/samples/SemanticKernelMigration/AzureOpenAIResponses/Step02_ReasoningModel/Program.cs
new file mode 100644
index 0000000000..7900e474ed
--- /dev/null
+++ b/dotnet/samples/SemanticKernelMigration/AzureOpenAIResponses/Step02_ReasoningModel/Program.cs
@@ -0,0 +1,166 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using Azure.AI.OpenAI;
+using Azure.Identity;
+using Microsoft.Extensions.AI;
+using Microsoft.Extensions.AI.Agents;
+using Microsoft.SemanticKernel;
+using Microsoft.SemanticKernel.Agents.OpenAI;
+using Microsoft.SemanticKernel.ChatCompletion;
+using OpenAI;
+
+#pragma warning disable SKEXP0110 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
+#pragma warning disable OPENAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
+
+var endpoint = Environment.GetEnvironmentVariable("AZUREOPENAI_ENDPOINT") ?? throw new InvalidOperationException("AZUREOPENAI_ENDPOINT is not set.");
+var deploymentName = System.Environment.GetEnvironmentVariable("AZUREOPENAI_DEPLOYMENT_NAME") ?? "o4-mini";
+var userInput =
+ """
+ Instructions:
+ - Given the React component below, think about it and change it so that nonfiction books have red
+ text.
+ - Return only the code in your reply
+ - Do not include any additional formatting, such as markdown code blocks
+ - For formatting, use four space tabs, and do not allow any lines of code to
+ exceed 80 columns
+ const books = [
+ { title: 'Dune', category: 'fiction', id: 1 },
+ { title: 'Frankenstein', category: 'fiction', id: 2 },
+ { title: 'Moneyball', category: 'nonfiction', id: 3 },
+ ];
+ export default function BookList() {
+ const listItems = books.map(book =>
+
+ {book.title}
+
+ );
+ return (
+
+ );
+ }
+ """;
+
+Console.WriteLine($"User Input: {userInput}");
+
+await SKAgent();
+await AFAgent();
+
+async Task SKAgent()
+{
+ Console.WriteLine("\n=== SK Agent ===\n");
+
+ OpenAIResponseAgent agent = new(new AzureOpenAIClient(new Uri(endpoint), new AzureCliCredential())
+ .GetOpenAIResponseClient(deploymentName))
+ {
+ Name = "Joker",
+ Instructions = "You are good at telling jokes.",
+ };
+
+ var agentOptions = new OpenAIResponseAgentInvokeOptions()
+ {
+ ResponseCreationOptions = new()
+ {
+ MaxOutputTokenCount = 8000,
+ ReasoningOptions = new()
+ {
+ ReasoningEffortLevel = OpenAI.Responses.ResponseReasoningEffortLevel.High,
+ ReasoningSummaryVerbosity = OpenAI.Responses.ResponseReasoningSummaryVerbosity.Detailed
+ }
+ }
+ };
+
+ Microsoft.SemanticKernel.Agents.AgentThread? thread = null;
+ await foreach (var item in agent.InvokeAsync(userInput, thread, agentOptions))
+ {
+ foreach (var content in item.Message.Items)
+ {
+ // Currently SK Responses Agent doesn't distinguish thinking from non-thinking content in non-streaming mode.
+ // SK Bugfix WIP: https://github.com/microsoft/semantic-kernel/issues/13046
+ if (content is ReasoningContent thinking)
+ {
+ Console.Write($"Thinking: \n{thinking}\n---\n");
+ }
+ else if (content is Microsoft.SemanticKernel.TextContent text)
+ {
+ Console.Write($"Assistant: {text}");
+ }
+ }
+ Console.WriteLine(item.Message);
+ }
+
+ Console.WriteLine("---");
+ var userMessage = new ChatMessageContent(AuthorRole.User, userInput);
+ await foreach (var item in agent.InvokeStreamingAsync(userMessage, thread, agentOptions))
+ {
+ thread = item.Thread;
+ foreach (var content in item.Message.Items)
+ {
+ // Currently SK Agent doesn't output thinking in streaming mode.
+ // SK Bugfix WIP: https://github.com/microsoft/semantic-kernel/issues/13046
+ if (content is StreamingReasoningContent thinking)
+ {
+ Console.WriteLine($"Thinking: [{thinking}]");
+ continue;
+ }
+
+ if (content is StreamingTextContent text)
+ {
+ Console.WriteLine($"Response: [{text}]");
+ }
+ }
+ }
+}
+
+async Task AFAgent()
+{
+ Console.WriteLine("\n=== AF Agent ===\n");
+
+ var agent = new AzureOpenAIClient(new Uri(endpoint), new AzureCliCredential())
+ .GetOpenAIResponseClient(deploymentName)
+ .CreateAIAgent(name: "Joker", instructions: "You are good at telling jokes.");
+
+ var thread = agent.GetNewThread();
+ var agentOptions = new ChatClientAgentRunOptions(new()
+ {
+ MaxOutputTokens = 8000,
+ // Microsoft.Extensions.AI currently does not have an abstraction for reasoning-effort,
+ // we need to break glass using the RawRepresentationFactory.
+ RawRepresentationFactory = (_) => new OpenAI.Responses.ResponseCreationOptions()
+ {
+ ReasoningOptions = new()
+ {
+ ReasoningEffortLevel = OpenAI.Responses.ResponseReasoningEffortLevel.High,
+ ReasoningSummaryVerbosity = OpenAI.Responses.ResponseReasoningSummaryVerbosity.Detailed
+ }
+ }
+ });
+
+ var result = await agent.RunAsync(userInput, thread, agentOptions);
+
+ // Retrieve the thinking as a full text block requires flattening multiple TextReasoningContents from multiple messages content lists.
+ string assistantThinking = string.Join("\n", result.Messages
+ .SelectMany(m => m.Contents)
+ .OfType()
+ .Select(trc => trc.Text));
+
+ var assistantText = result.Text;
+ Console.WriteLine($"Thinking: \n{assistantThinking}\n---\n");
+ Console.WriteLine($"Assistant: \n{assistantText}\n---\n");
+
+ Console.WriteLine("---");
+ await foreach (var update in agent.RunStreamingAsync(userInput, thread, agentOptions))
+ {
+ var thinkingContents = update.Contents
+ .OfType()
+ .Select(trc => trc.Text)
+ .ToList();
+
+ if (thinkingContents.Count != 0)
+ {
+ Console.WriteLine($"Thinking: [{string.Join("\n", thinkingContents)}]");
+ continue;
+ }
+
+ Console.WriteLine($"Response: [{update.Text}]");
+ }
+}
diff --git a/dotnet/samples/SemanticKernelMigration/AzureOpenAIResponses/Step03_ToolCall/AzureOpenAIResponses_Step03_ToolCall.csproj b/dotnet/samples/SemanticKernelMigration/AzureOpenAIResponses/Step03_ToolCall/AzureOpenAIResponses_Step03_ToolCall.csproj
new file mode 100644
index 0000000000..dcf6e25469
--- /dev/null
+++ b/dotnet/samples/SemanticKernelMigration/AzureOpenAIResponses/Step03_ToolCall/AzureOpenAIResponses_Step03_ToolCall.csproj
@@ -0,0 +1,24 @@
+
+
+
+ Exe
+ net9.0
+ enable
+ enable
+ $(NoWarn);CA1812;RCS1102;CA1707;CA1050;CA1052
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/dotnet/samples/SemanticKernelMigration/AzureOpenAIResponses/Step03_ToolCall/Program.cs b/dotnet/samples/SemanticKernelMigration/AzureOpenAIResponses/Step03_ToolCall/Program.cs
new file mode 100644
index 0000000000..9c9ab8995c
--- /dev/null
+++ b/dotnet/samples/SemanticKernelMigration/AzureOpenAIResponses/Step03_ToolCall/Program.cs
@@ -0,0 +1,56 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using System.ComponentModel;
+using Azure.AI.OpenAI;
+using Azure.Identity;
+using Microsoft.Extensions.AI;
+using Microsoft.SemanticKernel;
+using Microsoft.SemanticKernel.Agents.OpenAI;
+using OpenAI;
+
+#pragma warning disable OPENAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
+
+var endpoint = Environment.GetEnvironmentVariable("AZUREOPENAI_ENDPOINT") ?? throw new InvalidOperationException("AZUREOPENAI_ENDPOINT is not set.");
+var deploymentName = System.Environment.GetEnvironmentVariable("AZUREOPENAI_DEPLOYMENT_NAME") ?? "gpt-4o";
+var userInput = "What is the weather like in Amsterdam?";
+
+Console.WriteLine($"User Input: {userInput}");
+
+[KernelFunction]
+[Description("Get the weather for a given location.")]
+static string GetWeather([Description("The location to get the weather for.")] string location)
+ => $"The weather in {location} is cloudy with a high of 15°C.";
+
+await SKAgent();
+await AFAgent();
+
+async Task SKAgent()
+{
+ OpenAIResponseAgent agent = new(new AzureOpenAIClient(new Uri(endpoint), new AzureCliCredential())
+ .GetOpenAIResponseClient(deploymentName));
+
+ // Initialize plugin and add to the agent's Kernel (same as direct Kernel usage).
+ agent.Kernel.Plugins.Add(KernelPluginFactory.CreateFromFunctions("KernelPluginName", [KernelFunctionFactory.CreateFromMethod(GetWeather)]));
+
+ Console.WriteLine("\n=== SK Agent Response ===\n");
+
+ await foreach (ChatMessageContent responseItem in agent.InvokeAsync(userInput))
+ {
+ if (!string.IsNullOrWhiteSpace(responseItem.Content))
+ {
+ Console.WriteLine(responseItem);
+ }
+ }
+}
+
+async Task AFAgent()
+{
+ var agent = new AzureOpenAIClient(new Uri(endpoint), new AzureCliCredential())
+ .GetChatClient(deploymentName)
+ .CreateAIAgent(instructions: "You are a helpful assistant", tools: [AIFunctionFactory.Create(GetWeather)]);
+
+ Console.WriteLine("\n=== AF Agent Response ===\n");
+
+ var result = await agent.RunAsync(userInput);
+ Console.WriteLine(result);
+}
diff --git a/dotnet/samples/SemanticKernelMigration/AzureOpenAIResponses/Step04_DependencyInjection/AzureOpenAIResponses_Step04_DependencyInjection.csproj b/dotnet/samples/SemanticKernelMigration/AzureOpenAIResponses/Step04_DependencyInjection/AzureOpenAIResponses_Step04_DependencyInjection.csproj
new file mode 100644
index 0000000000..3f455d079f
--- /dev/null
+++ b/dotnet/samples/SemanticKernelMigration/AzureOpenAIResponses/Step04_DependencyInjection/AzureOpenAIResponses_Step04_DependencyInjection.csproj
@@ -0,0 +1,24 @@
+
+
+
+ Exe
+ net9.0
+ enable
+ enable
+ $(NoWarn);CA1812;RCS1102;CA1707
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/dotnet/samples/SemanticKernelMigration/AzureOpenAIResponses/Step04_DependencyInjection/Program.cs b/dotnet/samples/SemanticKernelMigration/AzureOpenAIResponses/Step04_DependencyInjection/Program.cs
new file mode 100644
index 0000000000..53e65b4c3b
--- /dev/null
+++ b/dotnet/samples/SemanticKernelMigration/AzureOpenAIResponses/Step04_DependencyInjection/Program.cs
@@ -0,0 +1,56 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using Azure.AI.OpenAI;
+using Azure.Identity;
+using Microsoft.Extensions.AI;
+using Microsoft.Extensions.AI.Agents;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.SemanticKernel.Agents.OpenAI;
+using OpenAI;
+
+#pragma warning disable OPENAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
+
+var endpoint = Environment.GetEnvironmentVariable("AZUREOPENAI_ENDPOINT") ?? throw new InvalidOperationException("AZUREOPENAI_ENDPOINT is not set.");
+var deploymentName = System.Environment.GetEnvironmentVariable("AZUREOPENAI_DEPLOYMENT_NAME") ?? "gpt-4o";
+var userInput = "Tell me a joke about a pirate.";
+
+Console.WriteLine($"User Input: {userInput}");
+
+await SKAgent();
+await AFAgent();
+
+async Task SKAgent()
+{
+ Console.WriteLine("\n=== SK Agent ===\n");
+
+ var serviceCollection = new ServiceCollection();
+ serviceCollection.AddTransient((sp)
+ => new OpenAIResponseAgent(new AzureOpenAIClient(new Uri(endpoint), new AzureCliCredential())
+ .GetOpenAIResponseClient(deploymentName))
+ {
+ Name = "Joker",
+ Instructions = "You are good at telling jokes."
+ });
+
+ await using ServiceProvider serviceProvider = serviceCollection.BuildServiceProvider();
+ var agent = serviceProvider.GetRequiredService();
+
+ var result = await agent.InvokeAsync(userInput).FirstAsync();
+ Console.WriteLine(result.Message);
+}
+
+async Task AFAgent()
+{
+ Console.WriteLine("\n=== AF Agent ===\n");
+
+ var serviceCollection = new ServiceCollection();
+ serviceCollection.AddTransient((sp) => new AzureOpenAIClient(new Uri(endpoint), new AzureCliCredential())
+ .GetChatClient(deploymentName)
+ .CreateAIAgent(name: "Joker", instructions: "You are good at telling jokes."));
+
+ await using ServiceProvider serviceProvider = serviceCollection.BuildServiceProvider();
+ var agent = serviceProvider.GetRequiredService();
+
+ var result = await agent.RunAsync(userInput);
+ Console.WriteLine(result);
+}
diff --git a/dotnet/samples/SemanticKernelMigration/OpenAIResponses/Step01_Basics/OpenAIResponses_Step01_Basics.csproj b/dotnet/samples/SemanticKernelMigration/OpenAIResponses/Step01_Basics/OpenAIResponses_Step01_Basics.csproj
new file mode 100644
index 0000000000..3f455d079f
--- /dev/null
+++ b/dotnet/samples/SemanticKernelMigration/OpenAIResponses/Step01_Basics/OpenAIResponses_Step01_Basics.csproj
@@ -0,0 +1,24 @@
+
+
+
+ Exe
+ net9.0
+ enable
+ enable
+ $(NoWarn);CA1812;RCS1102;CA1707
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/dotnet/samples/SemanticKernelMigration/OpenAIResponses/Step01_Basics/Program.cs b/dotnet/samples/SemanticKernelMigration/OpenAIResponses/Step01_Basics/Program.cs
new file mode 100644
index 0000000000..dd2147c67c
--- /dev/null
+++ b/dotnet/samples/SemanticKernelMigration/OpenAIResponses/Step01_Basics/Program.cs
@@ -0,0 +1,64 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using Microsoft.Extensions.AI.Agents;
+using Microsoft.SemanticKernel.Agents.OpenAI;
+using OpenAI;
+
+#pragma warning disable SKEXP0110 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
+#pragma warning disable OPENAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
+
+var apiKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY") ?? throw new InvalidOperationException("OPENAI_API_KEY is not set.");
+var modelId = System.Environment.GetEnvironmentVariable("OPENAI_MODELID") ?? "gpt-4o";
+var userInput = "Tell me a joke about a pirate.";
+
+Console.WriteLine($"User Input: {userInput}");
+
+await SKAgent();
+await AFAgent();
+
+async Task SKAgent()
+{
+ Console.WriteLine("\n=== SK Agent ===\n");
+
+ OpenAIResponseAgent agent = new(new OpenAIClient(apiKey).GetOpenAIResponseClient(modelId))
+ {
+ Name = "Joker",
+ Instructions = "You are good at telling jokes.",
+ };
+
+ var agentOptions = new OpenAIResponseAgentInvokeOptions() { ResponseCreationOptions = new() { MaxOutputTokenCount = 1000 } };
+
+ Microsoft.SemanticKernel.Agents.AgentThread? thread = null;
+ await foreach (var item in agent.InvokeAsync(userInput, thread, agentOptions))
+ {
+ Console.WriteLine(item.Message);
+ }
+
+ Console.WriteLine("---");
+ await foreach (var item in agent.InvokeStreamingAsync(userInput, thread, agentOptions))
+ {
+ // Thread need to be updated for subsequent calls
+ thread = item.Thread;
+ Console.Write(item.Message);
+ }
+}
+
+async Task AFAgent()
+{
+ Console.WriteLine("\n=== AF Agent ===\n");
+
+ var agent = new OpenAIClient(apiKey).GetOpenAIResponseClient(modelId)
+ .CreateAIAgent(name: "Joker", instructions: "You are good at telling jokes.");
+
+ var thread = agent.GetNewThread();
+ var agentOptions = new ChatClientAgentRunOptions(new() { MaxOutputTokens = 8000 });
+
+ var result = await agent.RunAsync(userInput, thread, agentOptions);
+ Console.WriteLine(result);
+
+ Console.WriteLine("---");
+ await foreach (var update in agent.RunStreamingAsync(userInput, thread, agentOptions))
+ {
+ Console.Write(update);
+ }
+}
diff --git a/dotnet/samples/SemanticKernelMigration/OpenAIResponses/Step02_ReasoningModel/OpenAIResponses_Step02_ReasoningModel.csproj b/dotnet/samples/SemanticKernelMigration/OpenAIResponses/Step02_ReasoningModel/OpenAIResponses_Step02_ReasoningModel.csproj
new file mode 100644
index 0000000000..3f455d079f
--- /dev/null
+++ b/dotnet/samples/SemanticKernelMigration/OpenAIResponses/Step02_ReasoningModel/OpenAIResponses_Step02_ReasoningModel.csproj
@@ -0,0 +1,24 @@
+
+
+
+ Exe
+ net9.0
+ enable
+ enable
+ $(NoWarn);CA1812;RCS1102;CA1707
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/dotnet/samples/SemanticKernelMigration/OpenAIResponses/Step02_ReasoningModel/Program.cs b/dotnet/samples/SemanticKernelMigration/OpenAIResponses/Step02_ReasoningModel/Program.cs
new file mode 100644
index 0000000000..13267811e0
--- /dev/null
+++ b/dotnet/samples/SemanticKernelMigration/OpenAIResponses/Step02_ReasoningModel/Program.cs
@@ -0,0 +1,162 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using Microsoft.Extensions.AI;
+using Microsoft.Extensions.AI.Agents;
+using Microsoft.SemanticKernel;
+using Microsoft.SemanticKernel.Agents.OpenAI;
+using Microsoft.SemanticKernel.ChatCompletion;
+using OpenAI;
+
+#pragma warning disable SKEXP0110 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
+#pragma warning disable OPENAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
+
+var apiKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY") ?? throw new InvalidOperationException("OPENAI_API_KEY is not set.");
+var modelId = System.Environment.GetEnvironmentVariable("OPENAI_MODELID") ?? "o4-mini";
+var userInput =
+ """
+ Instructions:
+ - Given the React component below, think about it and change it so that nonfiction books have red
+ text.
+ - Return only the code in your reply
+ - Do not include any additional formatting, such as markdown code blocks
+ - For formatting, use four space tabs, and do not allow any lines of code to
+ exceed 80 columns
+ const books = [
+ { title: 'Dune', category: 'fiction', id: 1 },
+ { title: 'Frankenstein', category: 'fiction', id: 2 },
+ { title: 'Moneyball', category: 'nonfiction', id: 3 },
+ ];
+ export default function BookList() {
+ const listItems = books.map(book =>
+
+ {book.title}
+
+ );
+ return (
+
+ );
+ }
+ """;
+
+Console.WriteLine($"User Input: {userInput}");
+
+await SKAgent();
+await AFAgent();
+
+async Task SKAgent()
+{
+ Console.WriteLine("\n=== SK Agent ===\n");
+
+ OpenAIResponseAgent agent = new(new OpenAIClient(apiKey).GetOpenAIResponseClient(modelId))
+ {
+ Name = "Joker",
+ Instructions = "You are good at telling jokes.",
+ };
+
+ var agentOptions = new OpenAIResponseAgentInvokeOptions()
+ {
+ ResponseCreationOptions = new()
+ {
+ MaxOutputTokenCount = 8000,
+ ReasoningOptions = new()
+ {
+ ReasoningEffortLevel = OpenAI.Responses.ResponseReasoningEffortLevel.High,
+ ReasoningSummaryVerbosity = OpenAI.Responses.ResponseReasoningSummaryVerbosity.Detailed
+ }
+ }
+ };
+
+ Microsoft.SemanticKernel.Agents.AgentThread? thread = null;
+ await foreach (var item in agent.InvokeAsync(userInput, thread, agentOptions))
+ {
+ foreach (var content in item.Message.Items)
+ {
+ // Currently SK Responses Agent doesn't distinguish thinking from non-thinking content in non-streaming mode.
+ // SK Bugfix WIP: https://github.com/microsoft/semantic-kernel/issues/13046
+ if (content is ReasoningContent thinking)
+ {
+ Console.Write($"Thinking: \n{thinking}\n---\n");
+ }
+ else if (content is Microsoft.SemanticKernel.TextContent text)
+ {
+ Console.Write($"Assistant: {text}");
+ }
+ }
+ Console.WriteLine(item.Message);
+ }
+
+ Console.WriteLine("---");
+ var userMessage = new ChatMessageContent(AuthorRole.User, userInput);
+ await foreach (var item in agent.InvokeStreamingAsync(userMessage, thread, agentOptions))
+ {
+ thread = item.Thread;
+ foreach (var content in item.Message.Items)
+ {
+ // Currently SK Agent doesn't output thinking in streaming mode.
+ // SK Bugfix WIP: https://github.com/microsoft/semantic-kernel/issues/13046
+ if (content is StreamingReasoningContent thinking)
+ {
+ Console.WriteLine($"Thinking: [{thinking}]");
+ continue;
+ }
+
+ if (content is StreamingTextContent text)
+ {
+ Console.WriteLine($"Response: [{text}]");
+ }
+ }
+ }
+}
+
+async Task AFAgent()
+{
+ Console.WriteLine("\n=== AF Agent ===\n");
+
+ var agent = new OpenAIClient(apiKey).GetOpenAIResponseClient(modelId)
+ .CreateAIAgent(name: "Joker", instructions: "You are good at telling jokes.");
+
+ var thread = agent.GetNewThread();
+ var agentOptions = new ChatClientAgentRunOptions(new()
+ {
+ MaxOutputTokens = 8000,
+ // Microsoft.Extensions.AI currently does not have an abstraction for reasoning-effort,
+ // we need to break glass using the RawRepresentationFactory.
+ RawRepresentationFactory = (_) => new OpenAI.Responses.ResponseCreationOptions()
+ {
+ ReasoningOptions = new()
+ {
+ ReasoningEffortLevel = OpenAI.Responses.ResponseReasoningEffortLevel.High,
+ ReasoningSummaryVerbosity = OpenAI.Responses.ResponseReasoningSummaryVerbosity.Detailed
+ }
+ }
+ });
+
+ var result = await agent.RunAsync(userInput, thread, agentOptions);
+
+ // Retrieve the thinking as a full text block requires flattening multiple TextReasoningContents from multiple messages content lists.
+ string assistantThinking = string.Join("\n", result.Messages
+ .SelectMany(m => m.Contents)
+ .OfType()
+ .Select(trc => trc.Text));
+
+ var assistantText = result.Text;
+ Console.WriteLine($"Thinking: \n{assistantThinking}\n---\n");
+ Console.WriteLine($"Assistant: \n{assistantText}\n---\n");
+
+ Console.WriteLine("---");
+ await foreach (var update in agent.RunStreamingAsync(userInput, thread, agentOptions))
+ {
+ var thinkingContents = update.Contents
+ .OfType()
+ .Select(trc => trc.Text)
+ .ToList();
+
+ if (thinkingContents.Count != 0)
+ {
+ Console.WriteLine($"Thinking: [{string.Join("\n", thinkingContents)}]");
+ continue;
+ }
+
+ Console.WriteLine($"Response: [{update.Text}]");
+ }
+}
diff --git a/dotnet/samples/SemanticKernelMigration/OpenAIResponses/Step03_ToolCall/OpenAIResponses_Step03_ToolCall.csproj b/dotnet/samples/SemanticKernelMigration/OpenAIResponses/Step03_ToolCall/OpenAIResponses_Step03_ToolCall.csproj
new file mode 100644
index 0000000000..dcf6e25469
--- /dev/null
+++ b/dotnet/samples/SemanticKernelMigration/OpenAIResponses/Step03_ToolCall/OpenAIResponses_Step03_ToolCall.csproj
@@ -0,0 +1,24 @@
+
+
+
+ Exe
+ net9.0
+ enable
+ enable
+ $(NoWarn);CA1812;RCS1102;CA1707;CA1050;CA1052
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/dotnet/samples/SemanticKernelMigration/OpenAIResponses/Step03_ToolCall/Program.cs b/dotnet/samples/SemanticKernelMigration/OpenAIResponses/Step03_ToolCall/Program.cs
new file mode 100644
index 0000000000..c7de76df78
--- /dev/null
+++ b/dotnet/samples/SemanticKernelMigration/OpenAIResponses/Step03_ToolCall/Program.cs
@@ -0,0 +1,55 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using System.ComponentModel;
+using Microsoft.Extensions.AI;
+using Microsoft.SemanticKernel;
+using Microsoft.SemanticKernel.Agents.OpenAI;
+using OpenAI;
+
+#pragma warning disable OPENAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
+
+var apiKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY") ?? throw new InvalidOperationException("OPENAI_API_KEY is not set.");
+var modelId = System.Environment.GetEnvironmentVariable("OPENAI_MODELID") ?? "gpt-4o";
+var userInput = "What is the weather like in Amsterdam?";
+
+Console.WriteLine($"User Input: {userInput}");
+
+[KernelFunction]
+[Description("Get the weather for a given location.")]
+static string GetWeather([Description("The location to get the weather for.")] string location)
+ => $"The weather in {location} is cloudy with a high of 15°C.";
+
+await SKAgent();
+await AFAgent();
+
+async Task SKAgent()
+{
+ var builder = Kernel.CreateBuilder().AddOpenAIChatClient(modelId, apiKey);
+
+ OpenAIResponseAgent agent = new(new OpenAIClient(apiKey).GetOpenAIResponseClient(modelId));
+
+ // Initialize plugin and add to the agent's Kernel (same as direct Kernel usage).
+ agent.Kernel.Plugins.Add(KernelPluginFactory.CreateFromFunctions("KernelPluginName", [KernelFunctionFactory.CreateFromMethod(GetWeather)]));
+
+ Console.WriteLine("\n=== SK Agent Response ===\n");
+
+ await foreach (ChatMessageContent responseItem in agent.InvokeAsync(userInput))
+ {
+ if (!string.IsNullOrWhiteSpace(responseItem.Content))
+ {
+ Console.WriteLine(responseItem);
+ }
+ }
+}
+
+async Task AFAgent()
+{
+ var agent = new OpenAIClient(apiKey).GetChatClient(modelId).CreateAIAgent(
+ instructions: "You are a helpful assistant",
+ tools: [AIFunctionFactory.Create(GetWeather)]);
+
+ Console.WriteLine("\n=== AF Agent Response ===\n");
+
+ var result = await agent.RunAsync(userInput);
+ Console.WriteLine(result);
+}
diff --git a/dotnet/samples/SemanticKernelMigration/OpenAIResponses/Step04_DependencyInjection/OpenAIResponses_Step04_DependencyInjection.csproj b/dotnet/samples/SemanticKernelMigration/OpenAIResponses/Step04_DependencyInjection/OpenAIResponses_Step04_DependencyInjection.csproj
new file mode 100644
index 0000000000..3f455d079f
--- /dev/null
+++ b/dotnet/samples/SemanticKernelMigration/OpenAIResponses/Step04_DependencyInjection/OpenAIResponses_Step04_DependencyInjection.csproj
@@ -0,0 +1,24 @@
+
+
+
+ Exe
+ net9.0
+ enable
+ enable
+ $(NoWarn);CA1812;RCS1102;CA1707
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/dotnet/samples/SemanticKernelMigration/OpenAIResponses/Step04_DependencyInjection/Program.cs b/dotnet/samples/SemanticKernelMigration/OpenAIResponses/Step04_DependencyInjection/Program.cs
new file mode 100644
index 0000000000..36f38a5fe9
--- /dev/null
+++ b/dotnet/samples/SemanticKernelMigration/OpenAIResponses/Step04_DependencyInjection/Program.cs
@@ -0,0 +1,53 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using Microsoft.Extensions.AI;
+using Microsoft.Extensions.AI.Agents;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.SemanticKernel.Agents.OpenAI;
+using OpenAI;
+
+#pragma warning disable OPENAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
+
+var apiKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY") ?? throw new InvalidOperationException("OPENAI_API_KEY is not set.");
+var modelId = System.Environment.GetEnvironmentVariable("OPENAI_MODELID") ?? "gpt-4o";
+var userInput = "Tell me a joke about a pirate.";
+
+Console.WriteLine($"User Input: {userInput}");
+
+await SKAgent();
+await AFAgent();
+
+async Task SKAgent()
+{
+ Console.WriteLine("\n=== SK Agent ===\n");
+
+ var serviceCollection = new ServiceCollection();
+ serviceCollection.AddTransient((sp)
+ => new OpenAIResponseAgent(new OpenAIClient(apiKey).GetOpenAIResponseClient(modelId))
+ {
+ Name = "Joker",
+ Instructions = "You are good at telling jokes."
+ });
+
+ await using ServiceProvider serviceProvider = serviceCollection.BuildServiceProvider();
+ var agent = serviceProvider.GetRequiredService();
+
+ var result = await agent.InvokeAsync(userInput).FirstAsync();
+ Console.WriteLine(result.Message);
+}
+
+async Task AFAgent()
+{
+ Console.WriteLine("\n=== AF Agent ===\n");
+
+ var serviceCollection = new ServiceCollection();
+ serviceCollection.AddTransient((sp) => new OpenAIClient(apiKey)
+ .GetChatClient(modelId)
+ .CreateAIAgent(name: "Joker", instructions: "You are good at telling jokes."));
+
+ await using ServiceProvider serviceProvider = serviceCollection.BuildServiceProvider();
+ var agent = serviceProvider.GetRequiredService();
+
+ var result = await agent.RunAsync(userInput);
+ Console.WriteLine(result);
+}
diff --git a/dotnet/samples/SemanticKernelMigration/README.md b/dotnet/samples/SemanticKernelMigration/README.md
index 9b253aa1c1..92c635d49a 100644
--- a/dotnet/samples/SemanticKernelMigration/README.md
+++ b/dotnet/samples/SemanticKernelMigration/README.md
@@ -346,6 +346,10 @@ To run any migration sample, navigate to the desired sample directory:
cd "AzureAIFoundry\Step01_Basics"
dotnet run
+# Azure OpenAI Examples
+cd "AzureOpenAI\Step01_Basics"
+dotnet run
+
# OpenAI Examples
cd "OpenAI\Step01_Basics"
dotnet run
@@ -354,6 +358,9 @@ dotnet run
cd "OpenAIAssistants\Step01_Basics"
dotnet run
+# OpenAI Responses Examples
+cd "OpenAIResponses\Step01_Basics"
+
# Azure OpenAI Examples
cd "AzureOpenAI\Step01_Basics"
dotnet run
diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/NewOpenAIResponsesChatClient.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/NewOpenAIResponsesChatClient.cs
index 2e6051231a..bd849ad320 100644
--- a/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/NewOpenAIResponsesChatClient.cs
+++ b/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/NewOpenAIResponsesChatClient.cs
@@ -11,6 +11,7 @@ using System.Text;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
+using System.Text.Json.Serialization.Metadata;
using Microsoft.Shared.Diagnostics;
using OpenAI.Responses;
@@ -26,6 +27,9 @@ namespace Microsoft.Extensions.AI;
/// Represents an for an .
internal sealed class NewOpenAIResponsesChatClient : IChatClient
{
+ /// Type info for serializing and deserializing arbitrary JSON objects.
+ private static readonly JsonTypeInfo s_jsonTypeInfo = AIJsonUtilities.DefaultOptions.GetTypeInfo(typeof(object));
+
/// Metadata about the client.
private readonly ChatClientMetadata _metadata;
@@ -324,8 +328,24 @@ internal sealed class NewOpenAIResponsesChatClient : IChatClient
break;
default:
+ {
+ // Capture streaming thinking contents
+ if (streamingUpdate.GetType().Name == "InternalResponseReasoningSummaryTextDeltaEvent")
+ {
+ var updateJson = JsonSerializer.Deserialize(
+ JsonSerializer.Serialize(streamingUpdate, s_jsonTypeInfo),
+ OpenAIJsonContext2.Default.JsonElement);
+
+ if (updateJson.TryGetProperty("delta", out var deltaProperty))
+ {
+ yield return CreateUpdate(new TextReasoningContent(deltaProperty.GetString()));
+ break;
+ }
+ }
+
yield return CreateUpdate();
break;
+ }
}
}
}