Skip to content

.Net: Concept sample showing how to switch deployments based on functions being called #10480

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
267babd
Concept sample showing how to switch deployments based on functions b…
markwallace-microsoft Feb 11, 2025
903ca5d
Fix warning
markwallace-microsoft Feb 11, 2025
c3c3bbb
Update dotnet/samples/Concepts/Filtering/AzureOpenAI_DeploymentSwitch.cs
markwallace-microsoft Feb 12, 2025
4feb829
Update dotnet/samples/Concepts/Filtering/AzureOpenAI_DeploymentSwitch.cs
markwallace-microsoft Feb 12, 2025
25c16c6
Update dotnet/samples/Concepts/Filtering/AzureOpenAI_DeploymentSwitch.cs
markwallace-microsoft Feb 12, 2025
c6b2fb8
Update dotnet/samples/Concepts/Filtering/AzureOpenAI_DeploymentSwitch.cs
markwallace-microsoft Feb 12, 2025
2d22fe5
Update dotnet/samples/Concepts/Filtering/AzureOpenAI_DeploymentSwitch.cs
markwallace-microsoft Feb 12, 2025
e0a4760
Merge branch 'main' into users/markwallace/concept_depoymentswitch
markwallace-microsoft Feb 12, 2025
f3fe7e9
Update dotnet/samples/Concepts/Filtering/AzureOpenAI_DeploymentSwitch.cs
markwallace-microsoft Feb 14, 2025
2b6c16f
Merge branch 'main' into users/markwallace/concept_depoymentswitch
markwallace-microsoft Feb 14, 2025
4c89e14
Merge branch 'main' into users/markwallace/concept_depoymentswitch
markwallace-microsoft Feb 17, 2025
4a9ee00
Fix formatting problem
markwallace-microsoft Feb 17, 2025
418d16f
Merge branch 'main' into users/markwallace/concept_depoymentswitch
markwallace-microsoft Feb 19, 2025
638d3ed
Merge branch 'main' into users/markwallace/concept_depoymentswitch
markwallace-microsoft Feb 24, 2025
2887a07
Switch to using execution settings
markwallace-microsoft Feb 24, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,32 @@ public class AzureOpenAI_ChatCompletion(ITestOutputHelper output) : BaseTest(out
[Fact]
public async Task ChatPromptAsync()
{
Assert.NotNull(TestConfiguration.Ollama.ModelId);
Assert.NotNull(TestConfiguration.AzureOpenAI.ChatDeploymentName);
Assert.NotNull(TestConfiguration.AzureOpenAI.Endpoint);

StringBuilder chatPrompt = new("""
<message role="system">You are a librarian, expert about books</message>
<message role="user">Hi, I'm looking for book suggestions</message>
""");

var kernel = Kernel.CreateBuilder()
.AddAzureOpenAIChatCompletion(
var kernelBuilder = Kernel.CreateBuilder();
if (string.IsNullOrEmpty(TestConfiguration.AzureOpenAI.ApiKey))
{
kernelBuilder.AddAzureOpenAIChatCompletion(
deploymentName: TestConfiguration.AzureOpenAI.ChatDeploymentName,
endpoint: TestConfiguration.AzureOpenAI.Endpoint,
credentials: new DefaultAzureCredential(),
modelId: TestConfiguration.AzureOpenAI.ChatModelId);
}
else
{
kernelBuilder.AddAzureOpenAIChatCompletion(
deploymentName: TestConfiguration.AzureOpenAI.ChatDeploymentName,
endpoint: TestConfiguration.AzureOpenAI.Endpoint,
apiKey: TestConfiguration.AzureOpenAI.ApiKey,
modelId: TestConfiguration.AzureOpenAI.ChatModelId)
.Build();
modelId: TestConfiguration.AzureOpenAI.ChatModelId);
}
var kernel = kernelBuilder.Build();

var reply = await kernel.InvokePromptAsync(chatPrompt.ToString());

Expand All @@ -44,7 +56,14 @@ public async Task ServicePromptAsync()
{
Console.WriteLine("======== Azure Open AI - Chat Completion ========");

AzureOpenAIChatCompletionService chatCompletionService = new(
AzureOpenAIChatCompletionService chatCompletionService =
string.IsNullOrEmpty(TestConfiguration.AzureOpenAI.ApiKey) ?
new(
deploymentName: TestConfiguration.AzureOpenAI.ChatDeploymentName,
endpoint: TestConfiguration.AzureOpenAI.Endpoint,
credentials: new DefaultAzureCredential(),
modelId: TestConfiguration.AzureOpenAI.ChatModelId) :
new(
deploymentName: TestConfiguration.AzureOpenAI.ChatDeploymentName,
endpoint: TestConfiguration.AzureOpenAI.Endpoint,
apiKey: TestConfiguration.AzureOpenAI.ApiKey,
Expand All @@ -62,10 +81,17 @@ public async Task DefaultAzureCredentialSampleAsync()
{
Console.WriteLine("======== Azure Open AI - Chat Completion with Azure Default Credential ========");

AzureOpenAIChatCompletionService chatCompletionService = new(
AzureOpenAIChatCompletionService chatCompletionService =
string.IsNullOrEmpty(TestConfiguration.AzureOpenAI.ApiKey) ?
new(
deploymentName: TestConfiguration.AzureOpenAI.ChatDeploymentName,
endpoint: TestConfiguration.AzureOpenAI.Endpoint,
credentials: new DefaultAzureCredential(),
modelId: TestConfiguration.AzureOpenAI.ChatModelId) :
new(
deploymentName: TestConfiguration.AzureOpenAI.ChatDeploymentName,
endpoint: TestConfiguration.AzureOpenAI.Endpoint,
credentials: new DefaultAzureCredential(),
apiKey: TestConfiguration.AzureOpenAI.ApiKey,
modelId: TestConfiguration.AzureOpenAI.ChatModelId);

await StartChatAsync(chatCompletionService);
Expand Down
116 changes: 116 additions & 0 deletions dotnet/samples/Concepts/Filtering/AzureOpenAI_DeploymentSwitch.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
// Copyright (c) Microsoft. All rights reserved.

using Azure.Identity;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel.Connectors.OpenAI;

namespace Filtering;

/// <summary>
/// This sample shows how to switch between Azure OpenAI deployments based on the functions that are being called.
/// This can be useful if semantic caching is enabled and you want to switch to a different deployment based on the functions that are being called.
/// </summary>
public class AzureOpenAI_DeploymentSwitch(ITestOutputHelper output) : BaseTest(output)
{
[Fact]
public async Task DeploymentSwitchAsync()
{
Assert.NotNull(TestConfiguration.AzureOpenAI.ChatDeploymentName);
Assert.NotNull(TestConfiguration.AzureOpenAI.Endpoint);

// Create a logging handler to output HTTP requests and responses
using var httpHandler = new HttpClientHandler();
using var loggingHandler = new LoggingHandler(httpHandler, this.Output);
using var httpClient = new HttpClient(loggingHandler);

// Create KernelBuilder with an auto function invocation filter
var kernelBuilder = Kernel.CreateBuilder();
kernelBuilder.Services.AddSingleton<IAutoFunctionInvocationFilter>(new AutoFunctionInvocationFilter(this.Output));

// Define the endpoints for the two Azure OpenAI services
var endpoint1 = "https://lightspeed-team-shared-openai-eastus.openai.azure.com/";
var endpoint2 = "https://lightspeed-team-shared-openai-swedencentral.openai.azure.com/";

// Add Azure OpenAI chat completion services
kernelBuilder.AddAzureOpenAIChatCompletion(
serviceId: "eastus",
deploymentName: "gpt-4o-mini",
endpoint: endpoint1,
credentials: new AzureCliCredential(),
httpClient: httpClient,
modelId: TestConfiguration.AzureOpenAI.ChatModelId);
kernelBuilder.AddAzureOpenAIChatCompletion(
serviceId: "swedencentral",
deploymentName: "gpt-4o",
endpoint: endpoint2,
credentials: new AzureCliCredential(),
httpClient: httpClient,
modelId: TestConfiguration.AzureOpenAI.ChatModelId);

var kernel = kernelBuilder.Build();

kernel.ImportPluginFromFunctions("HelperFunctions",
[
kernel.CreateFunctionFromMethod(() => "Brown", "GetEyeColor", "Retrieves eye color for the current user."),
kernel.CreateFunctionFromMethod(() => DateTime.UtcNow.ToString("R"), "GetCurrentDateTimeInUtc", "Retrieves the current date time in UTC."),
]);

OpenAIPromptExecutionSettings settings = new()
{
ServiceId = "swedencentral",
FunctionChoiceBehavior = FunctionChoiceBehavior.Auto()
};

var reply = await kernel.InvokePromptAsync("What time is it and what is my eye color and what time is it?", new(settings));

Console.WriteLine(reply);
}

private sealed class AutoFunctionInvocationFilter(ITestOutputHelper output) : IAutoFunctionInvocationFilter
{
public async Task OnAutoFunctionInvocationAsync(AutoFunctionInvocationContext context, Func<AutoFunctionInvocationContext, Task> next)
{
var kernel = context.Kernel;
var chatHistory = context.ChatHistory;
var executionSettings = context.ExecutionSettings;
var functionCalls = FunctionCallContent.GetFunctionCalls(context.ChatHistory.Last());

if (executionSettings is not null && "swedencentral".Equals(executionSettings.ServiceId, StringComparison.Ordinal))
{
bool includesGetEyeColor = functionCalls.Any(fc => fc.FunctionName.Equals("GetEyeColor", StringComparison.Ordinal));

// For the "GetEyeColor" function, switch to a different deployment.
// If the function is not present in the collection of function calls, proceed with the request as usual.
if (!includesGetEyeColor)
{
await next(context);
}
else
{
output.WriteLine("Switching to use eastus deployment");

chatHistory.RemoveAt(chatHistory.Count - 1);

IChatCompletionService chatCompletionService = kernel.Services.GetRequiredKeyedService<IChatCompletionService>("eastus");

OpenAIPromptExecutionSettings settings = new()
{
ServiceId = "eastus",
FunctionChoiceBehavior = FunctionChoiceBehavior.Auto()
};

var chatContent = await chatCompletionService.GetChatMessageContentAsync(chatHistory, settings, context.Kernel);

context.Result = new FunctionResult(context.Result, chatContent);
context.Terminate = true;
}
}
else
{
await next(context);
}
}
}
}
1 change: 1 addition & 0 deletions dotnet/samples/Concepts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ dotnet test -l "console;verbosity=detailed" --filter "FullyQualifiedName=ChatCom
- [PromptRenderFiltering](https://github.com/microsoft/semantic-kernel/blob/main/dotnet/samples/Concepts/Filtering/PromptRenderFiltering.cs)
- [RetryWithFilters](https://github.com/microsoft/semantic-kernel/blob/main/dotnet/samples/Concepts/Filtering/RetryWithFilters.cs)
- [TelemetryWithFilters](https://github.com/microsoft/semantic-kernel/blob/main/dotnet/samples/Concepts/Filtering/TelemetryWithFilters.cs)
- [AzureOpenAI_DeploymentSwitch](https://github.com/microsoft/semantic-kernel/blob/main/dotnet/samples/Concepts/Filtering/AzureOpenAI_DeploymentSwitch.cs)

### Functions - Invoking [`Method`](https://github.com/microsoft/semantic-kernel/blob/main/dotnet/src/SemanticKernel.Core/Functions/KernelFunctionFromMethod.cs) or [`Prompt`](https://github.com/microsoft/semantic-kernel/blob/main/dotnet/src/SemanticKernel.Core/Functions/KernelFunctionFromPrompt.cs) functions with [`Kernel`](https://github.com/microsoft/semantic-kernel/blob/main/dotnet/src/SemanticKernel.Abstractions/Kernel.cs)

Expand Down
Loading