From 14f38116484d833929402be1db7cbd00e2c0bef3 Mon Sep 17 00:00:00 2001
From: Roger Barreto <19890735+RogerBarreto@users.noreply.github.com>
Date: Wed, 18 Jun 2025 13:00:38 +0100
Subject: [PATCH] .Net: Add Azure OpenAI Agent Model Samples (#80)
* Improved Merge logic
* Add modularization for Model Samples and Azure OpenAI
* Address PR feedback
* Warning fix
* Address PR comments
---
dotnet/Directory.Packages.props | 3 +
dotnet/samples/GettingStarted/AgentSample.cs | 32 +++++++-
.../GettingStarted/GettingStarted.csproj | 2 +
...entAgent_With_AzureOpenAIChatCompletion.cs | 56 +++++++++++++
.../ChatClientAgent_With_OpenAIAssistant.cs | 8 +-
...atClientAgent_With_OpenAIChatCompletion.cs | 10 +--
.../GettingStarted/Steps/Step01_Running.cs | 44 ++++++-----
.../GettingStarted/Steps/Step02_UsingTools.cs | 36 +++++----
.../ChatCompletion/ChatClientAgent.cs | 78 +++++++++++--------
.../src/Shared/Samples/TestConfiguration.cs | 66 ++++++++++------
10 files changed, 235 insertions(+), 100 deletions(-)
create mode 100644 dotnet/samples/GettingStarted/Providers/ChatClientAgent_With_AzureOpenAIChatCompletion.cs
diff --git a/dotnet/Directory.Packages.props b/dotnet/Directory.Packages.props
index a295476d40..eb2578453b 100644
--- a/dotnet/Directory.Packages.props
+++ b/dotnet/Directory.Packages.props
@@ -5,6 +5,9 @@
true
+
+
+
diff --git a/dotnet/samples/GettingStarted/AgentSample.cs b/dotnet/samples/GettingStarted/AgentSample.cs
index 3ba34ac3fa..12cf9fa7af 100644
--- a/dotnet/samples/GettingStarted/AgentSample.cs
+++ b/dotnet/samples/GettingStarted/AgentSample.cs
@@ -1,5 +1,8 @@
// Copyright (c) Microsoft. All rights reserved.
+using System.ClientModel;
+using Azure.AI.OpenAI;
+using Azure.Identity;
using Microsoft.Extensions.AI;
using Microsoft.Shared.Samples;
using OpenAI;
@@ -8,8 +11,35 @@ namespace GettingStarted;
public class AgentSample(ITestOutputHelper output) : BaseSample(output)
{
- protected IChatClient GetOpenAIChatClient()
+ ///
+ /// Represents the available providers for instances.
+ ///
+ public enum ChatClientProviders
+ {
+ OpenAI,
+ AzureOpenAI,
+ }
+
+ protected IChatClient GetChatClient(ChatClientProviders provider)
+ {
+ return provider switch
+ {
+ ChatClientProviders.OpenAI => GetOpenAIChatClient(),
+ ChatClientProviders.AzureOpenAI => GetAzureOpenAIChatClient(),
+ _ => throw new NotSupportedException($"Provider {provider} is not supported.")
+ };
+ }
+
+ private IChatClient GetOpenAIChatClient()
=> new OpenAIClient(TestConfiguration.OpenAI.ApiKey)
.GetChatClient(TestConfiguration.OpenAI.ChatModelId)
.AsIChatClient();
+
+ private IChatClient GetAzureOpenAIChatClient()
+ => ((TestConfiguration.AzureOpenAI.ApiKey is null)
+ // Use Azure CLI credentials if API key is not provided.
+ ? new AzureOpenAIClient(TestConfiguration.AzureOpenAI.Endpoint, new AzureCliCredential())
+ : new AzureOpenAIClient(TestConfiguration.AzureOpenAI.Endpoint, new ApiKeyCredential(TestConfiguration.AzureOpenAI.ApiKey)))
+ .GetChatClient(TestConfiguration.AzureOpenAI.DeploymentName)
+ .AsIChatClient();
}
diff --git a/dotnet/samples/GettingStarted/GettingStarted.csproj b/dotnet/samples/GettingStarted/GettingStarted.csproj
index 908c71111b..ad71ab05d8 100644
--- a/dotnet/samples/GettingStarted/GettingStarted.csproj
+++ b/dotnet/samples/GettingStarted/GettingStarted.csproj
@@ -15,6 +15,8 @@
+
+
diff --git a/dotnet/samples/GettingStarted/Providers/ChatClientAgent_With_AzureOpenAIChatCompletion.cs b/dotnet/samples/GettingStarted/Providers/ChatClientAgent_With_AzureOpenAIChatCompletion.cs
new file mode 100644
index 0000000000..c3da844e96
--- /dev/null
+++ b/dotnet/samples/GettingStarted/Providers/ChatClientAgent_With_AzureOpenAIChatCompletion.cs
@@ -0,0 +1,56 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using System.ClientModel;
+using Azure.AI.OpenAI;
+using Azure.Identity;
+using Microsoft.Agents;
+using Microsoft.Extensions.AI;
+using Microsoft.Shared.Samples;
+
+namespace Providers;
+
+///
+/// End-to-end sample showing how to use with Azure OpenAI Chat Completion.
+///
+public sealed class ChatClientAgent_With_AzureOpenAIChatCompletion(ITestOutputHelper output) : AgentSample(output)
+{
+ private const string JokerName = "Joker";
+ private const string JokerInstructions = "You are good at telling jokes.";
+
+ [Fact]
+ public async Task RunWithChatCompletion()
+ {
+ // Get the chat client to use for the agent.
+ using var chatClient = ((TestConfiguration.AzureOpenAI.ApiKey is null)
+ // Use Azure CLI credentials if API key is not provided.
+ ? new AzureOpenAIClient(TestConfiguration.AzureOpenAI.Endpoint, new AzureCliCredential())
+ : new AzureOpenAIClient(TestConfiguration.AzureOpenAI.Endpoint, new ApiKeyCredential(TestConfiguration.AzureOpenAI.ApiKey)))
+ .GetChatClient(TestConfiguration.AzureOpenAI.DeploymentName)
+ .AsIChatClient();
+
+ // Define the agent
+ ChatClientAgent agent =
+ new(chatClient, new()
+ {
+ Name = JokerName,
+ Instructions = JokerInstructions,
+ });
+
+ // Start a new thread for the agent conversation.
+ AgentThread thread = agent.GetNewThread();
+
+ // Respond to user input
+ await RunAgentAsync("Tell me a joke about a pirate.");
+ await RunAgentAsync("Now add some emojis to the joke.");
+
+ // Local function to invoke agent and display the conversation messages for the thread.
+ async Task RunAgentAsync(string input)
+ {
+ this.WriteUserMessage(input);
+
+ var response = await agent.RunAsync(input, thread);
+
+ this.WriteResponseOutput(response);
+ }
+ }
+}
diff --git a/dotnet/samples/GettingStarted/Providers/ChatClientAgent_With_OpenAIAssistant.cs b/dotnet/samples/GettingStarted/Providers/ChatClientAgent_With_OpenAIAssistant.cs
index 148c71b605..650f727d5c 100644
--- a/dotnet/samples/GettingStarted/Providers/ChatClientAgent_With_OpenAIAssistant.cs
+++ b/dotnet/samples/GettingStarted/Providers/ChatClientAgent_With_OpenAIAssistant.cs
@@ -10,7 +10,7 @@ using OpenAI;
namespace Providers;
///
-/// Shows how to use with Open AI Assistants.
+/// End-to-end sample showing how to use with OpenAI Assistants.
///
public sealed class ChatClientAgent_With_OpenAIAssistant(ITestOutputHelper output) : AgentSample(output)
{
@@ -45,11 +45,11 @@ public sealed class ChatClientAgent_With_OpenAIAssistant(ITestOutputHelper outpu
AgentThread thread = agent.GetNewThread();
// Respond to user input
- await InvokeAgentAsync("Tell me a joke about a pirate.");
- await InvokeAgentAsync("Now add some emojis to the joke.");
+ await RunAgentAsync("Tell me a joke about a pirate.");
+ await RunAgentAsync("Now add some emojis to the joke.");
// Local function to invoke agent and display the conversation messages for the thread.
- async Task InvokeAgentAsync(string input)
+ async Task RunAgentAsync(string input)
{
this.WriteUserMessage(input);
diff --git a/dotnet/samples/GettingStarted/Providers/ChatClientAgent_With_OpenAIChatCompletion.cs b/dotnet/samples/GettingStarted/Providers/ChatClientAgent_With_OpenAIChatCompletion.cs
index b2ba8c6982..84062d47f8 100644
--- a/dotnet/samples/GettingStarted/Providers/ChatClientAgent_With_OpenAIChatCompletion.cs
+++ b/dotnet/samples/GettingStarted/Providers/ChatClientAgent_With_OpenAIChatCompletion.cs
@@ -8,7 +8,7 @@ using OpenAI;
namespace Providers;
///
-/// Shows how to use with Open AI Chat Completion.
+/// End-to-end sample showing how to use with OpenAI Chat Completion.
///
public sealed class ChatClientAgent_With_OpenAIChatCompletion(ITestOutputHelper output) : AgentSample(output)
{
@@ -16,7 +16,7 @@ public sealed class ChatClientAgent_With_OpenAIChatCompletion(ITestOutputHelper
private const string JokerInstructions = "You are good at telling jokes.";
[Fact]
- public async Task RunWithOpenAIAssistant()
+ public async Task RunWithChatCompletion()
{
// Get the chat client to use for the agent.
using var chatClient = new OpenAIClient(TestConfiguration.OpenAI.ApiKey)
@@ -35,11 +35,11 @@ public sealed class ChatClientAgent_With_OpenAIChatCompletion(ITestOutputHelper
AgentThread thread = agent.GetNewThread();
// Respond to user input
- await InvokeAgentAsync("Tell me a joke about a pirate.");
- await InvokeAgentAsync("Now add some emojis to the joke.");
+ await RunAgentAsync("Tell me a joke about a pirate.");
+ await RunAgentAsync("Now add some emojis to the joke.");
// Local function to invoke agent and display the conversation messages for the thread.
- async Task InvokeAgentAsync(string input)
+ async Task RunAgentAsync(string input)
{
this.WriteUserMessage(input);
diff --git a/dotnet/samples/GettingStarted/Steps/Step01_Running.cs b/dotnet/samples/GettingStarted/Steps/Step01_Running.cs
index abc27459db..9f5fca31db 100644
--- a/dotnet/samples/GettingStarted/Steps/Step01_Running.cs
+++ b/dotnet/samples/GettingStarted/Steps/Step01_Running.cs
@@ -22,11 +22,13 @@ public sealed class Step01_Running(ITestOutputHelper output) : AgentSample(outpu
/// Demonstrate the usage of where each invocation is
/// a unique interaction with no conversation history between them.
///
- [Fact]
- public async Task RunWithoutThread()
+ [Theory]
+ [InlineData(ChatClientProviders.OpenAI)]
+ [InlineData(ChatClientProviders.AzureOpenAI)]
+ public async Task RunWithoutThread(ChatClientProviders provider)
{
// Get the chat client to use for the agent.
- using var chatClient = base.GetOpenAIChatClient();
+ using var chatClient = base.GetChatClient(provider);
// Define the agent
ChatClientAgent agent =
@@ -37,12 +39,12 @@ public sealed class Step01_Running(ITestOutputHelper output) : AgentSample(outpu
});
// Respond to user input
- await InvokeAgentAsync("Fortune favors the bold.");
- await InvokeAgentAsync("I came, I saw, I conquered.");
- await InvokeAgentAsync("Practice makes perfect.");
+ await RunAgentAsync("Fortune favors the bold.");
+ await RunAgentAsync("I came, I saw, I conquered.");
+ await RunAgentAsync("Practice makes perfect.");
// Local function to invoke agent and display the conversation messages.
- async Task InvokeAgentAsync(string input)
+ async Task RunAgentAsync(string input)
{
this.WriteUserMessage(input);
@@ -54,11 +56,13 @@ public sealed class Step01_Running(ITestOutputHelper output) : AgentSample(outpu
///
/// Demonstrate the usage of where a conversation history is maintained.
///
- [Fact]
- public async Task RunWithConversationThread()
+ [Theory]
+ [InlineData(ChatClientProviders.OpenAI)]
+ [InlineData(ChatClientProviders.AzureOpenAI)]
+ public async Task RunWithConversationThread(ChatClientProviders provider)
{
// Get the chat client to use for the agent.
- using var chatClient = base.GetOpenAIChatClient();
+ using var chatClient = base.GetChatClient(provider);
// Define the agent
ChatClientAgent agent =
@@ -72,11 +76,11 @@ public sealed class Step01_Running(ITestOutputHelper output) : AgentSample(outpu
AgentThread thread = agent.GetNewThread();
// Respond to user input
- await InvokeAgentAsync("Tell me a joke about a pirate.");
- await InvokeAgentAsync("Now add some emojis to the joke.");
+ await RunAgentAsync("Tell me a joke about a pirate.");
+ await RunAgentAsync("Now add some emojis to the joke.");
// Local function to invoke agent and display the conversation messages for the thread.
- async Task InvokeAgentAsync(string input)
+ async Task RunAgentAsync(string input)
{
this.WriteUserMessage(input);
@@ -90,11 +94,13 @@ public sealed class Step01_Running(ITestOutputHelper output) : AgentSample(outpu
/// Demonstrate the usage of in streaming mode,
/// where a conversation is maintained by the .
///
- [Fact]
- public async Task StreamingRunWithConversationThread()
+ [Theory]
+ [InlineData(ChatClientProviders.OpenAI)]
+ [InlineData(ChatClientProviders.AzureOpenAI)]
+ public async Task StreamingRunWithConversationThread(ChatClientProviders provider)
{
// Get the chat client to use for the agent.
- using var chatClient = base.GetOpenAIChatClient();
+ using var chatClient = base.GetChatClient(provider);
// Define the agent
ChatClientAgent agent =
@@ -108,11 +114,11 @@ public sealed class Step01_Running(ITestOutputHelper output) : AgentSample(outpu
AgentThread thread = agent.GetNewThread();
// Respond to user input
- await InvokeAgentAsync("Tell me a joke about a pirate.");
- await InvokeAgentAsync("Now add some emojis to the joke.");
+ await RunAgentAsync("Tell me a joke about a pirate.");
+ await RunAgentAsync("Now add some emojis to the joke.");
// Local function to invoke agent and display the conversation messages.
- async Task InvokeAgentAsync(string input)
+ async Task RunAgentAsync(string input)
{
this.WriteUserMessage(input);
diff --git a/dotnet/samples/GettingStarted/Steps/Step02_UsingTools.cs b/dotnet/samples/GettingStarted/Steps/Step02_UsingTools.cs
index a653819591..a97299927a 100644
--- a/dotnet/samples/GettingStarted/Steps/Step02_UsingTools.cs
+++ b/dotnet/samples/GettingStarted/Steps/Step02_UsingTools.cs
@@ -8,11 +8,13 @@ namespace Steps;
public sealed class Step02_UsingTools(ITestOutputHelper output) : AgentSample(output)
{
- [Fact]
- public async Task RunningWithTools()
+ [Theory]
+ [InlineData(ChatClientProviders.OpenAI)]
+ [InlineData(ChatClientProviders.AzureOpenAI)]
+ public async Task RunningWithTools(ChatClientProviders provider)
{
// Get the chat client to use for the agent.
- using var chatClient = base.GetOpenAIChatClient();
+ using var chatClient = base.GetChatClient(provider);
// Define the agent
var menuTools = new MenuTools();
@@ -35,12 +37,12 @@ public sealed class Step02_UsingTools(ITestOutputHelper output) : AgentSample(ou
var thread = agent.GetNewThread();
// Respond to user input, invoking functions where appropriate.
- await InvokeAgentAsync("Hello");
- await InvokeAgentAsync("What is the special soup and its price?");
- await InvokeAgentAsync("What is the special drink and its price?");
- await InvokeAgentAsync("Thank you");
+ await RunAgentAsync("Hello");
+ await RunAgentAsync("What is the special soup and its price?");
+ await RunAgentAsync("What is the special drink and its price?");
+ await RunAgentAsync("Thank you");
- async Task InvokeAgentAsync(string input)
+ async Task RunAgentAsync(string input)
{
this.WriteUserMessage(input);
var response = await agent.RunAsync(input, thread);
@@ -48,11 +50,13 @@ public sealed class Step02_UsingTools(ITestOutputHelper output) : AgentSample(ou
}
}
- [Fact]
- public async Task StreamingRunWithTools()
+ [Theory]
+ [InlineData(ChatClientProviders.OpenAI)]
+ [InlineData(ChatClientProviders.AzureOpenAI)]
+ public async Task StreamingRunWithTools(ChatClientProviders provider)
{
// Get the chat client to use for the agent.
- using var chatClient = base.GetOpenAIChatClient();
+ using var chatClient = base.GetChatClient(provider);
// Define the agent
var menuTools = new MenuTools();
@@ -75,12 +79,12 @@ public sealed class Step02_UsingTools(ITestOutputHelper output) : AgentSample(ou
var thread = agent.GetNewThread();
// Respond to user input, invoking functions where appropriate.
- await InvokeAgentAsync("Hello");
- await InvokeAgentAsync("What is the special soup and its price?");
- await InvokeAgentAsync("What is the special drink and its price?");
- await InvokeAgentAsync("Thank you");
+ await RunAgentAsync("Hello");
+ await RunAgentAsync("What is the special soup and its price?");
+ await RunAgentAsync("What is the special drink and its price?");
+ await RunAgentAsync("Thank you");
- async Task InvokeAgentAsync(string input)
+ async Task RunAgentAsync(string input)
{
this.WriteUserMessage(input);
await foreach (var update in agent.RunStreamingAsync(input, thread))
diff --git a/dotnet/src/Microsoft.Agents/ChatCompletion/ChatClientAgent.cs b/dotnet/src/Microsoft.Agents/ChatCompletion/ChatClientAgent.cs
index 1c521863c8..bb765626eb 100644
--- a/dotnet/src/Microsoft.Agents/ChatCompletion/ChatClientAgent.cs
+++ b/dotnet/src/Microsoft.Agents/ChatCompletion/ChatClientAgent.cs
@@ -117,10 +117,10 @@ public sealed class ChatClientAgent : Agent
AgentRunOptions? options = null,
[EnumeratorCancellation] CancellationToken cancellationToken = default)
{
- Throw.IfNull(messages);
+ var inputMessages = Throw.IfNull(messages);
(ChatClientAgentThread chatClientThread, ChatOptions? chatOptions, List threadMessages) =
- await this.PrepareThreadAndMessagesAsync(thread, messages, options, cancellationToken).ConfigureAwait(false);
+ await this.PrepareThreadAndMessagesAsync(thread, inputMessages, options, cancellationToken).ConfigureAwait(false);
int messageCount = threadMessages.Count;
var agentName = this.GetAgentName();
@@ -158,7 +158,7 @@ public sealed class ChatClientAgent : Agent
this.UpdateThreadWithTypeAndConversationId(chatClientThread, chatResponse.ConversationId);
// To avoid inconsistent state we only notify the thread of the input messages if no error occurs after the initial request.
- await this.NotifyThreadOfNewMessagesAsync(chatClientThread, messages, cancellationToken).ConfigureAwait(false);
+ await this.NotifyThreadOfNewMessagesAsync(chatClientThread, inputMessages, cancellationToken).ConfigureAwait(false);
await this.NotifyThreadOfNewMessagesAsync(chatClientThread, chatResponseMessages, cancellationToken).ConfigureAwait(false);
if (options?.OnIntermediateMessages is not null)
@@ -198,28 +198,33 @@ public sealed class ChatClientAgent : Agent
}
// If both are present, we need to merge them.
-
// The merge strategy will prioritize the request options over the agent options,
// and will fill the blanks with agent options where the request options were not set.
-
- // Merge only the additional properties from the agent if they are not already set in the request options.
- if (requestChatOptions.AdditionalProperties is not null && this._agentOptions.ChatOptions.AdditionalProperties is not null)
- {
- foreach (var property in this._agentOptions.ChatOptions.AdditionalProperties.Keys)
- {
- requestChatOptions.AdditionalProperties.TryAdd(property, this._agentOptions.ChatOptions.AdditionalProperties[property]);
- }
- }
- else
- {
- requestChatOptions.AdditionalProperties ??= this._agentOptions.ChatOptions.AdditionalProperties;
- }
requestChatOptions.AllowMultipleToolCalls ??= this._agentOptions.ChatOptions.AllowMultipleToolCalls;
requestChatOptions.ConversationId ??= this._agentOptions.ChatOptions.ConversationId;
requestChatOptions.FrequencyPenalty ??= this._agentOptions.ChatOptions.FrequencyPenalty;
requestChatOptions.MaxOutputTokens ??= this._agentOptions.ChatOptions.MaxOutputTokens;
requestChatOptions.ModelId ??= this._agentOptions.ChatOptions.ModelId;
requestChatOptions.PresencePenalty ??= this._agentOptions.ChatOptions.PresencePenalty;
+ requestChatOptions.ResponseFormat ??= this._agentOptions.ChatOptions.ResponseFormat;
+ requestChatOptions.Seed ??= this._agentOptions.ChatOptions.Seed;
+ requestChatOptions.Temperature ??= this._agentOptions.ChatOptions.Temperature;
+ requestChatOptions.TopP ??= this._agentOptions.ChatOptions.TopP;
+ requestChatOptions.TopK ??= this._agentOptions.ChatOptions.TopK;
+ requestChatOptions.ToolMode ??= this._agentOptions.ChatOptions.ToolMode;
+
+ // Merge only the additional properties from the agent if they are not already set in the request options.
+ if (requestChatOptions.AdditionalProperties is not null && this._agentOptions.ChatOptions.AdditionalProperties is not null)
+ {
+ foreach (var propertyKey in this._agentOptions.ChatOptions.AdditionalProperties.Keys)
+ {
+ requestChatOptions.AdditionalProperties.TryAdd(propertyKey, this._agentOptions.ChatOptions.AdditionalProperties[propertyKey]);
+ }
+ }
+ else
+ {
+ requestChatOptions.AdditionalProperties ??= this._agentOptions.ChatOptions.AdditionalProperties?.Clone();
+ }
// Chain the raw representation factory from the request options with the agent's factory if available.
if (this._agentOptions.ChatOptions.RawRepresentationFactory is { } agentFactory)
@@ -229,41 +234,52 @@ public sealed class ChatClientAgent : Agent
: agentFactory;
}
- requestChatOptions.ResponseFormat ??= this._agentOptions.ChatOptions.ResponseFormat;
- requestChatOptions.Seed ??= this._agentOptions.ChatOptions.Seed;
-
// We concatenate the request stop sequences with the agent's stop sequences when available.
if (this._agentOptions.ChatOptions.StopSequences is { Count: not 0 })
{
if (requestChatOptions.StopSequences is null || requestChatOptions.StopSequences.Count == 0)
{
// If the request stop sequences are not set or empty, we use the agent's stop sequences directly.
- requestChatOptions.StopSequences = this._agentOptions.ChatOptions.StopSequences.ToArray();
+ requestChatOptions.StopSequences = [.. this._agentOptions.ChatOptions.StopSequences];
+ }
+ else if (requestChatOptions.StopSequences is List requestStopSequences)
+ {
+ // If the request stop sequences are set, we concatenate them with the agent's stop sequences.
+ requestStopSequences.AddRange(this._agentOptions.ChatOptions.StopSequences);
}
else
{
// If both agent's and request's stop sequences are set, we concatenate them.
- requestChatOptions.StopSequences = [.. requestChatOptions.StopSequences, .. this._agentOptions.ChatOptions.StopSequences];
+ foreach (string stopSequence in this._agentOptions.ChatOptions.StopSequences)
+ {
+ requestChatOptions.StopSequences.Add(stopSequence);
+ }
}
}
- requestChatOptions.Temperature ??= this._agentOptions.ChatOptions.Temperature;
- requestChatOptions.TopP ??= this._agentOptions.ChatOptions.TopP;
- requestChatOptions.TopK ??= this._agentOptions.ChatOptions.TopK;
- requestChatOptions.ToolMode ??= this._agentOptions.ChatOptions.ToolMode;
-
// We concatenate the request tools with the agent's tools when available.
if (this._agentOptions.ChatOptions.Tools is { Count: not 0 })
{
if (requestChatOptions.Tools is not { Count: > 0 })
{
- // If the request tools are not set or empty, we use the agent's tools directly.
- requestChatOptions.Tools = this._agentOptions.ChatOptions.Tools;
+ // If the request tools are not set or empty, we use the agent's tools.
+ requestChatOptions.Tools = [.. this._agentOptions.ChatOptions.Tools];
}
else
{
- // If the both agent's and request's tools are set, we concatenate all tools.
- requestChatOptions.Tools = [.. requestChatOptions.Tools, .. this._agentOptions.ChatOptions.Tools];
+ if (requestChatOptions.Tools is List requestTools)
+ {
+ // If the request tools are set, we concatenate them with the agent's tools.
+ requestTools.AddRange(this._agentOptions.ChatOptions.Tools);
+ }
+ else
+ {
+ // If the both agent's and request's tools are set, we concatenate all tools.
+ foreach (var tool in this._agentOptions.ChatOptions.Tools)
+ {
+ requestChatOptions.Tools.Add(tool);
+ }
+ }
}
}
diff --git a/dotnet/src/Shared/Samples/TestConfiguration.cs b/dotnet/src/Shared/Samples/TestConfiguration.cs
index 945fece430..58c831d079 100644
--- a/dotnet/src/Shared/Samples/TestConfiguration.cs
+++ b/dotnet/src/Shared/Samples/TestConfiguration.cs
@@ -5,17 +5,42 @@ using Microsoft.Extensions.Configuration;
namespace Microsoft.Shared.Samples;
+#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable.
+
///
-/// Provides a centralized configuration management system for accessing application settings.
+/// Provides access to application configuration settings.
///
public sealed class TestConfiguration
{
- private readonly IConfigurationRoot _configRoot;
- private static TestConfiguration? s_instance;
+ /// Gets the configuration settings for the OpenAI integration.
+ public static OpenAIConfig OpenAI => LoadSection();
- private TestConfiguration(IConfigurationRoot configRoot)
+ /// Gets the configuration settings for the Azure OpenAI integration.
+ public static AzureOpenAIConfig AzureOpenAI => LoadSection();
+
+ /// Represents the configuration settings required to interact with the OpenAI service.
+ public class OpenAIConfig
{
- this._configRoot = configRoot;
+ /// Gets or sets the identifier for the chat completion model used in the application.
+ public string ChatModelId { get; set; }
+
+ /// Gets or sets the API key used for authentication with the OpenAI service.
+ public string ApiKey { get; set; }
+ }
+
+ ///
+ /// Represents the configuration settings required to interact with the Azure OpenAI service.
+ ///
+ public class AzureOpenAIConfig
+ {
+ /// Gets the URI endpoint used to connect to the service.
+ public Uri Endpoint { get; set; }
+
+ /// Gets or sets the name of the deployment.
+ public string DeploymentName { get; set; }
+
+ /// Gets or sets the API key used for authentication with the OpenAI service.
+ public string? ApiKey { get; set; }
}
///
@@ -27,15 +52,19 @@ public sealed class TestConfiguration
s_instance = new TestConfiguration(configRoot);
}
+ #region Private Members
+ private readonly IConfigurationRoot _configRoot;
+ private static TestConfiguration? s_instance;
+
+ private TestConfiguration(IConfigurationRoot configRoot)
+ {
+ this._configRoot = configRoot;
+ }
+
///
/// Provides access to the configuration root for the application.
///
- public static IConfigurationRoot? ConfigurationRoot => s_instance?._configRoot;
-
- ///
- /// Gets the configuration settings for the OpenAI integration.
- ///
- public static OpenAIConfig OpenAI => LoadSection();
+ private static IConfigurationRoot? ConfigurationRoot => s_instance?._configRoot;
///
/// Retrieves a configuration section based on the specified key.
@@ -43,7 +72,7 @@ public sealed class TestConfiguration
/// The key identifying the configuration section to retrieve. Cannot be null or empty.
/// The corresponding to the specified key.
/// Thrown if the configuration root is not initialized or the specified key does not correspond to a valid section.
- public static IConfigurationSection GetSection(string caller)
+ private static IConfigurationSection GetSection(string caller)
{
return s_instance?._configRoot.GetSection(caller) ??
throw new InvalidOperationException(caller);
@@ -66,16 +95,5 @@ public sealed class TestConfiguration
throw new InvalidOperationException(caller);
}
- /// Represents the configuration settings required to interact with the OpenAI service.
- public class OpenAIConfig
- {
- /// Gets or sets the identifier for the chat completion model used in the application.
- public string? ChatModelId { get; set; }
-
- /// Gets or sets the identifier for the embedding model used in the application.
- public string? EmbeddingModelId { get; set; }
-
- /// Gets or sets the API key used for authentication with the OpenAI service.
- public string? ApiKey { get; set; }
- }
+ #endregion
}