From cf0e779638ea0737252829a719593a9574122d7f Mon Sep 17 00:00:00 2001 From: Roger Barreto <19890735+rogerbarreto@users.noreply.github.com> Date: Mon, 1 Sep 2025 13:04:26 +0100 Subject: [PATCH] .NET: Add missing Azure OpenAI Migration Sample code (#559) * Add missing Azure OpenAI Migration Sample code * Add Azure OpenAI Assistants Samples * Address miner README typos * Remove leftover --- dotnet/agent-framework-dotnet.slnx | 11 ++ .../AzureOpenAI_Step01_Basics.csproj | 24 ++++ .../AzureOpenAI/Step01_Basics/Program.cs | 67 +++++++++ .../AzureOpenAI_Step02_ToolCall.csproj | 24 ++++ .../AzureOpenAI/Step02_ToolCall/Program.cs | 54 ++++++++ ...reOpenAI_Step03_DependencyInjection.csproj | 24 ++++ .../Step03_DependencyInjection/Program.cs | 55 ++++++++ ...AzureOpenAIAssistants_Step01_Basics.csproj | 24 ++++ .../Step01_Basics/Program.cs | 81 +++++++++++ ...ureOpenAIAssistants_Step02_ToolCall.csproj | 24 ++++ .../Step02_ToolCall/Program.cs | 97 +++++++++++++ ...sistants_Step03_DependencyInjection.csproj | 24 ++++ .../Step03_DependencyInjection/Program.cs | 99 ++++++++++++++ ...AIAssistants_Step04_CodeInterpreter.csproj | 24 ++++ .../Step04_CodeInterpreter/Program.cs | 129 ++++++++++++++++++ .../OpenAI/Step01_Basics/Program.cs | 1 + .../OpenAIAssistants/Step01_Basics/Program.cs | 1 - .../samples/SemanticKernelMigration/README.md | 21 ++- 18 files changed, 776 insertions(+), 8 deletions(-) create mode 100644 dotnet/samples/SemanticKernelMigration/AzureOpenAI/Step01_Basics/AzureOpenAI_Step01_Basics.csproj create mode 100644 dotnet/samples/SemanticKernelMigration/AzureOpenAI/Step01_Basics/Program.cs create mode 100644 dotnet/samples/SemanticKernelMigration/AzureOpenAI/Step02_ToolCall/AzureOpenAI_Step02_ToolCall.csproj create mode 100644 dotnet/samples/SemanticKernelMigration/AzureOpenAI/Step02_ToolCall/Program.cs create mode 100644 dotnet/samples/SemanticKernelMigration/AzureOpenAI/Step03_DependencyInjection/AzureOpenAI_Step03_DependencyInjection.csproj create mode 100644 dotnet/samples/SemanticKernelMigration/AzureOpenAI/Step03_DependencyInjection/Program.cs create mode 100644 dotnet/samples/SemanticKernelMigration/AzureOpenAIAssistants/Step01_Basics/AzureOpenAIAssistants_Step01_Basics.csproj create mode 100644 dotnet/samples/SemanticKernelMigration/AzureOpenAIAssistants/Step01_Basics/Program.cs create mode 100644 dotnet/samples/SemanticKernelMigration/AzureOpenAIAssistants/Step02_ToolCall/AzureOpenAIAssistants_Step02_ToolCall.csproj create mode 100644 dotnet/samples/SemanticKernelMigration/AzureOpenAIAssistants/Step02_ToolCall/Program.cs create mode 100644 dotnet/samples/SemanticKernelMigration/AzureOpenAIAssistants/Step03_DependencyInjection/AzureOpenAIAssistants_Step03_DependencyInjection.csproj create mode 100644 dotnet/samples/SemanticKernelMigration/AzureOpenAIAssistants/Step03_DependencyInjection/Program.cs create mode 100644 dotnet/samples/SemanticKernelMigration/AzureOpenAIAssistants/Step04_CodeInterpreter/AzureOpenAIAssistants_Step04_CodeInterpreter.csproj create mode 100644 dotnet/samples/SemanticKernelMigration/AzureOpenAIAssistants/Step04_CodeInterpreter/Program.cs diff --git a/dotnet/agent-framework-dotnet.slnx b/dotnet/agent-framework-dotnet.slnx index c1dd73d982..8edd8bffcd 100644 --- a/dotnet/agent-framework-dotnet.slnx +++ b/dotnet/agent-framework-dotnet.slnx @@ -23,6 +23,17 @@ + + + + + + + + + + + diff --git a/dotnet/samples/SemanticKernelMigration/AzureOpenAI/Step01_Basics/AzureOpenAI_Step01_Basics.csproj b/dotnet/samples/SemanticKernelMigration/AzureOpenAI/Step01_Basics/AzureOpenAI_Step01_Basics.csproj new file mode 100644 index 0000000000..3f455d079f --- /dev/null +++ b/dotnet/samples/SemanticKernelMigration/AzureOpenAI/Step01_Basics/AzureOpenAI_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/AzureOpenAI/Step01_Basics/Program.cs b/dotnet/samples/SemanticKernelMigration/AzureOpenAI/Step01_Basics/Program.cs new file mode 100644 index 0000000000..941858b8dd --- /dev/null +++ b/dotnet/samples/SemanticKernelMigration/AzureOpenAI/Step01_Basics/Program.cs @@ -0,0 +1,67 @@ +// Copyright (c) Microsoft. All rights reserved. + +using Azure.AI.OpenAI; +using Azure.Identity; +using Microsoft.Extensions.AI.Agents; +using Microsoft.SemanticKernel; +using Microsoft.SemanticKernel.Agents; +using Microsoft.SemanticKernel.Connectors.OpenAI; +using OpenAI; + +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 builder = Kernel.CreateBuilder().AddAzureOpenAIChatClient(deploymentName, endpoint, new AzureCliCredential()); + + var agent = new ChatCompletionAgent() + { + Kernel = builder.Build(), + Name = "Joker", + Instructions = "You are good at telling jokes.", + }; + + var thread = new ChatHistoryAgentThread(); + var settings = new OpenAIPromptExecutionSettings() { MaxTokens = 1000 }; + var agentOptions = new AgentInvokeOptions() { KernelArguments = new(settings) }; + + await foreach (var result in agent.InvokeAsync(userInput, thread, agentOptions)) + { + Console.WriteLine(result.Message); + } + + Console.WriteLine("---"); + await foreach (var update in agent.InvokeStreamingAsync(userInput, thread, agentOptions)) + { + Console.Write(update.Message); + } +} + +async Task AFAgent() +{ + Console.WriteLine("\n=== AF Agent ===\n"); + + var agent = new AzureOpenAIClient(new Uri(endpoint), new AzureCliCredential()).GetChatClient(deploymentName) + .CreateAIAgent(name: "Joker", instructions: "You are good at telling jokes."); + + var thread = agent.GetNewThread(); + var agentOptions = new ChatClientAgentRunOptions(new() { MaxOutputTokens = 1000 }); + + 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/AzureOpenAI/Step02_ToolCall/AzureOpenAI_Step02_ToolCall.csproj b/dotnet/samples/SemanticKernelMigration/AzureOpenAI/Step02_ToolCall/AzureOpenAI_Step02_ToolCall.csproj new file mode 100644 index 0000000000..dcf6e25469 --- /dev/null +++ b/dotnet/samples/SemanticKernelMigration/AzureOpenAI/Step02_ToolCall/AzureOpenAI_Step02_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/AzureOpenAI/Step02_ToolCall/Program.cs b/dotnet/samples/SemanticKernelMigration/AzureOpenAI/Step02_ToolCall/Program.cs new file mode 100644 index 0000000000..b01cff8e38 --- /dev/null +++ b/dotnet/samples/SemanticKernelMigration/AzureOpenAI/Step02_ToolCall/Program.cs @@ -0,0 +1,54 @@ +// 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; +using OpenAI; + +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() +{ + var builder = Kernel.CreateBuilder().AddAzureOpenAIChatClient(deploymentName, endpoint, new AzureCliCredential()); + + ChatCompletionAgent agent = new() + { + Instructions = "You are a helpful assistant", + Kernel = builder.Build(), + Arguments = new KernelArguments(new PromptExecutionSettings() { FunctionChoiceBehavior = FunctionChoiceBehavior.Auto() }), + }; + + // 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"); + + var result = await agent.InvokeAsync(userInput).FirstAsync(); + Console.WriteLine(result.Message); +} + +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/AzureOpenAI/Step03_DependencyInjection/AzureOpenAI_Step03_DependencyInjection.csproj b/dotnet/samples/SemanticKernelMigration/AzureOpenAI/Step03_DependencyInjection/AzureOpenAI_Step03_DependencyInjection.csproj new file mode 100644 index 0000000000..3f455d079f --- /dev/null +++ b/dotnet/samples/SemanticKernelMigration/AzureOpenAI/Step03_DependencyInjection/AzureOpenAI_Step03_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/AzureOpenAI/Step03_DependencyInjection/Program.cs b/dotnet/samples/SemanticKernelMigration/AzureOpenAI/Step03_DependencyInjection/Program.cs new file mode 100644 index 0000000000..f0f5b383c8 --- /dev/null +++ b/dotnet/samples/SemanticKernelMigration/AzureOpenAI/Step03_DependencyInjection/Program.cs @@ -0,0 +1,55 @@ +// 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; +using Microsoft.SemanticKernel.Agents; +using OpenAI; + +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.AddKernel().AddAzureOpenAIChatClient(deploymentName, endpoint, new AzureCliCredential()); + serviceCollection.AddTransient((sp) => new ChatCompletionAgent() + { + Kernel = sp.GetRequiredService(), + 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(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/AzureOpenAIAssistants/Step01_Basics/AzureOpenAIAssistants_Step01_Basics.csproj b/dotnet/samples/SemanticKernelMigration/AzureOpenAIAssistants/Step01_Basics/AzureOpenAIAssistants_Step01_Basics.csproj new file mode 100644 index 0000000000..3f455d079f --- /dev/null +++ b/dotnet/samples/SemanticKernelMigration/AzureOpenAIAssistants/Step01_Basics/AzureOpenAIAssistants_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/AzureOpenAIAssistants/Step01_Basics/Program.cs b/dotnet/samples/SemanticKernelMigration/AzureOpenAIAssistants/Step01_Basics/Program.cs new file mode 100644 index 0000000000..b245aa237e --- /dev/null +++ b/dotnet/samples/SemanticKernelMigration/AzureOpenAIAssistants/Step01_Basics/Program.cs @@ -0,0 +1,81 @@ +// Copyright (c) Microsoft. All rights reserved. + +#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. + +using Azure.AI.OpenAI; +using Azure.Identity; +using Microsoft.Extensions.AI.Agents; +using Microsoft.SemanticKernel; +using Microsoft.SemanticKernel.Agents.OpenAI; +using Microsoft.SemanticKernel.Connectors.OpenAI; +using OpenAI; +using OpenAI.Assistants; + +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 builder = Kernel.CreateBuilder().AddAzureOpenAIChatClient(deploymentName, endpoint, new AzureCliCredential()); + + var assistantsClient = new AzureOpenAIClient(new Uri(endpoint), new AzureCliCredential()).GetAssistantClient(); + + // Define the assistant + Assistant assistant = await assistantsClient.CreateAssistantAsync(deploymentName, name: "Joker", instructions: "You are good at telling jokes."); + + // Create the agent + OpenAIAssistantAgent agent = new(assistant, assistantsClient); + + // Create a thread for the agent conversation. + var thread = new OpenAIAssistantAgentThread(assistantsClient); + var settings = new OpenAIPromptExecutionSettings() { MaxTokens = 1000 }; + var agentOptions = new OpenAIAssistantAgentInvokeOptions() { KernelArguments = new(settings) }; + + await foreach (var result in agent.InvokeAsync(userInput, thread, agentOptions)) + { + Console.WriteLine(result.Message); + } + + Console.WriteLine("---"); + await foreach (var update in agent.InvokeStreamingAsync(userInput, thread, agentOptions)) + { + Console.Write(update.Message); + } + + // Clean up + await thread.DeleteAsync(); + await assistantsClient.DeleteAssistantAsync(agent.Id); +} + +async Task AFAgent() +{ + Console.WriteLine("\n=== AF Agent ===\n"); + + var assistantClient = new AzureOpenAIClient(new Uri(endpoint), new AzureCliCredential()).GetAssistantClient(); + + var agent = await assistantClient.CreateAIAgentAsync(deploymentName, name: "Joker", instructions: "You are good at telling jokes."); + + var thread = agent.GetNewThread(); + var agentOptions = new ChatClientAgentRunOptions(new() { MaxOutputTokens = 1000 }); + + 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); + } + + // Clean up + await assistantClient.DeleteThreadAsync(thread.ConversationId); + await assistantClient.DeleteAssistantAsync(agent.Id); +} diff --git a/dotnet/samples/SemanticKernelMigration/AzureOpenAIAssistants/Step02_ToolCall/AzureOpenAIAssistants_Step02_ToolCall.csproj b/dotnet/samples/SemanticKernelMigration/AzureOpenAIAssistants/Step02_ToolCall/AzureOpenAIAssistants_Step02_ToolCall.csproj new file mode 100644 index 0000000000..dcf6e25469 --- /dev/null +++ b/dotnet/samples/SemanticKernelMigration/AzureOpenAIAssistants/Step02_ToolCall/AzureOpenAIAssistants_Step02_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/AzureOpenAIAssistants/Step02_ToolCall/Program.cs b/dotnet/samples/SemanticKernelMigration/AzureOpenAIAssistants/Step02_ToolCall/Program.cs new file mode 100644 index 0000000000..279a96c0af --- /dev/null +++ b/dotnet/samples/SemanticKernelMigration/AzureOpenAIAssistants/Step02_ToolCall/Program.cs @@ -0,0 +1,97 @@ +// Copyright (c) Microsoft. All rights reserved. + +#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. + +using System.ComponentModel; +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.Connectors.OpenAI; +using OpenAI; +using OpenAI.Assistants; + +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?"; + +[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."; + +Console.WriteLine($"User Input: {userInput}"); + +await SKAgent(); +await AFAgent(); + +async Task SKAgent() +{ + Console.WriteLine("\n=== SK Agent ===\n"); + + var builder = Kernel.CreateBuilder(); + var assistantsClient = new AzureOpenAIClient(new Uri(endpoint), new AzureCliCredential()).GetAssistantClient(); + + Assistant assistant = await assistantsClient.CreateAssistantAsync(deploymentName, + instructions: "You are a helpful assistant"); + + OpenAIAssistantAgent agent = new(assistant, assistantsClient) + { + Kernel = builder.Build(), + Arguments = new KernelArguments(new OpenAIPromptExecutionSettings() + { + MaxTokens = 1000, + FunctionChoiceBehavior = FunctionChoiceBehavior.Auto() + }), + }; + + // Initialize plugin and add to the agent's Kernel (same as direct Kernel usage). + agent.Kernel.Plugins.Add(KernelPluginFactory.CreateFromFunctions("KernelPluginName", [KernelFunctionFactory.CreateFromMethod(GetWeather)])); + + // Create a thread for the agent conversation. + var thread = new OpenAIAssistantAgentThread(assistantsClient); + + await foreach (var result in agent.InvokeAsync(userInput, thread)) + { + Console.WriteLine(result.Message); + } + + Console.WriteLine("---"); + await foreach (var update in agent.InvokeStreamingAsync(userInput, thread)) + { + Console.Write(update.Message); + } + + // Clean up + await thread.DeleteAsync(); + await assistantsClient.DeleteAssistantAsync(agent.Id); +} + +async Task AFAgent() +{ + Console.WriteLine("\n=== AF Agent ===\n"); + + var assistantClient = new AzureOpenAIClient(new Uri(endpoint), new AzureCliCredential()).GetAssistantClient(); + + var agent = await assistantClient.CreateAIAgentAsync(deploymentName, + instructions: "You are a helpful assistant", + tools: [AIFunctionFactory.Create(GetWeather)]); + + var thread = agent.GetNewThread(); + var agentOptions = new ChatClientAgentRunOptions(new() { MaxOutputTokens = 1000 }); + + 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); + } + + // Clean up + await assistantClient.DeleteThreadAsync(thread.ConversationId); + await assistantClient.DeleteAssistantAsync(agent.Id); +} diff --git a/dotnet/samples/SemanticKernelMigration/AzureOpenAIAssistants/Step03_DependencyInjection/AzureOpenAIAssistants_Step03_DependencyInjection.csproj b/dotnet/samples/SemanticKernelMigration/AzureOpenAIAssistants/Step03_DependencyInjection/AzureOpenAIAssistants_Step03_DependencyInjection.csproj new file mode 100644 index 0000000000..3f455d079f --- /dev/null +++ b/dotnet/samples/SemanticKernelMigration/AzureOpenAIAssistants/Step03_DependencyInjection/AzureOpenAIAssistants_Step03_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/AzureOpenAIAssistants/Step03_DependencyInjection/Program.cs b/dotnet/samples/SemanticKernelMigration/AzureOpenAIAssistants/Step03_DependencyInjection/Program.cs new file mode 100644 index 0000000000..add47b4954 --- /dev/null +++ b/dotnet/samples/SemanticKernelMigration/AzureOpenAIAssistants/Step03_DependencyInjection/Program.cs @@ -0,0 +1,99 @@ +// Copyright (c) Microsoft. All rights reserved. + +#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. + +using Azure.AI.OpenAI; +using Azure.Identity; +using Microsoft.Extensions.AI.Agents; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.SemanticKernel; +using Microsoft.SemanticKernel.Agents.OpenAI; +using Microsoft.SemanticKernel.Connectors.OpenAI; +using OpenAI; +using OpenAI.Assistants; + +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.AddSingleton((sp) => new AzureOpenAIClient(new Uri(endpoint), new AzureCliCredential()).GetAssistantClient()); + serviceCollection.AddKernel().AddAzureOpenAIChatClient(deploymentName, endpoint, new AzureCliCredential()); + serviceCollection.AddTransient((sp) => + { + var assistantsClient = sp.GetRequiredService(); + + Assistant assistant = assistantsClient.CreateAssistant(deploymentName, new() { Name = "Joker", Instructions = "You are good at telling jokes." }); + + return new OpenAIAssistantAgent(assistant, assistantsClient); + }); + + await using ServiceProvider serviceProvider = serviceCollection.BuildServiceProvider(); + var agent = serviceProvider.GetRequiredService(); + + // Create a thread for the agent conversation. + var assistantsClient = serviceProvider.GetRequiredService(); + var thread = new OpenAIAssistantAgentThread(assistantsClient); + var settings = new OpenAIPromptExecutionSettings() { MaxTokens = 1000 }; + var agentOptions = new OpenAIAssistantAgentInvokeOptions() { KernelArguments = new(settings) }; + + await foreach (var result in agent.InvokeAsync(userInput, thread, agentOptions)) + { + Console.WriteLine(result.Message); + } + + Console.WriteLine("---"); + await foreach (var update in agent.InvokeStreamingAsync(userInput, thread, agentOptions)) + { + Console.Write(update.Message); + } + + // Clean up + await thread.DeleteAsync(); + await assistantsClient.DeleteAssistantAsync(agent.Id); +} + +async Task AFAgent() +{ + Console.WriteLine("\n=== AF Agent ===\n"); + + var serviceCollection = new ServiceCollection(); + serviceCollection.AddSingleton((sp) => new AzureOpenAIClient(new Uri(endpoint), new AzureCliCredential()).GetAssistantClient()); + serviceCollection.AddTransient((sp) => + { + var assistantClient = sp.GetRequiredService(); + + var agent = assistantClient.CreateAIAgent(deploymentName, name: "Joker", instructions: "You are good at telling jokes."); + + return agent; + }); + + await using ServiceProvider serviceProvider = serviceCollection.BuildServiceProvider(); + var agent = serviceProvider.GetRequiredService(); + + var thread = agent.GetNewThread(); + var agentOptions = new ChatClientAgentRunOptions(new() { MaxOutputTokens = 1000 }); + + 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); + } + + // Clean up + var assistantClient = serviceProvider.GetRequiredService(); + await assistantClient.DeleteThreadAsync(thread.ConversationId); + await assistantClient.DeleteAssistantAsync(agent.Id); +} diff --git a/dotnet/samples/SemanticKernelMigration/AzureOpenAIAssistants/Step04_CodeInterpreter/AzureOpenAIAssistants_Step04_CodeInterpreter.csproj b/dotnet/samples/SemanticKernelMigration/AzureOpenAIAssistants/Step04_CodeInterpreter/AzureOpenAIAssistants_Step04_CodeInterpreter.csproj new file mode 100644 index 0000000000..3f455d079f --- /dev/null +++ b/dotnet/samples/SemanticKernelMigration/AzureOpenAIAssistants/Step04_CodeInterpreter/AzureOpenAIAssistants_Step04_CodeInterpreter.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/AzureOpenAIAssistants/Step04_CodeInterpreter/Program.cs b/dotnet/samples/SemanticKernelMigration/AzureOpenAIAssistants/Step04_CodeInterpreter/Program.cs new file mode 100644 index 0000000000..8805a52e38 --- /dev/null +++ b/dotnet/samples/SemanticKernelMigration/AzureOpenAIAssistants/Step04_CodeInterpreter/Program.cs @@ -0,0 +1,129 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System.Text; +using Azure.AI.OpenAI; +using Azure.Identity; +using Microsoft.Extensions.AI; +using Microsoft.SemanticKernel; +using Microsoft.SemanticKernel.Agents; +using Microsoft.SemanticKernel.Agents.OpenAI; +using OpenAI; +using OpenAI.Assistants; + +#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. +#pragma warning disable CS8321 // Local function is declared but never used + +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 = "Create a python code file using the code interpreter tool with a code ready to determine the values in the Fibonacci sequence that are less then the value of 101"; + +var assistantsClient = new AzureOpenAIClient(new Uri(endpoint), new AzureCliCredential()).GetAssistantClient(); + +Console.WriteLine($"User Input: {userInput}"); + +await SKAgent(); +await AFAgent(); + +async Task SKAgent() +{ + Console.WriteLine("\n=== SK Agent ===\n"); + + var builder = Kernel.CreateBuilder().AddAzureOpenAIChatClient(deploymentName, endpoint, new AzureCliCredential()); + + // Define the assistant + Assistant assistant = await assistantsClient.CreateAssistantAsync(deploymentName, enableCodeInterpreter: true); + + // Create the agent + OpenAIAssistantAgent agent = new(assistant, assistantsClient); + + // Create a thread for the agent conversation. + var thread = new OpenAIAssistantAgentThread(assistantsClient); + + // Respond to user input + await foreach (var content in agent.InvokeAsync(userInput, thread)) + { + if (!string.IsNullOrWhiteSpace(content.Message.Content)) + { + bool isCode = content.Message.Metadata?.ContainsKey(OpenAIAssistantAgent.CodeInterpreterMetadataKey) ?? false; + Console.WriteLine($"\n# {content.Message.Role}{(isCode ? "\n# Generated Code:\n" : ":")}{content.Message.Content}"); + } + + // Check for the citations + foreach (var item in content.Message.Items) + { + // Process each item in the message +#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. + if (item is AnnotationContent annotation) + { + if (annotation.Kind != AnnotationKind.UrlCitation) + { + Console.WriteLine($" [{item.GetType().Name}] {annotation.Label}: File #{annotation.ReferenceId}"); + } + } + else if (item is FileReferenceContent fileReference) + { + Console.WriteLine($" [{item.GetType().Name}] File #{fileReference.FileId}"); + } + } + } +#pragma warning restore SKEXP0110 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. + + // Clean up + await thread.DeleteAsync(); + await assistantsClient.DeleteAssistantAsync(agent.Id); +} + +async Task AFAgent() +{ + Console.WriteLine("\n=== AF Agent ===\n"); + + var agent = await assistantsClient.CreateAIAgentAsync(deploymentName, tools: [new HostedCodeInterpreterTool()]); + + var thread = agent.GetNewThread(); + + var result = await agent.RunAsync(userInput, thread); + Console.WriteLine(result); + + // Extracts via breaking glass the code generated by code interpreter tool + var chatResponse = result.RawRepresentation as ChatResponse; + StringBuilder generatedCode = new(); + foreach (object? updateRawRepresentation in chatResponse?.RawRepresentation as IEnumerable ?? []) + { + if (updateRawRepresentation is RunStepDetailsUpdate update && update.CodeInterpreterInput is not null) + { + generatedCode.Append(update.CodeInterpreterInput); + } + } + + if (!string.IsNullOrEmpty(generatedCode.ToString())) + { + Console.WriteLine($"\n# {chatResponse?.Messages[0].Role}:Generated Code:\n{generatedCode}"); + } + + // Check for the citations + foreach (var textContent in result.Messages[0].Contents.OfType()) + { + foreach (var annotation in textContent.Annotations ?? []) + { + if (annotation is CitationAnnotation citation) + { + if (citation.Url is null) + { + Console.WriteLine($" [{citation.GetType().Name}] {citation.Snippet}: File #{citation.FileId}"); + } + + foreach (var region in citation.AnnotatedRegions ?? []) + { + if (region is TextSpanAnnotatedRegion textSpanRegion) + { + Console.WriteLine($"\n[TextSpan Region] {textSpanRegion.StartIndex}-{textSpanRegion.EndIndex}"); + } + } + } + } + } + + // Clean up + await assistantsClient.DeleteThreadAsync(thread.ConversationId); + await assistantsClient.DeleteAssistantAsync(agent.Id); +} diff --git a/dotnet/samples/SemanticKernelMigration/OpenAI/Step01_Basics/Program.cs b/dotnet/samples/SemanticKernelMigration/OpenAI/Step01_Basics/Program.cs index c435d02058..ab16e47e94 100644 --- a/dotnet/samples/SemanticKernelMigration/OpenAI/Step01_Basics/Program.cs +++ b/dotnet/samples/SemanticKernelMigration/OpenAI/Step01_Basics/Program.cs @@ -55,6 +55,7 @@ async Task AFAgent() var agentOptions = new ChatClientAgentRunOptions(new() { MaxOutputTokens = 1000 }); var result = await agent.RunAsync(userInput, thread, agentOptions); + Console.WriteLine(result); Console.WriteLine("---"); await foreach (var update in agent.RunStreamingAsync(userInput, thread, agentOptions)) diff --git a/dotnet/samples/SemanticKernelMigration/OpenAIAssistants/Step01_Basics/Program.cs b/dotnet/samples/SemanticKernelMigration/OpenAIAssistants/Step01_Basics/Program.cs index 9d14e0ccbc..4d4898b0dc 100644 --- a/dotnet/samples/SemanticKernelMigration/OpenAIAssistants/Step01_Basics/Program.cs +++ b/dotnet/samples/SemanticKernelMigration/OpenAIAssistants/Step01_Basics/Program.cs @@ -73,7 +73,6 @@ async Task AFAgent() Console.Write(update); } - var yes = agent.GetService() is not null; // Clean up await assistantClient.DeleteThreadAsync(thread.ConversationId); await assistantClient.DeleteAssistantAsync(agent.Id); diff --git a/dotnet/samples/SemanticKernelMigration/README.md b/dotnet/samples/SemanticKernelMigration/README.md index b49bc4bad7..9b253aa1c1 100644 --- a/dotnet/samples/SemanticKernelMigration/README.md +++ b/dotnet/samples/SemanticKernelMigration/README.md @@ -312,6 +312,12 @@ $env:AZURE_FOUNDRY_PROJECT_ENDPOINT = "https://-resource.services. $env:OPENAI_API_KEY = "sk-..." ``` +**For Azure OpenAI and Azure OpenAI Assistants projects:** +```powershell +$env:AZUREOPENAI_ENDPOINT = "https://.cognitiveservices.azure.com/" +$env:AZUREOPENAI_DEPLOYMENT_NAME = "gpt-4o" # Optional, defaults to gpt-4o +``` + **Optional debug mode:** ```powershell $env:AF_SHOW_ALL_DEMO_SETTING_VALUES = "Y" @@ -326,6 +332,8 @@ The migration samples are organized into three categories, each demonstrating di |Category|Description| |---|---| |[AzureAIFoundry](./AzureAIFoundry/)|Azure OpenAI service integration samples| +|[AzureOpenAI](./AzureOpenAI/)|Direct Azure OpenAI API integration samples| +|[AzureOpenAIAssistants](./AzureOpenAIAssistants/)|Azure OpenAI Assistants API integration samples| |[OpenAI](./OpenAI/)|Direct OpenAI API integration samples| |[OpenAIAssistants](./OpenAIAssistants/)|OpenAI Assistant API integration samples| @@ -338,20 +346,19 @@ To run any migration sample, navigate to the desired sample directory: cd "AzureAIFoundry\Step01_Basics" dotnet run -cd "AzureAIFoundry\Step03_ToolCall" -dotnet run - # OpenAI Examples cd "OpenAI\Step01_Basics" dotnet run -cd "OpenAI\Step02_DependencyInjection" -dotnet run - # OpenAI Assistants Examples cd "OpenAIAssistants\Step01_Basics" dotnet run -cd "OpenAIAssistants\Step04_CodeInterpreter" +# Azure OpenAI Examples +cd "AzureOpenAI\Step01_Basics" +dotnet run + +# Azure OpenAI Assistants Examples +cd "AzureOpenAIAssistants\Step01_Basics" dotnet run ``` \ No newline at end of file