From 6dcf24158fd8248f7a63f0147f960e8d983615cc Mon Sep 17 00:00:00 2001
From: Roger Barreto <19890735+rogerbarreto@users.noreply.github.com>
Date: Thu, 23 Oct 2025 12:03:25 +0100
Subject: [PATCH 01/16] Move packages
---
dotnet/agent-framework-dotnet.slnx | 1 +
dotnet/nuget.config | 4 +-
.../Microsoft.Agents.AI.AzureAI.csproj | 5 +-
.../AgentsClientExtensions.cs} | 402 ++++--------------
.../AzureAIAgent.cs | 127 ++++++
.../Microsoft.Agents.AI.AzureAIAgents.csproj | 29 ++
.../OpenAIResponseFixture.cs | 2 +-
7 files changed, 243 insertions(+), 327 deletions(-)
rename dotnet/src/{Microsoft.Agents.AI.AzureAI/New/PersistentAgentsClientExtensions.cs => Microsoft.Agents.AI.AzureAIAgents/AgentsClientExtensions.cs} (56%)
create mode 100644 dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AzureAIAgent.cs
create mode 100644 dotnet/src/Microsoft.Agents.AI.AzureAIAgents/Microsoft.Agents.AI.AzureAIAgents.csproj
diff --git a/dotnet/agent-framework-dotnet.slnx b/dotnet/agent-framework-dotnet.slnx
index eb73146202..12285616fc 100644
--- a/dotnet/agent-framework-dotnet.slnx
+++ b/dotnet/agent-framework-dotnet.slnx
@@ -251,6 +251,7 @@
+
diff --git a/dotnet/nuget.config b/dotnet/nuget.config
index 62c87f185c..543e8497d7 100644
--- a/dotnet/nuget.config
+++ b/dotnet/nuget.config
@@ -3,13 +3,13 @@
-
+
-
+
diff --git a/dotnet/src/Microsoft.Agents.AI.AzureAI/Microsoft.Agents.AI.AzureAI.csproj b/dotnet/src/Microsoft.Agents.AI.AzureAI/Microsoft.Agents.AI.AzureAI.csproj
index 5300c98b14..3161d893a6 100644
--- a/dotnet/src/Microsoft.Agents.AI.AzureAI/Microsoft.Agents.AI.AzureAI.csproj
+++ b/dotnet/src/Microsoft.Agents.AI.AzureAI/Microsoft.Agents.AI.AzureAI.csproj
@@ -1,19 +1,18 @@
-
+
$(ProjectsTargetFrameworks)
$(ProjectsDebugTargetFrameworks)
preview
enable
+ true
-
-
diff --git a/dotnet/src/Microsoft.Agents.AI.AzureAI/New/PersistentAgentsClientExtensions.cs b/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AgentsClientExtensions.cs
similarity index 56%
rename from dotnet/src/Microsoft.Agents.AI.AzureAI/New/PersistentAgentsClientExtensions.cs
rename to dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AgentsClientExtensions.cs
index 800324e66f..5552e0c732 100644
--- a/dotnet/src/Microsoft.Agents.AI.AzureAI/New/PersistentAgentsClientExtensions.cs
+++ b/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AgentsClientExtensions.cs
@@ -1,15 +1,17 @@
// Copyright (c) Microsoft. All rights reserved.
using Microsoft.Agents.AI;
+using Microsoft.Agents.AI.AzureAIAgents;
using Microsoft.Extensions.AI;
+using Microsoft.Shared.Diagnostics;
using OpenAI.Responses;
#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.
-namespace Azure.AI.Agents.Persistent;
+namespace Azure.AI.Agents;
///
-/// Provides extension methods for .
+/// Provides extension methods for .
///
public static class AgentsClientExtensions
{
@@ -273,29 +275,32 @@ public static async Task GetAIAgentAsync(
}*/
///
- /// Creates a new server side agent using the provided .
+ /// Creates a new server side agent using the provided .
///
- /// The to create the agent with.
+ /// The to create the agent with.
/// The model to be used by the agent.
/// The name of the agent.
/// The instructions for the agent.
/// The tools to be used by the agent.
/// The temperature setting for the agent.
/// The top-p setting for the agent.
- /// The response format for the agent.
+ /// The response format for the agent.
+ /// The reasoning options for the agent.
+ /// The text options for the agent.
+ /// The structured inputs for the agent.
/// The metadata for the agent.
/// Provides a way to customize the creation of the underlying used by the agent.
/// The to monitor for cancellation requests. The default is .
- /// A instance that can be used to perform operations on the newly created agent.
- public static async Task CreateAIAgentAsync(
- this AgentsClient client,
+ /// A instance that can be used to perform operations on the newly created agent.
+ public static AIAgent CreateAIAgent(
+ this AgentsClient agentsClient,
string model,
string? name = null,
string? instructions = null,
IEnumerable? tools = null,
float? temperature = null,
float? topP = null,
- RaiConfig raiConfig = null,
+ RaiConfig? raiConfig = null,
ResponseReasoningOptions? reasoningOptions = null,
ResponseTextOptions? textOptions = null,
IDictionary? structuredInputs = null,
@@ -303,360 +308,115 @@ public static async Task CreateAIAgentAsync(
Func? clientFactory = null,
CancellationToken cancellationToken = default)
{
- if (client is null)
- {
- throw new ArgumentNullException(nameof(client));
- }
-
- var openAIClient = client.GetOpenAIClient();
- var chatClient = openAIClient.GetOpenAIResponseClient(model).AsIChatClient();
-
- var promptAgentDefinition = new PromptAgentDefinition(model)
- {
- Instructions = instructions,
- Temperature = temperature,
- TopP = topP,
- RaiConfig = raiConfig,
- ReasoningOptions = reasoningOptions,
- TextOptions = textOptions,
- };
-
- var versionCreation = new AgentVersionCreationOptions();
- if (metadata is not null)
- {
- foreach (var kvp in metadata)
- {
- versionCreation.Metadata.Add(kvp.Key, kvp.Value);
- }
- }
-
- AgentVersion newAgentVersion = await client.CreateAgentVersionAsync(name, promptAgentDefinition, versionCreation, cancellationToken).ConfigureAwait(false);
+ Throw.IfNull(agentsClient);
- if (tools is not null)
- {
- if (promptAgentDefinition.Tools is List toolsList)
- {
- toolsList.AddRange(tools);
- }
- else
- {
- foreach (var tool in tools)
- {
- promptAgentDefinition.Tools.Add(tool);
- }
- }
- }
+ var (promptAgentDefinition, versionCreationOptions) = CreatePromptAgentDefinitionAndOptions(
+ model, instructions, temperature, topP, raiConfig, reasoningOptions, textOptions, tools, structuredInputs, metadata);
- if (structuredInputs is not null)
- {
- foreach (var kvp in structuredInputs)
- {
- promptAgentDefinition.StructuredInputs.Add(kvp.Key, kvp.Value);
- }
- }
-
- var agent = new ChatClientAgent(chatClient);
- agent.AsBuilder().Use(FoundryAgentMiddlewareAsync).Build();
-
- async Task FoundryAgentMiddlewareAsync(IEnumerable messages, AgentThread? thread, AgentRunOptions? options, Func, AgentThread?, AgentRunOptions?, CancellationToken, Task> sharedFunc, CancellationToken cancellationToken)
- {
- if (options is not ChatClientAgentRunOptions chatClientOptions)
- {
- throw new InvalidOperationException("The provided AgentRunOptions is not of type ChatClientAgentRunOptions.");
- }
-
- ChatClientAgentThread? chatClientThread = null;
- if (thread is not null)
- {
- if (thread is not ChatClientAgentThread asChatClientAgentThread)
- {
- throw new InvalidOperationException("The provided AgentThread is not of type ChatClientAgentThread.");
- }
-
- if (string.IsNullOrWhiteSpace(asChatClientAgentThread.ConversationId))
- {
- throw new InvalidOperationException("The ChatClientAgentThread does not have a valid ConversationId.");
- }
-
- chatClientThread = asChatClientAgentThread;
- }
-
- var conversation = (chatClientThread is not null)
- ? await client.GetConversationsClient().GetConversationAsync(chatClientThread.ConversationId, cancellationToken).ConfigureAwait(false)
- : await client.GetConversationsClient().CreateConversationAsync(cancellationToken: cancellationToken).ConfigureAwait(false);
-
- chatClientOptions.ChatOptions ??= new();
- chatClientOptions.ChatOptions.RawRepresentationFactory = (client) =>
- {
- var rawRepresentationFactory = chatClientOptions.ChatOptions?.RawRepresentationFactory;
- ResponseCreationOptions? responseCreationOptions = null;
-
- if (rawRepresentationFactory is not null)
- {
- responseCreationOptions = rawRepresentationFactory.Invoke(chatClient) as ResponseCreationOptions;
-
- if (responseCreationOptions is null)
- {
- throw new InvalidOperationException("The RawRepresentationFactory did not return a valid ResponseCreationOptions instance.");
- }
- }
- else
- {
- responseCreationOptions = new ResponseCreationOptions();
- }
-
- responseCreationOptions.SetAgentReference(name);
- responseCreationOptions.SetConversationReference(conversation);
-
- return responseCreationOptions;
- };
-
- await sharedFunc(messages, thread, options, cancellationToken).ConfigureAwait(false);
- }
-
- return agent;
+ AgentVersion agentVersion = agentsClient.CreateAgentVersion(name, promptAgentDefinition, versionCreationOptions, cancellationToken);
+ IChatClient chatClient = agentsClient.GetOpenAIClient().GetOpenAIResponseClient(model).AsIChatClient();
+ return new AzureAIAgent(agentsClient, agentVersion, new ChatClientAgent(chatClient));
}
///
- /// Creates a new server side agent using the provided .
+ /// Creates a new server side agent using the provided .
///
- /// The to create the agent with.
+ /// The to create the agent with.
/// The model to be used by the agent.
/// The name of the agent.
- /// The description of the agent.
/// The instructions for the agent.
/// The tools to be used by the agent.
- /// The resources for the tools.
/// The temperature setting for the agent.
/// The top-p setting for the agent.
- /// The response format for the agent.
+ /// The response format for the agent.
+ /// The reasoning options for the agent.
+ /// The text options for the agent.
+ /// The structured inputs for the agent.
/// The metadata for the agent.
/// Provides a way to customize the creation of the underlying used by the agent.
/// The to monitor for cancellation requests. The default is .
- /// A instance that can be used to perform operations on the newly created agent.
- public static ChatClientAgent CreateAIAgent(
- this PersistentAgentsClient persistentAgentsClient,
+ /// A instance that can be used to perform operations on the newly created agent.
+ public static async Task CreateAIAgentAsync(
+ this AgentsClient agentsClient,
string model,
string? name = null,
- string? description = null,
string? instructions = null,
- IEnumerable? tools = null,
- ToolResources? toolResources = null,
+ IEnumerable? tools = null,
float? temperature = null,
float? topP = null,
- BinaryData? responseFormat = null,
+ RaiConfig? raiConfig = null,
+ ResponseReasoningOptions? reasoningOptions = null,
+ ResponseTextOptions? textOptions = null,
+ IDictionary? structuredInputs = null,
IReadOnlyDictionary? metadata = null,
Func? clientFactory = null,
CancellationToken cancellationToken = default)
{
- if (persistentAgentsClient is null)
- {
- throw new ArgumentNullException(nameof(persistentAgentsClient));
- }
-
- var createPersistentAgentResponse = persistentAgentsClient.Administration.CreateAgent(
- model: model,
- name: name,
- description: description,
- instructions: instructions,
- tools: tools,
- toolResources: toolResources,
- temperature: temperature,
- topP: topP,
- responseFormat: responseFormat,
- metadata: metadata,
- cancellationToken: cancellationToken);
-
- // Get a local proxy for the agent to work with.
- return persistentAgentsClient.GetAIAgent(createPersistentAgentResponse.Value.Id, clientFactory: clientFactory, cancellationToken: cancellationToken);
- }
-
- ///
- /// Creates a new server side agent using the provided .
- ///
- /// The to create the agent with.
- /// The model to be used by the agent.
- /// Full set of options to configure the agent.
- /// Provides a way to customize the creation of the underlying used by the agent.
- /// The to monitor for cancellation requests. The default is .
- /// A instance that can be used to perform operations on the newly created agent.
- /// Thrown when or or is .
- /// Thrown when is empty or whitespace.
- public static ChatClientAgent CreateAIAgent(
- this PersistentAgentsClient persistentAgentsClient,
- string model,
- ChatClientAgentOptions options,
- Func? clientFactory = null,
- CancellationToken cancellationToken = default)
- {
- if (persistentAgentsClient is null)
- {
- throw new ArgumentNullException(nameof(persistentAgentsClient));
- }
-
- if (string.IsNullOrWhiteSpace(model))
- {
- throw new ArgumentException($"{nameof(model)} should not be null or whitespace.", nameof(model));
- }
-
- if (options is null)
- {
- throw new ArgumentNullException(nameof(options));
- }
+ Throw.IfNull(agentsClient);
- var toolDefinitionsAndResources = ConvertAIToolsToToolDefinitions(options.ChatOptions?.Tools);
-
- var createPersistentAgentResponse = persistentAgentsClient.Administration.CreateAgent(
- model: model,
- name: options.Name,
- description: options.Description,
- instructions: options.Instructions,
- tools: toolDefinitionsAndResources.ToolDefinitions,
- toolResources: toolDefinitionsAndResources.ToolResources,
- temperature: null,
- topP: null,
- responseFormat: null,
- metadata: null,
- cancellationToken: cancellationToken);
-
- if (options.ChatOptions?.Tools is { Count: > 0 } && (toolDefinitionsAndResources.FunctionToolsAndOtherTools is null || options.ChatOptions.Tools.Count != toolDefinitionsAndResources.FunctionToolsAndOtherTools.Count))
- {
- options = options.Clone();
- options.ChatOptions!.Tools = toolDefinitionsAndResources.FunctionToolsAndOtherTools;
- }
+ var (promptAgentDefinition, versionCreationOptions) = CreatePromptAgentDefinitionAndOptions(
+ model, instructions, temperature, topP, raiConfig, reasoningOptions, textOptions, tools, structuredInputs, metadata);
- // Get a local proxy for the agent to work with.
- return persistentAgentsClient.GetAIAgent(createPersistentAgentResponse.Value.Id, options, clientFactory: clientFactory, cancellationToken: cancellationToken);
+ AgentVersion agentVersion = await agentsClient.CreateAgentVersionAsync(name, promptAgentDefinition, versionCreationOptions, cancellationToken).ConfigureAwait(false);
+ IChatClient chatClient = agentsClient.GetOpenAIClient().GetOpenAIResponseClient(model).AsIChatClient();
+ return new AzureAIAgent(agentsClient, agentVersion, new ChatClientAgent(chatClient));
}
- ///
- /// Creates a new server side agent using the provided .
- ///
- /// The to create the agent with.
- /// The model to be used by the agent.
- /// Full set of options to configure the agent.
- /// Provides a way to customize the creation of the underlying used by the agent.
- /// The to monitor for cancellation requests. The default is .
- /// A instance that can be used to perform operations on the newly created agent.
- /// Thrown when or or is .
- /// Thrown when is empty or whitespace.
- public static async Task CreateAIAgentAsync(
- this PersistentAgentsClient persistentAgentsClient,
+ private static (PromptAgentDefinition, AgentVersionCreationOptions?) CreatePromptAgentDefinitionAndOptions(
string model,
- ChatClientAgentOptions options,
- Func? clientFactory = null,
- CancellationToken cancellationToken = default)
+ string? instructions,
+ float? temperature,
+ float? topP,
+ RaiConfig? raiConfig,
+ ResponseReasoningOptions? reasoningOptions,
+ ResponseTextOptions? textOptions,
+ IEnumerable? tools,
+ IDictionary? structuredInputs,
+ IReadOnlyDictionary? metadata)
{
- if (persistentAgentsClient is null)
+ PromptAgentDefinition promptAgentDefinition = new(model)
{
- throw new ArgumentNullException(nameof(persistentAgentsClient));
- }
-
- if (string.IsNullOrWhiteSpace(model))
- {
- throw new ArgumentException($"{nameof(model)} should not be null or whitespace.", nameof(model));
- }
-
- if (options is null)
- {
- throw new ArgumentNullException(nameof(options));
- }
+ Instructions = instructions,
+ Temperature = temperature,
+ TopP = topP,
+ RaiConfig = raiConfig,
+ ReasoningOptions = reasoningOptions,
+ TextOptions = textOptions,
+ };
- var toolDefinitionsAndResources = ConvertAIToolsToToolDefinitions(options.ChatOptions?.Tools);
-
- var createPersistentAgentResponse = await persistentAgentsClient.Administration.CreateAgentAsync(
- model: model,
- name: options.Name,
- description: options.Description,
- instructions: options.Instructions,
- tools: toolDefinitionsAndResources.ToolDefinitions,
- toolResources: toolDefinitionsAndResources.ToolResources,
- temperature: null,
- topP: null,
- responseFormat: null,
- metadata: null,
- cancellationToken: cancellationToken).ConfigureAwait(false);
-
- if (options.ChatOptions?.Tools is { Count: > 0 } && (toolDefinitionsAndResources.FunctionToolsAndOtherTools is null || options.ChatOptions.Tools.Count != toolDefinitionsAndResources.FunctionToolsAndOtherTools.Count))
+ AgentVersionCreationOptions? versionCreationOptions = null;
+ if (metadata is not null)
{
- options = options.Clone();
- options.ChatOptions!.Tools = toolDefinitionsAndResources.FunctionToolsAndOtherTools;
+ versionCreationOptions = new();
+ foreach (var kvp in metadata)
+ {
+ versionCreationOptions.Metadata.Add(kvp.Key, kvp.Value);
+ }
}
- // Get a local proxy for the agent to work with.
- return await persistentAgentsClient.GetAIAgentAsync(createPersistentAgentResponse.Value.Id, options, clientFactory: clientFactory, cancellationToken: cancellationToken).ConfigureAwait(false);
- }
-
- private static (List? ToolDefinitions, ToolResources? ToolResources, List? FunctionToolsAndOtherTools) ConvertAIToolsToToolDefinitions(IList? tools)
- {
- List? toolDefinitions = null;
- ToolResources? toolResources = null;
- List? functionToolsAndOtherTools = null;
-
if (tools is not null)
{
- foreach (AITool tool in tools)
+ if (promptAgentDefinition.Tools is List toolsList)
{
- switch (tool)
+ toolsList.AddRange(tools);
+ }
+ else
+ {
+ foreach (var tool in tools)
{
- case HostedCodeInterpreterTool codeTool:
-
- toolDefinitions ??= new();
- toolDefinitions.Add(new CodeInterpreterToolDefinition());
-
- if (codeTool.Inputs is { Count: > 0 })
- {
- foreach (var input in codeTool.Inputs)
- {
- switch (input)
- {
- case HostedFileContent hostedFile:
- // If the input is a HostedFileContent, we can use its ID directly.
- toolResources ??= new();
- toolResources.CodeInterpreter ??= new();
- toolResources.CodeInterpreter.FileIds.Add(hostedFile.FileId);
- break;
- }
- }
- }
- break;
-
- case HostedFileSearchTool fileSearchTool:
- toolDefinitions ??= new();
- toolDefinitions.Add(new FileSearchToolDefinition
- {
- FileSearch = new() { MaxNumResults = fileSearchTool.MaximumResultCount }
- });
-
- if (fileSearchTool.Inputs is { Count: > 0 })
- {
- foreach (var input in fileSearchTool.Inputs)
- {
- switch (input)
- {
- case HostedVectorStoreContent hostedVectorStore:
- toolResources ??= new();
- toolResources.FileSearch ??= new();
- toolResources.FileSearch.VectorStoreIds.Add(hostedVectorStore.VectorStoreId);
- break;
- }
- }
- }
- break;
-
- case HostedWebSearchTool webSearch when webSearch.AdditionalProperties?.TryGetValue("connectionId", out object? connectionId) is true:
- toolDefinitions ??= new();
- toolDefinitions.Add(new BingGroundingToolDefinition(new BingGroundingSearchToolParameters([new BingGroundingSearchConfiguration(connectionId!.ToString())])));
- break;
-
- default:
- functionToolsAndOtherTools ??= new();
- functionToolsAndOtherTools.Add(tool);
- break;
+ promptAgentDefinition.Tools.Add(tool);
}
}
}
- return (toolDefinitions, toolResources, functionToolsAndOtherTools);
+ if (structuredInputs is not null)
+ {
+ foreach (var kvp in structuredInputs)
+ {
+ promptAgentDefinition.StructuredInputs.Add(kvp.Key, kvp.Value);
+ }
+ }
+
+ return (promptAgentDefinition, versionCreationOptions);
}
}
diff --git a/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AzureAIAgent.cs b/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AzureAIAgent.cs
new file mode 100644
index 0000000000..0f27b37dd4
--- /dev/null
+++ b/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AzureAIAgent.cs
@@ -0,0 +1,127 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using System.Runtime.CompilerServices;
+using Azure.AI.Agents;
+using Microsoft.Extensions.AI;
+using OpenAI.Responses;
+
+#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.
+
+namespace Microsoft.Agents.AI.AzureAIAgents;
+
+///
+/// Provides an agent implementation that integrates with Azure AI Agents services, enabling chat-based interactions.
+///
+internal sealed class AzureAIAgent : DelegatingAIAgent
+{
+ private readonly AgentsClient _agentsClient;
+ private readonly AgentVersion _agentVersion;
+ private readonly AIAgentMetadata _agentMetadata;
+
+ internal AzureAIAgent(AgentsClient agentsClient, AgentVersion agentVersion, AIAgent innerAgent) : base(innerAgent)
+ {
+ this._agentsClient = agentsClient;
+ this._agentVersion = agentVersion;
+ this._agentMetadata = new AIAgentMetadata("azure.ai.agents");
+ }
+
+ ///
+ public async override Task RunAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default)
+ {
+ await this.PrepareAsync(thread, options, cancellationToken).ConfigureAwait(false);
+ return await this.InnerAgent.RunAsync(messages, thread, options, cancellationToken).ConfigureAwait(false);
+ }
+
+ ///
+ public async override IAsyncEnumerable RunStreamingAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default)
+ {
+ await this.PrepareAsync(thread, options, cancellationToken).ConfigureAwait(false);
+ await foreach (var update in this.InnerAgent.RunStreamingAsync(messages, thread, options, cancellationToken).ConfigureAwait(false))
+ {
+ yield return update;
+ }
+ }
+
+ ///
+ public override object? GetService(Type serviceType, object? serviceKey = null)
+ {
+ return (serviceKey is null && serviceType == typeof(AgentVersion))
+ ? this._agentVersion
+ : (serviceKey is null && serviceType == typeof(AgentsClient))
+ ? this._agentsClient
+ : (serviceKey is null && serviceType == typeof(AIAgentMetadata))
+ ? this._agentMetadata
+ : base.GetService(serviceType, serviceKey);
+ }
+
+ private async Task PrepareAsync(AgentThread? thread, AgentRunOptions? options, CancellationToken cancellationToken)
+ {
+ var chatClient = this.ValidateAndGetChatClient();
+ var chatOptions = (options as ChatClientAgentRunOptions)?.ChatOptions ?? new ChatOptions();
+ var chatClientThread = this.ValidateThread(thread);
+
+ var conversation = (chatClientThread is not null)
+ ? await this._agentsClient.GetConversationsClient().GetConversationAsync(chatClientThread.ConversationId, cancellationToken: cancellationToken).ConfigureAwait(false)
+ : await this._agentsClient.GetConversationsClient().CreateConversationAsync(cancellationToken: cancellationToken).ConfigureAwait(false);
+
+ this.SetupChatOptionsFactory(chatOptions, chatClient, conversation.Value);
+ }
+
+ private IChatClient ValidateAndGetChatClient()
+ {
+ var chatClient = this.GetService();
+ if (chatClient is null)
+ {
+ throw new InvalidOperationException("Cannot obtain a IChatClient from the agent pipeline.");
+ }
+ return chatClient;
+ }
+
+ private ChatClientAgentThread? ValidateThread(AgentThread? thread)
+ {
+ if (thread is null)
+ {
+ return null;
+ }
+
+ if (thread is not ChatClientAgentThread asChatClientAgentThread)
+ {
+ throw new InvalidOperationException("The provided thread is not compatible with the agent. Only threads created by the agent can be used.");
+ }
+
+ if (string.IsNullOrWhiteSpace(asChatClientAgentThread.ConversationId))
+ {
+ throw new InvalidOperationException("The ChatClientAgentThread does not have a valid ConversationId.");
+ }
+
+ return asChatClientAgentThread;
+ }
+
+ private void SetupChatOptionsFactory(ChatOptions chatOptions, IChatClient chatClient, AgentConversation agentConversation)
+ {
+ chatOptions.RawRepresentationFactory = (client) =>
+ {
+ var rawRepresentationFactory = chatOptions.RawRepresentationFactory;
+ ResponseCreationOptions? responseCreationOptions = null;
+
+ if (rawRepresentationFactory is not null)
+ {
+ responseCreationOptions = rawRepresentationFactory.Invoke(chatClient) as ResponseCreationOptions;
+
+ if (responseCreationOptions is null)
+ {
+ throw new InvalidOperationException("The provided ChatOptions RawRepresentationFactory did not return a valid ResponseCreationOptions instance.");
+ }
+ }
+ else
+ {
+ responseCreationOptions = new ResponseCreationOptions();
+ }
+
+ responseCreationOptions.SetAgentReference(this.InnerAgent.Name);
+ responseCreationOptions.SetConversationReference(agentConversation);
+
+ return responseCreationOptions;
+ };
+ }
+}
diff --git a/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/Microsoft.Agents.AI.AzureAIAgents.csproj b/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/Microsoft.Agents.AI.AzureAIAgents.csproj
new file mode 100644
index 0000000000..98be43fff7
--- /dev/null
+++ b/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/Microsoft.Agents.AI.AzureAIAgents.csproj
@@ -0,0 +1,29 @@
+
+
+
+ $(ProjectsTargetFrameworks)
+ $(ProjectsDebugTargetFrameworks)
+ preview
+ enable
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Microsoft Agent Framework AzureAI
+ Provides Microsoft Agent Framework support for Azure AI.
+
+
+
diff --git a/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseFixture.cs b/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseFixture.cs
index d223a65e28..fbb087a153 100644
--- a/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseFixture.cs
+++ b/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseFixture.cs
@@ -68,7 +68,7 @@ public async Task CreateChatClientAgentAsync(
string name = "HelpfulAssistant",
string instructions = "You are a helpful assistant.",
IList? aiTools = null) =>
- new ChatClientAgent(
+ new(
this._openAIResponseClient.AsIChatClient(),
options: new()
{
From f98ddc01ed5a59aba4a0990111f283b807105f9a Mon Sep 17 00:00:00 2001
From: Roger Barreto <19890735+rogerbarreto@users.noreply.github.com>
Date: Thu, 23 Oct 2025 12:10:58 +0100
Subject: [PATCH 02/16] Update nuget.config
---
dotnet/nuget.config | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dotnet/nuget.config b/dotnet/nuget.config
index 543e8497d7..ae2bf13b3a 100644
--- a/dotnet/nuget.config
+++ b/dotnet/nuget.config
@@ -10,7 +10,7 @@
-
+
\ No newline at end of file
From 41d3ae4d5d0f1bd10b9381592d3dfa625a8701fb Mon Sep 17 00:00:00 2001
From: Roger Barreto <19890735+rogerbarreto@users.noreply.github.com>
Date: Thu, 23 Oct 2025 12:26:57 +0100
Subject: [PATCH 03/16] Address Xmldoc
---
.../Microsoft.Agents.AI.AzureAIAgents/AgentsClientExtensions.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AgentsClientExtensions.cs b/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AgentsClientExtensions.cs
index 5552e0c732..0e03890a48 100644
--- a/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AgentsClientExtensions.cs
+++ b/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AgentsClientExtensions.cs
@@ -328,7 +328,7 @@ public static AIAgent CreateAIAgent(
/// The tools to be used by the agent.
/// The temperature setting for the agent.
/// The top-p setting for the agent.
- /// The response format for the agent.
+ /// The responsible AI configuration for the agent.
/// The reasoning options for the agent.
/// The text options for the agent.
/// The structured inputs for the agent.
From 292238f971a946e82c5cdb8136c94c7802aecbf8 Mon Sep 17 00:00:00 2001
From: Roger Barreto <19890735+rogerbarreto@users.noreply.github.com>
Date: Thu, 23 Oct 2025 12:38:09 +0100
Subject: [PATCH 04/16] Remove format from branches checks
---
.github/workflows/dotnet-format.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/dotnet-format.yml b/.github/workflows/dotnet-format.yml
index a9fe090013..bb55dbde86 100644
--- a/.github/workflows/dotnet-format.yml
+++ b/.github/workflows/dotnet-format.yml
@@ -7,7 +7,7 @@ name: dotnet-format
on:
workflow_dispatch:
pull_request:
- branches: ["main", "feature*"]
+ branches: ["main"]
paths:
- dotnet/**
- '.github/workflows/dotnet-format.yml'
From 04b5cc9ae5fcad7ca7c0702793af9b1ff8e5076f Mon Sep 17 00:00:00 2001
From: Roger Barreto <19890735+rogerbarreto@users.noreply.github.com>
Date: Thu, 23 Oct 2025 12:44:52 +0100
Subject: [PATCH 05/16] Address Xmldocs
---
.../AgentsClientExtensions.cs | 2 +-
.../Microsoft.Agents.AI.AzureAIAgents.csproj | 6 +++---
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AgentsClientExtensions.cs b/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AgentsClientExtensions.cs
index 0e03890a48..b24bffbc2e 100644
--- a/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AgentsClientExtensions.cs
+++ b/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AgentsClientExtensions.cs
@@ -284,7 +284,7 @@ public static async Task GetAIAgentAsync(
/// The tools to be used by the agent.
/// The temperature setting for the agent.
/// The top-p setting for the agent.
- /// The response format for the agent.
+ /// The responsible AI configuration for the agent.
/// The reasoning options for the agent.
/// The text options for the agent.
/// The structured inputs for the agent.
diff --git a/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/Microsoft.Agents.AI.AzureAIAgents.csproj b/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/Microsoft.Agents.AI.AzureAIAgents.csproj
index 98be43fff7..6f662416e2 100644
--- a/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/Microsoft.Agents.AI.AzureAIAgents.csproj
+++ b/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/Microsoft.Agents.AI.AzureAIAgents.csproj
@@ -3,7 +3,7 @@
$(ProjectsTargetFrameworks)
$(ProjectsDebugTargetFrameworks)
- preview
+ alpha
enable
true
@@ -22,8 +22,8 @@
- Microsoft Agent Framework AzureAI
- Provides Microsoft Agent Framework support for Azure AI.
+ Microsoft Agent Framework Azure AI Agents
+ Provides Microsoft Agent Framework support for Azure AI Agents.
From 698c8591cdc639b49b6734f8fc92ce35091b9df5 Mon Sep 17 00:00:00 2001
From: Roger Barreto <19890735+rogerbarreto@users.noreply.github.com>
Date: Thu, 23 Oct 2025 14:19:31 +0100
Subject: [PATCH 06/16] Add more details to the implementation
---
.../AgentsClientExtensions.cs | 110 ++++++++++++++++--
.../AzureAIAgentChatClient.cs | 26 +++++
2 files changed, 129 insertions(+), 7 deletions(-)
create mode 100644 dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AzureAIAgentChatClient.cs
diff --git a/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AgentsClientExtensions.cs b/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AgentsClientExtensions.cs
index b24bffbc2e..b13d220274 100644
--- a/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AgentsClientExtensions.cs
+++ b/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AgentsClientExtensions.cs
@@ -289,7 +289,7 @@ public static async Task GetAIAgentAsync(
/// The text options for the agent.
/// The structured inputs for the agent.
/// The metadata for the agent.
- /// Provides a way to customize the creation of the underlying used by the agent.
+ /// A factory function to customize the creation of the chat client used by the agent.
/// The to monitor for cancellation requests. The default is .
/// A instance that can be used to perform operations on the newly created agent.
public static AIAgent CreateAIAgent(
@@ -309,17 +309,105 @@ public static AIAgent CreateAIAgent(
CancellationToken cancellationToken = default)
{
Throw.IfNull(agentsClient);
+ Throw.IfNull(model);
var (promptAgentDefinition, versionCreationOptions) = CreatePromptAgentDefinitionAndOptions(
model, instructions, temperature, topP, raiConfig, reasoningOptions, textOptions, tools, structuredInputs, metadata);
AgentVersion agentVersion = agentsClient.CreateAgentVersion(name, promptAgentDefinition, versionCreationOptions, cancellationToken);
- IChatClient chatClient = agentsClient.GetOpenAIClient().GetOpenAIResponseClient(model).AsIChatClient();
+ IChatClient chatClient = new AzureAIAgentChatClient(agentsClient.GetOpenAIClient().GetOpenAIResponseClient(model).AsIChatClient());
+
+ if (clientFactory is not null)
+ {
+ chatClient = clientFactory(chatClient);
+ }
+
return new AzureAIAgent(agentsClient, agentVersion, new ChatClientAgent(chatClient));
}
///
- /// Creates a new server side agent using the provided .
+ /// Creates a new AI agent using the specified agent definition and optional configuration parameters.
+ ///
+ /// The client used to manage and interact with AI agents.
+ /// The definition that specifies the configuration and behavior of the agent to create.
+ /// The name of the model to use for the agent. If not specified, the model must be provided as part of the agent definition.
+ /// The name for the agent.
+ /// Settings that control the creation of the agent version.
+ /// A factory function to customize the creation of the chat client used by the agent.
+ /// A token to monitor for cancellation requests.
+ /// A instance that can be used to perform operations on the newly created agent.
+ /// Thrown if neither the 'model' parameter nor a model in the agent definition is provided.
+ public static AIAgent CreateAIAgent(
+ this AgentsClient agentsClient,
+ AgentDefinition agentDefinition,
+ string? model = null,
+ string? name = null,
+ AgentVersionCreationOptions? versionCreationOptions = null,
+ Func? clientFactory = null,
+ CancellationToken cancellationToken = default)
+ {
+ Throw.IfNull(agentsClient);
+ Throw.IfNull(agentDefinition);
+
+ if (model is null && (agentDefinition as PromptAgentDefinition)?.Model is null)
+ {
+ throw new ArgumentException("Model must be provided either directly or as part of a PromptAgentDefinition specialization.", nameof(model));
+ }
+
+ AgentVersion agentVersion = agentsClient.CreateAgentVersion(name, agentDefinition, versionCreationOptions, cancellationToken);
+ IChatClient chatClient = new AzureAIAgentChatClient(agentsClient.GetOpenAIClient().GetOpenAIResponseClient(model).AsIChatClient());
+
+ if (clientFactory is not null)
+ {
+ chatClient = clientFactory(chatClient);
+ }
+
+ return new AzureAIAgent(agentsClient, agentVersion, new ChatClientAgent(chatClient));
+ }
+
+ ///
+ /// Asynchronously creates a new AI agent using the specified agent definition and optional configuration
+ /// parameters.
+ ///
+ /// The client used to manage and interact with AI agents.
+ /// The definition that specifies the configuration and behavior of the agent to create.
+ /// The name of the model to use for the agent. If not specified, the model must be provided as part of the agent definition.
+ /// The name for the agent.
+ /// Settings that control the creation of the agent version.
+ /// A factory function to customize the creation of the chat client used by the agent.
+ /// A token to monitor for cancellation requests.
+ /// A task that represents the asynchronous operation. The task result contains the created AI agent.
+ /// Thrown if neither the 'model' parameter nor a model in the agent definition is provided.
+ public static async Task CreateAIAgentAsync(
+ this AgentsClient agentsClient,
+ AgentDefinition agentDefinition,
+ string? model = null,
+ string? name = null,
+ AgentVersionCreationOptions? versionCreationOptions = null,
+ Func? clientFactory = null,
+ CancellationToken cancellationToken = default)
+ {
+ Throw.IfNull(agentsClient);
+ Throw.IfNull(agentDefinition);
+
+ if (model is null && (agentDefinition as PromptAgentDefinition)?.Model is null)
+ {
+ throw new ArgumentException("Model must be provided either directly or as part of a PromptAgentDefinition specialization.", nameof(model));
+ }
+
+ AgentVersion agentVersion = await agentsClient.CreateAgentVersionAsync(name, agentDefinition, versionCreationOptions, cancellationToken).ConfigureAwait(false);
+ IChatClient chatClient = new AzureAIAgentChatClient(agentsClient.GetOpenAIClient().GetOpenAIResponseClient(model).AsIChatClient());
+
+ if (clientFactory is not null)
+ {
+ chatClient = clientFactory(chatClient);
+ }
+
+ return new AzureAIAgent(agentsClient, agentVersion, new ChatClientAgent(chatClient));
+ }
+
+ ///
+ /// Creates a new server side prompt agent using the provided .
///
/// The to create the agent with.
/// The model to be used by the agent.
@@ -333,9 +421,9 @@ public static AIAgent CreateAIAgent(
/// The text options for the agent.
/// The structured inputs for the agent.
/// The metadata for the agent.
- /// Provides a way to customize the creation of the underlying used by the agent.
- /// The to monitor for cancellation requests. The default is .
- /// A instance that can be used to perform operations on the newly created agent.
+ /// A factory function to customize the creation of the underlying used by the agent.
+ /// The to monitor for cancellation requests.
+ /// A task that represents the asynchronous operation. The task result contains a instance that can be used to perform operations on the newly created agent.
public static async Task CreateAIAgentAsync(
this AgentsClient agentsClient,
string model,
@@ -353,12 +441,19 @@ public static async Task CreateAIAgentAsync(
CancellationToken cancellationToken = default)
{
Throw.IfNull(agentsClient);
+ Throw.IfNull(model);
var (promptAgentDefinition, versionCreationOptions) = CreatePromptAgentDefinitionAndOptions(
model, instructions, temperature, topP, raiConfig, reasoningOptions, textOptions, tools, structuredInputs, metadata);
AgentVersion agentVersion = await agentsClient.CreateAgentVersionAsync(name, promptAgentDefinition, versionCreationOptions, cancellationToken).ConfigureAwait(false);
- IChatClient chatClient = agentsClient.GetOpenAIClient().GetOpenAIResponseClient(model).AsIChatClient();
+ IChatClient chatClient = new AzureAIAgentChatClient(agentsClient.GetOpenAIClient().GetOpenAIResponseClient(model).AsIChatClient());
+
+ if (clientFactory is not null)
+ {
+ chatClient = clientFactory(chatClient);
+ }
+
return new AzureAIAgent(agentsClient, agentVersion, new ChatClientAgent(chatClient));
}
@@ -376,6 +471,7 @@ private static (PromptAgentDefinition, AgentVersionCreationOptions?) CreatePromp
{
PromptAgentDefinition promptAgentDefinition = new(model)
{
+ Model = model,
Instructions = instructions,
Temperature = temperature,
TopP = topP,
diff --git a/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AzureAIAgentChatClient.cs b/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AzureAIAgentChatClient.cs
new file mode 100644
index 0000000000..9e5ad95222
--- /dev/null
+++ b/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AzureAIAgentChatClient.cs
@@ -0,0 +1,26 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using Microsoft.Extensions.AI;
+
+namespace Microsoft.Agents.AI.AzureAIAgents;
+
+///
+/// Provides a chat client implementation that integrates with Azure AI Agents, enabling chat interactions using
+/// Azure-specific agent capabilities.
+///
+internal sealed class AzureAIAgentChatClient : DelegatingChatClient
+{
+ private readonly ChatClientMetadata? _metadata;
+
+ internal AzureAIAgentChatClient(IChatClient innerClient) : base(innerClient)
+ {
+ this._metadata = new ChatClientMetadata("azure.ai.agents");
+ }
+
+ public override object? GetService(Type serviceType, object? serviceKey = null)
+ {
+ return (serviceKey is null && serviceType == typeof(ChatClientMetadata))
+ ? this._metadata
+ : base.GetService(serviceType, serviceKey);
+ }
+}
From 37d44cfaaba2df52de7f366964f8f0d0c8eb2589 Mon Sep 17 00:00:00 2001
From: Roger Barreto <19890735+rogerbarreto@users.noreply.github.com>
Date: Thu, 23 Oct 2025 15:18:53 +0100
Subject: [PATCH 07/16] Moving Agent logic to ChatClient
---
.../Microsoft.Agents.AI.AzureAI.csproj | 1 -
.../AgentsClientExtensions.cs | 14 ++-
.../AzureAIAgent.cs | 66 ++---------
.../AzureAIAgentChatClient.cs | 105 +++++++++++++++++-
4 files changed, 123 insertions(+), 63 deletions(-)
diff --git a/dotnet/src/Microsoft.Agents.AI.AzureAI/Microsoft.Agents.AI.AzureAI.csproj b/dotnet/src/Microsoft.Agents.AI.AzureAI/Microsoft.Agents.AI.AzureAI.csproj
index 3161d893a6..6d1cc23a1c 100644
--- a/dotnet/src/Microsoft.Agents.AI.AzureAI/Microsoft.Agents.AI.AzureAI.csproj
+++ b/dotnet/src/Microsoft.Agents.AI.AzureAI/Microsoft.Agents.AI.AzureAI.csproj
@@ -5,7 +5,6 @@
$(ProjectsDebugTargetFrameworks)
preview
enable
- true
diff --git a/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AgentsClientExtensions.cs b/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AgentsClientExtensions.cs
index b13d220274..c3d2d2c4f6 100644
--- a/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AgentsClientExtensions.cs
+++ b/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AgentsClientExtensions.cs
@@ -315,7 +315,7 @@ public static AIAgent CreateAIAgent(
model, instructions, temperature, topP, raiConfig, reasoningOptions, textOptions, tools, structuredInputs, metadata);
AgentVersion agentVersion = agentsClient.CreateAgentVersion(name, promptAgentDefinition, versionCreationOptions, cancellationToken);
- IChatClient chatClient = new AzureAIAgentChatClient(agentsClient.GetOpenAIClient().GetOpenAIResponseClient(model).AsIChatClient());
+ IChatClient chatClient = new AzureAIAgentChatClient(agentsClient, agentVersion, model);
if (clientFactory is not null)
{
@@ -349,13 +349,14 @@ public static AIAgent CreateAIAgent(
Throw.IfNull(agentsClient);
Throw.IfNull(agentDefinition);
- if (model is null && (agentDefinition as PromptAgentDefinition)?.Model is null)
+ model ??= (agentDefinition as PromptAgentDefinition)?.Model;
+ if (string.IsNullOrWhiteSpace(model))
{
throw new ArgumentException("Model must be provided either directly or as part of a PromptAgentDefinition specialization.", nameof(model));
}
AgentVersion agentVersion = agentsClient.CreateAgentVersion(name, agentDefinition, versionCreationOptions, cancellationToken);
- IChatClient chatClient = new AzureAIAgentChatClient(agentsClient.GetOpenAIClient().GetOpenAIResponseClient(model).AsIChatClient());
+ IChatClient chatClient = new AzureAIAgentChatClient(agentsClient, agentVersion, model);
if (clientFactory is not null)
{
@@ -390,13 +391,14 @@ public static async Task CreateAIAgentAsync(
Throw.IfNull(agentsClient);
Throw.IfNull(agentDefinition);
- if (model is null && (agentDefinition as PromptAgentDefinition)?.Model is null)
+ model ??= (agentDefinition as PromptAgentDefinition)?.Model;
+ if (string.IsNullOrWhiteSpace(model))
{
throw new ArgumentException("Model must be provided either directly or as part of a PromptAgentDefinition specialization.", nameof(model));
}
AgentVersion agentVersion = await agentsClient.CreateAgentVersionAsync(name, agentDefinition, versionCreationOptions, cancellationToken).ConfigureAwait(false);
- IChatClient chatClient = new AzureAIAgentChatClient(agentsClient.GetOpenAIClient().GetOpenAIResponseClient(model).AsIChatClient());
+ IChatClient chatClient = new AzureAIAgentChatClient(agentsClient, agentVersion, model);
if (clientFactory is not null)
{
@@ -447,7 +449,7 @@ public static async Task CreateAIAgentAsync(
model, instructions, temperature, topP, raiConfig, reasoningOptions, textOptions, tools, structuredInputs, metadata);
AgentVersion agentVersion = await agentsClient.CreateAgentVersionAsync(name, promptAgentDefinition, versionCreationOptions, cancellationToken).ConfigureAwait(false);
- IChatClient chatClient = new AzureAIAgentChatClient(agentsClient.GetOpenAIClient().GetOpenAIResponseClient(model).AsIChatClient());
+ IChatClient chatClient = new AzureAIAgentChatClient(agentsClient, agentVersion, model);
if (clientFactory is not null)
{
diff --git a/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AzureAIAgent.cs b/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AzureAIAgent.cs
index 0f27b37dd4..9cf8281837 100644
--- a/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AzureAIAgent.cs
+++ b/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AzureAIAgent.cs
@@ -3,7 +3,6 @@
using System.Runtime.CompilerServices;
using Azure.AI.Agents;
using Microsoft.Extensions.AI;
-using OpenAI.Responses;
#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.
@@ -28,14 +27,18 @@ internal AzureAIAgent(AgentsClient agentsClient, AgentVersion agentVersion, AIAg
///
public async override Task RunAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default)
{
- await this.PrepareAsync(thread, options, cancellationToken).ConfigureAwait(false);
+ this.ValidateChatClient();
+ this.ValidateThread(thread);
+
return await this.InnerAgent.RunAsync(messages, thread, options, cancellationToken).ConfigureAwait(false);
}
///
public async override IAsyncEnumerable RunStreamingAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default)
{
- await this.PrepareAsync(thread, options, cancellationToken).ConfigureAwait(false);
+ this.ValidateChatClient();
+ this.ValidateThread(thread);
+
await foreach (var update in this.InnerAgent.RunStreamingAsync(messages, thread, options, cancellationToken).ConfigureAwait(false))
{
yield return update;
@@ -54,34 +57,17 @@ public async override IAsyncEnumerable RunStreamingAsync
: base.GetService(serviceType, serviceKey);
}
- private async Task PrepareAsync(AgentThread? thread, AgentRunOptions? options, CancellationToken cancellationToken)
+ private void ValidateChatClient()
{
- var chatClient = this.ValidateAndGetChatClient();
- var chatOptions = (options as ChatClientAgentRunOptions)?.ChatOptions ?? new ChatOptions();
- var chatClientThread = this.ValidateThread(thread);
-
- var conversation = (chatClientThread is not null)
- ? await this._agentsClient.GetConversationsClient().GetConversationAsync(chatClientThread.ConversationId, cancellationToken: cancellationToken).ConfigureAwait(false)
- : await this._agentsClient.GetConversationsClient().CreateConversationAsync(cancellationToken: cancellationToken).ConfigureAwait(false);
-
- this.SetupChatOptionsFactory(chatOptions, chatClient, conversation.Value);
- }
-
- private IChatClient ValidateAndGetChatClient()
- {
- var chatClient = this.GetService();
- if (chatClient is null)
- {
- throw new InvalidOperationException("Cannot obtain a IChatClient from the agent pipeline.");
- }
- return chatClient;
+ _ = this.GetService()
+ ?? throw new InvalidOperationException("The provided IChatClient needs to be decorated with a AzureAIAgent ChatClient for the agent to function properly.");
}
- private ChatClientAgentThread? ValidateThread(AgentThread? thread)
+ private void ValidateThread(AgentThread? thread)
{
if (thread is null)
{
- return null;
+ return;
}
if (thread is not ChatClientAgentThread asChatClientAgentThread)
@@ -93,35 +79,5 @@ private IChatClient ValidateAndGetChatClient()
{
throw new InvalidOperationException("The ChatClientAgentThread does not have a valid ConversationId.");
}
-
- return asChatClientAgentThread;
- }
-
- private void SetupChatOptionsFactory(ChatOptions chatOptions, IChatClient chatClient, AgentConversation agentConversation)
- {
- chatOptions.RawRepresentationFactory = (client) =>
- {
- var rawRepresentationFactory = chatOptions.RawRepresentationFactory;
- ResponseCreationOptions? responseCreationOptions = null;
-
- if (rawRepresentationFactory is not null)
- {
- responseCreationOptions = rawRepresentationFactory.Invoke(chatClient) as ResponseCreationOptions;
-
- if (responseCreationOptions is null)
- {
- throw new InvalidOperationException("The provided ChatOptions RawRepresentationFactory did not return a valid ResponseCreationOptions instance.");
- }
- }
- else
- {
- responseCreationOptions = new ResponseCreationOptions();
- }
-
- responseCreationOptions.SetAgentReference(this.InnerAgent.Name);
- responseCreationOptions.SetConversationReference(agentConversation);
-
- return responseCreationOptions;
- };
}
}
diff --git a/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AzureAIAgentChatClient.cs b/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AzureAIAgentChatClient.cs
index 9e5ad95222..f1eee8faca 100644
--- a/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AzureAIAgentChatClient.cs
+++ b/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AzureAIAgentChatClient.cs
@@ -1,6 +1,12 @@
// Copyright (c) Microsoft. All rights reserved.
+using System.Runtime.CompilerServices;
+using Azure.AI.Agents;
using Microsoft.Extensions.AI;
+using Microsoft.Shared.Diagnostics;
+using OpenAI.Responses;
+
+#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.
namespace Microsoft.Agents.AI.AzureAIAgents;
@@ -11,16 +17,113 @@ namespace Microsoft.Agents.AI.AzureAIAgents;
internal sealed class AzureAIAgentChatClient : DelegatingChatClient
{
private readonly ChatClientMetadata? _metadata;
+ private readonly AgentsClient _agentsClient;
+ private readonly AgentVersion _agentVersion;
- internal AzureAIAgentChatClient(IChatClient innerClient) : base(innerClient)
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// An instance of to interact with Azure AI Agents services.
+ /// An instance of representing the specific agent version to use.
+ /// The model to use for the chat client.
+ ///
+ /// The provided should be decorated with a for proper functionality.
+ ///
+ internal AzureAIAgentChatClient(AgentsClient agentsClient, AgentVersion agentVersion, string model)
+ : base(agentsClient.GetOpenAIClient().GetOpenAIResponseClient(Throw.IfNull(model)).AsIChatClient())
{
+ this._agentsClient = Throw.IfNull(agentsClient);
+ this._agentVersion = Throw.IfNull(agentVersion);
this._metadata = new ChatClientMetadata("azure.ai.agents");
}
+ ///
public override object? GetService(Type serviceType, object? serviceKey = null)
{
return (serviceKey is null && serviceType == typeof(ChatClientMetadata))
? this._metadata
+ : (serviceKey is null && serviceType == typeof(AgentsClient))
+ ? this._agentsClient
+ : (serviceKey is null && serviceType == typeof(AgentVersion))
+ ? this._agentVersion
: base.GetService(serviceType, serviceKey);
}
+
+ ///
+ public override async Task GetResponseAsync(IEnumerable messages, ChatOptions? options = null, CancellationToken cancellationToken = default)
+ {
+ var conversation = await this.GetConversationAsync(messages, options, cancellationToken).ConfigureAwait(false);
+ this.SetChatOptionsFactory(options, conversation);
+
+ return await base.GetResponseAsync(messages, options, cancellationToken).ConfigureAwait(false);
+ }
+
+ ///
+ public async override IAsyncEnumerable GetStreamingResponseAsync(IEnumerable messages, ChatOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default)
+ {
+ var conversation = await this.GetConversationAsync(messages, options, cancellationToken).ConfigureAwait(false);
+ this.SetChatOptionsFactory(options, conversation);
+
+ await foreach (var chunk in base.GetStreamingResponseAsync(messages, options, cancellationToken).ConfigureAwait(false))
+ {
+ yield return chunk;
+ }
+ }
+
+ private async Task GetConversationAsync(IEnumerable messages, ChatOptions? options, CancellationToken cancellationToken)
+ {
+ AgentConversation conversation;
+ if (string.IsNullOrWhiteSpace(options?.ConversationId))
+ {
+ var creationOptions = new AgentConversationCreationOptions();
+ if (creationOptions.Items is List itemsList)
+ {
+ itemsList.AddRange(messages.AsOpenAIResponseItems());
+ }
+ else
+ {
+ foreach (var item in messages.AsOpenAIResponseItems())
+ {
+ creationOptions.Items.Add(item);
+ }
+ }
+
+ conversation = await this._agentsClient.GetConversationsClient().CreateConversationAsync(creationOptions, cancellationToken: cancellationToken).ConfigureAwait(false);
+ }
+ else
+ {
+ conversation = await this._agentsClient.GetConversationsClient().GetConversationAsync(options!.ConversationId, cancellationToken: cancellationToken).ConfigureAwait(false);
+ }
+
+ return conversation;
+ }
+
+ private void SetChatOptionsFactory(ChatOptions? chatOptions, AgentConversation agentConversation)
+ {
+ chatOptions ??= new ChatOptions();
+ chatOptions.RawRepresentationFactory = (client) =>
+ {
+ var rawRepresentationFactory = chatOptions.RawRepresentationFactory;
+ ResponseCreationOptions? responseCreationOptions = null;
+
+ if (rawRepresentationFactory is not null)
+ {
+ responseCreationOptions = rawRepresentationFactory.Invoke(this) as ResponseCreationOptions;
+
+ if (responseCreationOptions is null)
+ {
+ throw new InvalidOperationException("The provided ChatOptions RawRepresentationFactory did not return a valid ResponseCreationOptions instance.");
+ }
+ }
+ else
+ {
+ responseCreationOptions = new ResponseCreationOptions();
+ }
+
+ responseCreationOptions.SetAgentReference(this._agentVersion.Name);
+ responseCreationOptions.SetConversationReference(agentConversation);
+
+ return responseCreationOptions;
+ };
+ }
}
From 959361e6c78bae88d30b0ec4bbbb76136cd79849 Mon Sep 17 00:00:00 2001
From: Roger Barreto <19890735+rogerbarreto@users.noreply.github.com>
Date: Thu, 23 Oct 2025 15:32:04 +0100
Subject: [PATCH 08/16] Adding Name and Id overrides to AzureAIAgent
---
.../AzureAIAgent.cs | 15 +++++++++++++--
1 file changed, 13 insertions(+), 2 deletions(-)
diff --git a/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AzureAIAgent.cs b/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AzureAIAgent.cs
index 9cf8281837..02c85d5fe2 100644
--- a/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AzureAIAgent.cs
+++ b/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AzureAIAgent.cs
@@ -22,10 +22,21 @@ internal AzureAIAgent(AgentsClient agentsClient, AgentVersion agentVersion, AIAg
this._agentsClient = agentsClient;
this._agentVersion = agentVersion;
this._agentMetadata = new AIAgentMetadata("azure.ai.agents");
+
+ if (innerAgent.GetService() is null)
+ {
+ throw new InvalidOperationException("The provided AI Agent must to have a ChatClientAgent in the decoration pipeline for the agent to function properly.");
+ }
}
///
- public async override Task RunAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default)
+ public override string Id => this._agentVersion.Id;
+
+ ///
+ public override string Name => this._agentVersion.Name;
+
+ ///
+ public override async Task RunAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default)
{
this.ValidateChatClient();
this.ValidateThread(thread);
@@ -34,7 +45,7 @@ public async override Task RunAsync(IEnumerable m
}
///
- public async override IAsyncEnumerable RunStreamingAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default)
+ public override async IAsyncEnumerable RunStreamingAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default)
{
this.ValidateChatClient();
this.ValidateThread(thread);
From 2225418997b094374d6fc3f99c0eaa774a579984 Mon Sep 17 00:00:00 2001
From: Roger Barreto <19890735+rogerbarreto@users.noreply.github.com>
Date: Thu, 23 Oct 2025 20:47:52 +0100
Subject: [PATCH 09/16] Updating extensions
---
.../AgentsClientExtensions.cs | 348 ++++++++++++++++--
.../AgentsClientJsonContext.cs | 17 +
.../AzureAIAgent.cs | 94 -----
.../AzureAIAgentChatClient.cs | 16 +-
4 files changed, 337 insertions(+), 138 deletions(-)
create mode 100644 dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AgentsClientJsonContext.cs
delete mode 100644 dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AzureAIAgent.cs
diff --git a/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AgentsClientExtensions.cs b/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AgentsClientExtensions.cs
index c3d2d2c4f6..25ba5d87e6 100644
--- a/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AgentsClientExtensions.cs
+++ b/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AgentsClientExtensions.cs
@@ -1,11 +1,16 @@
// Copyright (c) Microsoft. All rights reserved.
+using System.Text;
+using System.Text.Json;
+using System.Text.Json.Nodes;
+using System.Text.Json.Serialization;
using Microsoft.Agents.AI;
using Microsoft.Agents.AI.AzureAIAgents;
using Microsoft.Extensions.AI;
using Microsoft.Shared.Diagnostics;
using OpenAI.Responses;
+#pragma warning disable MEAI001 // 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.
namespace Azure.AI.Agents;
@@ -105,32 +110,32 @@ public static ChatClientAgent GetAIAgent(
///
/// Retrieves an existing server side agent, wrapped as a using the provided .
///
- /// The to create the with.
+ /// The to create the with.
/// A for the persistent agent.
- /// The ID of the server side agent to create a for.
+ /// The ID of the server side agent to create a for.
/// Options that should apply to all runs of the agent.
/// Provides a way to customize the creation of the underlying used by the agent.
/// The to monitor for cancellation requests. The default is .
/// A instance that can be used to perform operations on the persistent agent.
public static async Task GetAIAgentAsync(
- this PersistentAgentsClient persistentAgentsClient,
- string agentId,
+ this AgentsClient agentsClient,
+ string name,
ChatOptions? chatOptions = null,
Func? clientFactory = null,
CancellationToken cancellationToken = default)
{
- if (persistentAgentsClient is null)
- {
- throw new ArgumentNullException(nameof(persistentAgentsClient));
- }
+ Throw.IfNull(agentsClient);
+ Throw.IfNullOrWhitespace(name);
- if (string.IsNullOrWhiteSpace(agentId))
+ var agent = await agentsClient.GetAgentAsync(name, cancellationToken).ConfigureAwait(false);
+ if (agent is null)
{
- throw new ArgumentException($"{nameof(agentId)} should not be null or whitespace.", nameof(agentId));
+ agent.ver
+ throw new InvalidOperationException($"Agent with name '{name}' not found.");
}
- var persistentAgentResponse = await persistentAgentsClient.Administration.GetAgentAsync(agentId, cancellationToken).ConfigureAwait(false);
- return persistentAgentsClient.GetAIAgent(persistentAgentResponse, chatOptions, clientFactory);
+ var persistentAgentResponse = await agentsClient.Administration.GetAgentAsync(name, cancellationToken).ConfigureAwait(false);
+ return agentsClient.GetAIAgent(persistentAgentResponse, chatOptions, clientFactory);
}
///
@@ -292,7 +297,7 @@ public static async Task GetAIAgentAsync(
/// A factory function to customize the creation of the chat client used by the agent.
/// The to monitor for cancellation requests. The default is .
/// A instance that can be used to perform operations on the newly created agent.
- public static AIAgent CreateAIAgent(
+ public static ChatClientAgent CreateAIAgent(
this AgentsClient agentsClient,
string model,
string? name = null,
@@ -311,18 +316,18 @@ public static AIAgent CreateAIAgent(
Throw.IfNull(agentsClient);
Throw.IfNull(model);
- var (promptAgentDefinition, versionCreationOptions) = CreatePromptAgentDefinitionAndOptions(
+ var (promptAgentDefinition, creationOptions) = CreatePromptAgentDefinitionAndOptions(
model, instructions, temperature, topP, raiConfig, reasoningOptions, textOptions, tools, structuredInputs, metadata);
- AgentVersion agentVersion = agentsClient.CreateAgentVersion(name, promptAgentDefinition, versionCreationOptions, cancellationToken);
- IChatClient chatClient = new AzureAIAgentChatClient(agentsClient, agentVersion, model);
+ AgentRecord agentRecord = agentsClient.CreateAgent(name, promptAgentDefinition, creationOptions, cancellationToken);
+ IChatClient chatClient = new AzureAIAgentChatClient(agentsClient, agentRecord, model);
if (clientFactory is not null)
{
chatClient = clientFactory(chatClient);
}
- return new AzureAIAgent(agentsClient, agentVersion, new ChatClientAgent(chatClient));
+ return new ChatClientAgent(chatClient);
}
///
@@ -332,17 +337,17 @@ public static AIAgent CreateAIAgent(
/// The definition that specifies the configuration and behavior of the agent to create.
/// The name of the model to use for the agent. If not specified, the model must be provided as part of the agent definition.
/// The name for the agent.
- /// Settings that control the creation of the agent version.
+ /// Settings that control the creation of the agent.
/// A factory function to customize the creation of the chat client used by the agent.
/// A token to monitor for cancellation requests.
/// A instance that can be used to perform operations on the newly created agent.
/// Thrown if neither the 'model' parameter nor a model in the agent definition is provided.
- public static AIAgent CreateAIAgent(
+ public static ChatClientAgent CreateAIAgent(
this AgentsClient agentsClient,
AgentDefinition agentDefinition,
string? model = null,
string? name = null,
- AgentVersionCreationOptions? versionCreationOptions = null,
+ AgentCreationOptions? creationOptions = null,
Func? clientFactory = null,
CancellationToken cancellationToken = default)
{
@@ -355,15 +360,61 @@ public static AIAgent CreateAIAgent(
throw new ArgumentException("Model must be provided either directly or as part of a PromptAgentDefinition specialization.", nameof(model));
}
- AgentVersion agentVersion = agentsClient.CreateAgentVersion(name, agentDefinition, versionCreationOptions, cancellationToken);
- IChatClient chatClient = new AzureAIAgentChatClient(agentsClient, agentVersion, model);
+ AgentRecord agentRecord = agentsClient.CreateAgent(name, agentDefinition, creationOptions, cancellationToken);
+ IChatClient chatClient = new AzureAIAgentChatClient(agentsClient, agentRecord, model);
if (clientFactory is not null)
{
chatClient = clientFactory(chatClient);
}
- return new AzureAIAgent(agentsClient, agentVersion, new ChatClientAgent(chatClient));
+ return new ChatClientAgent(chatClient);
+ }
+
+ ///
+ /// Creates a new Prompt AI Agent using the provided and options.
+ ///
+ /// The client used to manage and interact with AI agents.
+ /// The name of the model to use for the agent. If not specified, the model must be provided as part of the agent definition.
+ /// The options for creating the agent.
+ /// A factory function to customize the creation of the chat client used by the agent.
+ /// A instance that can be used to perform operations on the newly created agent.
+ /// Thrown if neither the 'model' parameter nor a model in the agent definition is provided.
+ public static ChatClientAgent CreateAIAgent(
+ this AgentsClient agentsClient,
+ string model,
+ ChatClientAgentOptions options,
+ Func? clientFactory = null)
+ {
+ Throw.IfNull(agentsClient);
+ Throw.IfNullOrWhitespace(model);
+ Throw.IfNull(options);
+
+ PromptAgentDefinition promptAgentDefinition = new(model)
+ {
+ Model = model,
+ Instructions = options.Instructions,
+ };
+
+ if (options.ChatOptions?.Tools is { Count: > 0 })
+ {
+ foreach (var tool in options.ChatOptions.Tools)
+ {
+ promptAgentDefinition.Tools.Add(ToResponseTool(tool, options.ChatOptions));
+ }
+ }
+
+ AgentCreationOptions creationOptions = new();
+
+ AgentRecord agentRecord = agentsClient.CreateAgent(options.Name, promptAgentDefinition, creationOptions);
+ IChatClient chatClient = new AzureAIAgentChatClient(agentsClient, agentRecord, model);
+
+ if (clientFactory is not null)
+ {
+ chatClient = clientFactory(chatClient);
+ }
+
+ return new ChatClientAgent(chatClient);
}
///
@@ -374,7 +425,7 @@ public static AIAgent CreateAIAgent(
/// The definition that specifies the configuration and behavior of the agent to create.
/// The name of the model to use for the agent. If not specified, the model must be provided as part of the agent definition.
/// The name for the agent.
- /// Settings that control the creation of the agent version.
+ /// Settings that control the creation of the agent.
/// A factory function to customize the creation of the chat client used by the agent.
/// A token to monitor for cancellation requests.
/// A task that represents the asynchronous operation. The task result contains the created AI agent.
@@ -384,7 +435,7 @@ public static async Task CreateAIAgentAsync(
AgentDefinition agentDefinition,
string? model = null,
string? name = null,
- AgentVersionCreationOptions? versionCreationOptions = null,
+ AgentCreationOptions? creationOptions = null,
Func? clientFactory = null,
CancellationToken cancellationToken = default)
{
@@ -397,15 +448,15 @@ public static async Task CreateAIAgentAsync(
throw new ArgumentException("Model must be provided either directly or as part of a PromptAgentDefinition specialization.", nameof(model));
}
- AgentVersion agentVersion = await agentsClient.CreateAgentVersionAsync(name, agentDefinition, versionCreationOptions, cancellationToken).ConfigureAwait(false);
- IChatClient chatClient = new AzureAIAgentChatClient(agentsClient, agentVersion, model);
+ AgentRecord agentRecord = await agentsClient.CreateAgentAsync(name, agentDefinition, creationOptions, cancellationToken).ConfigureAwait(false);
+ IChatClient chatClient = new AzureAIAgentChatClient(agentsClient, agentRecord, model);
if (clientFactory is not null)
{
chatClient = clientFactory(chatClient);
}
- return new AzureAIAgent(agentsClient, agentVersion, new ChatClientAgent(chatClient));
+ return new ChatClientAgent(chatClient);
}
///
@@ -445,21 +496,23 @@ public static async Task CreateAIAgentAsync(
Throw.IfNull(agentsClient);
Throw.IfNull(model);
- var (promptAgentDefinition, versionCreationOptions) = CreatePromptAgentDefinitionAndOptions(
+ var (promptAgentDefinition, creationOptions) = CreatePromptAgentDefinitionAndOptions(
model, instructions, temperature, topP, raiConfig, reasoningOptions, textOptions, tools, structuredInputs, metadata);
- AgentVersion agentVersion = await agentsClient.CreateAgentVersionAsync(name, promptAgentDefinition, versionCreationOptions, cancellationToken).ConfigureAwait(false);
- IChatClient chatClient = new AzureAIAgentChatClient(agentsClient, agentVersion, model);
+ AgentRecord agentRecord = await agentsClient.CreateAgentAsync(name, promptAgentDefinition, creationOptions, cancellationToken).ConfigureAwait(false);
+ IChatClient chatClient = new AzureAIAgentChatClient(agentsClient, agentRecord, model);
if (clientFactory is not null)
{
chatClient = clientFactory(chatClient);
}
- return new AzureAIAgent(agentsClient, agentVersion, new ChatClientAgent(chatClient));
+ return new ChatClientAgent(chatClient);
}
- private static (PromptAgentDefinition, AgentVersionCreationOptions?) CreatePromptAgentDefinitionAndOptions(
+ #region Private
+
+ private static (PromptAgentDefinition, AgentCreationOptions?) CreatePromptAgentDefinitionAndOptions(
string model,
string? instructions,
float? temperature,
@@ -482,13 +535,13 @@ private static (PromptAgentDefinition, AgentVersionCreationOptions?) CreatePromp
TextOptions = textOptions,
};
- AgentVersionCreationOptions? versionCreationOptions = null;
+ AgentCreationOptions? creationOptions = null;
if (metadata is not null)
{
- versionCreationOptions = new();
+ creationOptions = new();
foreach (var kvp in metadata)
{
- versionCreationOptions.Metadata.Add(kvp.Key, kvp.Value);
+ creationOptions.Metadata.Add(kvp.Key, kvp.Value);
}
}
@@ -515,6 +568,227 @@ private static (PromptAgentDefinition, AgentVersionCreationOptions?) CreatePromp
}
}
- return (promptAgentDefinition, versionCreationOptions);
+ return (promptAgentDefinition, creationOptions);
}
+
+ /// Key into AdditionalProperties used to store a strict option.
+ private const string StrictKey = "strictJsonSchema";
+
+ private static FunctionTool ToResponseFunctionTool(AIFunctionDeclaration aiFunction, ChatOptions? options = null)
+ {
+ bool? strict =
+ HasStrict(aiFunction.AdditionalProperties) ??
+ HasStrict(options?.AdditionalProperties);
+
+ return ResponseTool.CreateFunctionTool(
+ aiFunction.Name,
+ ToOpenAIFunctionParameters(aiFunction, strict),
+ strict,
+ aiFunction.Description);
+ }
+
+ /// Gets whether the properties specify that strict schema handling is desired.
+ private static bool? HasStrict(IReadOnlyDictionary? additionalProperties) =>
+ additionalProperties?.TryGetValue(StrictKey, out object? strictObj) is true &&
+ strictObj is bool strictValue ?
+ strictValue : null;
+
+ /// Extracts from an the parameters and strictness setting for use with OpenAI's APIs.
+ private static BinaryData ToOpenAIFunctionParameters(AIFunctionDeclaration aiFunction, bool? strict)
+ {
+ // Perform any desirable transformations on the function's JSON schema, if it'll be used in a strict setting.
+ JsonElement jsonSchema = strict is true ?
+ GetStrictSchemaTransformCache().GetOrCreateTransformedSchema(aiFunction) :
+ aiFunction.JsonSchema;
+
+ // Roundtrip the schema through the ToolJson model type to remove extra properties
+ // and force missing ones into existence, then return the serialized UTF8 bytes as BinaryData.
+ var tool = JsonSerializer.Deserialize(jsonSchema, AgentsClientJsonContext.Default.ToolJson)!;
+ return BinaryData.FromBytes(JsonSerializer.SerializeToUtf8Bytes(tool, AgentsClientJsonContext.Default.ToolJson));
+ }
+
+ ///
+ /// Gets the JSON schema transformer cache conforming to OpenAI strict / structured output restrictions per
+ /// https://platform.openai.com/docs/guides/structured-outputs?api-mode=responses#supported-schemas.
+ ///
+ private static AIJsonSchemaTransformCache GetStrictSchemaTransformCache() => new(new()
+ {
+ DisallowAdditionalProperties = true,
+ ConvertBooleanSchemas = true,
+ MoveDefaultKeywordToDescription = true,
+ RequireAllProperties = true,
+ TransformSchemaNode = (ctx, node) =>
+ {
+ // Move content from common but unsupported properties to description. In particular, we focus on properties that
+ // the AIJsonUtilities schema generator might produce and/or that are explicitly mentioned in the OpenAI documentation.
+
+ if (node is JsonObject schemaObj)
+ {
+ StringBuilder? additionalDescription = null;
+
+ ReadOnlySpan unsupportedProperties =
+ [
+ // Produced by AIJsonUtilities but not in allow list at https://platform.openai.com/docs/guides/structured-outputs#supported-properties:
+ "contentEncoding", "contentMediaType", "not",
+
+ // Explicitly mentioned at https://platform.openai.com/docs/guides/structured-outputs?api-mode=responses#key-ordering as being unsupported with some models:
+ "minLength", "maxLength", "pattern", "format",
+ "minimum", "maximum", "multipleOf",
+ "patternProperties",
+ "minItems", "maxItems",
+
+ // Explicitly mentioned at https://learn.microsoft.com/azure/ai-services/openai/how-to/structured-outputs?pivots=programming-language-csharp&tabs=python-secure%2Cdotnet-entra-id#unsupported-type-specific-keywords
+ // as being unsupported with Azure OpenAI:
+ "unevaluatedProperties", "propertyNames", "minProperties", "maxProperties",
+ "unevaluatedItems", "contains", "minContains", "maxContains", "uniqueItems",
+ ];
+
+ foreach (string propName in unsupportedProperties)
+ {
+ if (schemaObj[propName] is { } propNode)
+ {
+ _ = schemaObj.Remove(propName);
+ AppendLine(ref additionalDescription, propName, propNode);
+ }
+ }
+
+ if (additionalDescription is not null)
+ {
+ schemaObj["description"] = schemaObj["description"] is { } descriptionNode && descriptionNode.GetValueKind() == JsonValueKind.String ?
+ $"{descriptionNode.GetValue()}{Environment.NewLine}{additionalDescription}" :
+ additionalDescription.ToString();
+ }
+
+ return node;
+
+ static void AppendLine(ref StringBuilder? sb, string propName, JsonNode propNode)
+ {
+ sb ??= new();
+
+ if (sb.Length > 0)
+ {
+ _ = sb.AppendLine();
+ }
+
+ _ = sb.Append(propName).Append(": ").Append(propNode);
+ }
+ }
+
+ return node;
+ },
+ });
+
+ private static ResponseTool ToResponseTool(AITool tool, ChatOptions options)
+ {
+ switch (tool)
+ {
+ case AIFunctionDeclaration aiFunction:
+ return ToResponseFunctionTool(aiFunction, options);
+
+ case HostedWebSearchTool webSearchTool:
+ WebSearchToolLocation? location = null;
+ if (webSearchTool.AdditionalProperties.TryGetValue(nameof(WebSearchToolLocation), out object? objLocation))
+ {
+ location = objLocation as WebSearchToolLocation;
+ }
+
+ WebSearchToolContextSize? size = null;
+ if (webSearchTool.AdditionalProperties.TryGetValue(nameof(WebSearchToolContextSize), out object? objSize) &&
+ objSize is WebSearchToolContextSize)
+ {
+ size = (WebSearchToolContextSize)objSize;
+ }
+
+ return ResponseTool.CreateWebSearchTool(location, size);
+
+ case HostedFileSearchTool fileSearchTool:
+ return ResponseTool.CreateFileSearchTool(
+ fileSearchTool.Inputs?.OfType().Select(c => c.VectorStoreId) ?? [],
+ fileSearchTool.MaximumResultCount);
+
+ case HostedCodeInterpreterTool codeTool:
+ return ResponseTool.CreateCodeInterpreterTool(
+ new CodeInterpreterToolContainer(codeTool.Inputs?.OfType().Select(f => f.FileId).ToList() is { Count: > 0 } ids ?
+ CodeInterpreterToolContainerConfiguration.CreateAutomaticContainerConfiguration(ids) :
+ new()));
+
+ case HostedMcpServerTool mcpTool:
+ McpTool responsesMcpTool = Uri.TryCreate(mcpTool.ServerAddress, UriKind.Absolute, out Uri? url) ?
+ ResponseTool.CreateMcpTool(
+ mcpTool.ServerName,
+ url,
+ mcpTool.AuthorizationToken,
+ mcpTool.ServerDescription) :
+ ResponseTool.CreateMcpTool(
+ mcpTool.ServerName,
+ new McpToolConnectorId(mcpTool.ServerAddress),
+ mcpTool.AuthorizationToken,
+ mcpTool.ServerDescription);
+
+ if (mcpTool.AllowedTools is not null)
+ {
+ responsesMcpTool.AllowedTools = new();
+ AddAllMcpFilters(mcpTool.AllowedTools, responsesMcpTool.AllowedTools);
+ }
+
+ switch (mcpTool.ApprovalMode)
+ {
+ case HostedMcpServerToolAlwaysRequireApprovalMode:
+ responsesMcpTool.ToolCallApprovalPolicy = new McpToolCallApprovalPolicy(GlobalMcpToolCallApprovalPolicy.AlwaysRequireApproval);
+ break;
+
+ case HostedMcpServerToolNeverRequireApprovalMode:
+ responsesMcpTool.ToolCallApprovalPolicy = new McpToolCallApprovalPolicy(GlobalMcpToolCallApprovalPolicy.NeverRequireApproval);
+ break;
+
+ case HostedMcpServerToolRequireSpecificApprovalMode specificMode:
+ responsesMcpTool.ToolCallApprovalPolicy = new McpToolCallApprovalPolicy(new CustomMcpToolCallApprovalPolicy());
+
+ if (specificMode.AlwaysRequireApprovalToolNames is { Count: > 0 } alwaysRequireToolNames)
+ {
+ responsesMcpTool.ToolCallApprovalPolicy.CustomPolicy.ToolsAlwaysRequiringApproval = new();
+ AddAllMcpFilters(alwaysRequireToolNames, responsesMcpTool.ToolCallApprovalPolicy.CustomPolicy.ToolsAlwaysRequiringApproval);
+ }
+
+ if (specificMode.NeverRequireApprovalToolNames is { Count: > 0 } neverRequireToolNames)
+ {
+ responsesMcpTool.ToolCallApprovalPolicy.CustomPolicy.ToolsNeverRequiringApproval = new();
+ AddAllMcpFilters(neverRequireToolNames, responsesMcpTool.ToolCallApprovalPolicy.CustomPolicy.ToolsNeverRequiringApproval);
+ }
+
+ break;
+ }
+
+ return responsesMcpTool;
+
+ default:
+ throw new NotSupportedException($"Tool of type '{tool.GetType().FullName}' is not supported.");
+ }
+ }
+
+ private static void AddAllMcpFilters(IList toolNames, McpToolFilter filter)
+ {
+ foreach (var toolName in toolNames)
+ {
+ filter.ToolNames.Add(toolName);
+ }
+ }
+
+ /// Used to create the JSON payload for an OpenAI tool description.
+ internal sealed class ToolJson
+ {
+ [JsonPropertyName("type")]
+ public string Type { get; set; } = "object";
+
+ [JsonPropertyName("required")]
+ public HashSet Required { get; set; } = [];
+
+ [JsonPropertyName("properties")]
+ public Dictionary Properties { get; set; } = [];
+
+ [JsonPropertyName("additionalProperties")]
+ public bool AdditionalProperties { get; set; }
+ }
+
+ #endregion
}
diff --git a/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AgentsClientJsonContext.cs b/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AgentsClientJsonContext.cs
new file mode 100644
index 0000000000..d54bd5ff8a
--- /dev/null
+++ b/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AgentsClientJsonContext.cs
@@ -0,0 +1,17 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using System.Text.Json;
+using System.Text.Json.Serialization;
+
+#pragma warning disable MEAI001 // 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.
+
+namespace Azure.AI.Agents;
+
+/// Source-generated JSON type information for use by all OpenAI implementations.
+[JsonSourceGenerationOptions(JsonSerializerDefaults.Web,
+ UseStringEnumConverter = true,
+ DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
+ WriteIndented = true)]
+[JsonSerializable(typeof(AgentsClientExtensions.ToolJson))]
+internal sealed partial class AgentsClientJsonContext : JsonSerializerContext;
diff --git a/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AzureAIAgent.cs b/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AzureAIAgent.cs
deleted file mode 100644
index 02c85d5fe2..0000000000
--- a/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AzureAIAgent.cs
+++ /dev/null
@@ -1,94 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-
-using System.Runtime.CompilerServices;
-using Azure.AI.Agents;
-using Microsoft.Extensions.AI;
-
-#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.
-
-namespace Microsoft.Agents.AI.AzureAIAgents;
-
-///
-/// Provides an agent implementation that integrates with Azure AI Agents services, enabling chat-based interactions.
-///
-internal sealed class AzureAIAgent : DelegatingAIAgent
-{
- private readonly AgentsClient _agentsClient;
- private readonly AgentVersion _agentVersion;
- private readonly AIAgentMetadata _agentMetadata;
-
- internal AzureAIAgent(AgentsClient agentsClient, AgentVersion agentVersion, AIAgent innerAgent) : base(innerAgent)
- {
- this._agentsClient = agentsClient;
- this._agentVersion = agentVersion;
- this._agentMetadata = new AIAgentMetadata("azure.ai.agents");
-
- if (innerAgent.GetService() is null)
- {
- throw new InvalidOperationException("The provided AI Agent must to have a ChatClientAgent in the decoration pipeline for the agent to function properly.");
- }
- }
-
- ///
- public override string Id => this._agentVersion.Id;
-
- ///
- public override string Name => this._agentVersion.Name;
-
- ///
- public override async Task RunAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default)
- {
- this.ValidateChatClient();
- this.ValidateThread(thread);
-
- return await this.InnerAgent.RunAsync(messages, thread, options, cancellationToken).ConfigureAwait(false);
- }
-
- ///
- public override async IAsyncEnumerable RunStreamingAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default)
- {
- this.ValidateChatClient();
- this.ValidateThread(thread);
-
- await foreach (var update in this.InnerAgent.RunStreamingAsync(messages, thread, options, cancellationToken).ConfigureAwait(false))
- {
- yield return update;
- }
- }
-
- ///
- public override object? GetService(Type serviceType, object? serviceKey = null)
- {
- return (serviceKey is null && serviceType == typeof(AgentVersion))
- ? this._agentVersion
- : (serviceKey is null && serviceType == typeof(AgentsClient))
- ? this._agentsClient
- : (serviceKey is null && serviceType == typeof(AIAgentMetadata))
- ? this._agentMetadata
- : base.GetService(serviceType, serviceKey);
- }
-
- private void ValidateChatClient()
- {
- _ = this.GetService()
- ?? throw new InvalidOperationException("The provided IChatClient needs to be decorated with a AzureAIAgent ChatClient for the agent to function properly.");
- }
-
- private void ValidateThread(AgentThread? thread)
- {
- if (thread is null)
- {
- return;
- }
-
- if (thread is not ChatClientAgentThread asChatClientAgentThread)
- {
- throw new InvalidOperationException("The provided thread is not compatible with the agent. Only threads created by the agent can be used.");
- }
-
- if (string.IsNullOrWhiteSpace(asChatClientAgentThread.ConversationId))
- {
- throw new InvalidOperationException("The ChatClientAgentThread does not have a valid ConversationId.");
- }
- }
-}
diff --git a/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AzureAIAgentChatClient.cs b/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AzureAIAgentChatClient.cs
index f1eee8faca..ede35bb0c7 100644
--- a/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AzureAIAgentChatClient.cs
+++ b/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AzureAIAgentChatClient.cs
@@ -18,22 +18,22 @@ internal sealed class AzureAIAgentChatClient : DelegatingChatClient
{
private readonly ChatClientMetadata? _metadata;
private readonly AgentsClient _agentsClient;
- private readonly AgentVersion _agentVersion;
+ private readonly AgentRecord _agentRecord;
///
/// Initializes a new instance of the class.
///
/// An instance of to interact with Azure AI Agents services.
- /// An instance of representing the specific agent version to use.
- /// The model to use for the chat client.
+ /// An instance of representing the specific agent to use.
+ /// The AI model to use for the chat client.
///
/// The provided should be decorated with a for proper functionality.
///
- internal AzureAIAgentChatClient(AgentsClient agentsClient, AgentVersion agentVersion, string model)
+ internal AzureAIAgentChatClient(AgentsClient agentsClient, AgentRecord agentRecord, string model)
: base(agentsClient.GetOpenAIClient().GetOpenAIResponseClient(Throw.IfNull(model)).AsIChatClient())
{
this._agentsClient = Throw.IfNull(agentsClient);
- this._agentVersion = Throw.IfNull(agentVersion);
+ this._agentRecord = Throw.IfNull(agentRecord);
this._metadata = new ChatClientMetadata("azure.ai.agents");
}
@@ -45,7 +45,9 @@ internal AzureAIAgentChatClient(AgentsClient agentsClient, AgentVersion agentVer
: (serviceKey is null && serviceType == typeof(AgentsClient))
? this._agentsClient
: (serviceKey is null && serviceType == typeof(AgentVersion))
- ? this._agentVersion
+ ? this._agentRecord.Versions.Latest
+ : (serviceKey is null && serviceType == typeof(AgentRecord))
+ ? this._agentRecord
: base.GetService(serviceType, serviceKey);
}
@@ -120,7 +122,7 @@ private void SetChatOptionsFactory(ChatOptions? chatOptions, AgentConversation a
responseCreationOptions = new ResponseCreationOptions();
}
- responseCreationOptions.SetAgentReference(this._agentVersion.Name);
+ responseCreationOptions.SetAgentReference(this._agentRecord.Name);
responseCreationOptions.SetConversationReference(agentConversation);
return responseCreationOptions;
From 34ac7dddbbb973ee8da7e55c9c52717f634e058d Mon Sep 17 00:00:00 2001
From: Roger Barreto <19890735+rogerbarreto@users.noreply.github.com>
Date: Wed, 29 Oct 2025 11:14:51 +0000
Subject: [PATCH 10/16] Add GetAiAgent extensions
---
dotnet/agent-framework-dotnet.slnx | 1 +
.../Agent_With_AzureAIAgent.csproj | 20 ++
.../Agent_With_AzureAIAgent/Program.cs | 36 +++
.../Agent_With_AzureAIAgent/README.md | 16 +
.../AgentsClientExtensions.cs | 300 +++++++-----------
.../AzureAIAgentChatClient.cs | 67 ++--
6 files changed, 214 insertions(+), 226 deletions(-)
create mode 100644 dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_With_AzureAIAgent.csproj
create mode 100644 dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Program.cs
create mode 100644 dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/README.md
diff --git a/dotnet/agent-framework-dotnet.slnx b/dotnet/agent-framework-dotnet.slnx
index 0bc95e9d96..8a8e4c3743 100644
--- a/dotnet/agent-framework-dotnet.slnx
+++ b/dotnet/agent-framework-dotnet.slnx
@@ -28,6 +28,7 @@
+
diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_With_AzureAIAgent.csproj b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_With_AzureAIAgent.csproj
new file mode 100644
index 0000000000..0aa9f48d64
--- /dev/null
+++ b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_With_AzureAIAgent.csproj
@@ -0,0 +1,20 @@
+
+
+
+ Exe
+ net9.0
+
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Program.cs b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Program.cs
new file mode 100644
index 0000000000..aa9f81178b
--- /dev/null
+++ b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Program.cs
@@ -0,0 +1,36 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+// This sample shows how to create and use a simple AI agent with Azure Foundry Agents as the backend.
+
+using Azure.AI.Agents;
+using Azure.Identity;
+using Microsoft.Agents.AI;
+
+var endpoint = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_FOUNDRY_PROJECT_ENDPOINT is not set.");
+var deploymentName = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
+
+const string JokerName = "Joker";
+const string JokerInstructions = "You are good at telling jokes.";
+
+// Get a client to create/retrieve server side agents with.
+var agentsClient = new AgentsClient(new Uri(endpoint), new AzureCliCredential());
+
+// Define the agent you want to create.
+var agentDefinition = new PromptAgentDefinition(model: deploymentName) { Instructions = JokerInstructions };
+
+// You can create a server side agent with the Azure.AI.Agents SDK.
+var agentRecord = agentsClient.CreateAgent(name: JokerName, definition: agentDefinition).Value;
+
+// You can retrieve an already created server side agent as an AIAgent.
+AIAgent existingAgent = await agentsClient.GetAIAgentAsync(agentRecord.Name);
+
+// You can also create a server side persistent agent and return it as an AIAgent directly.
+var createdAgent = agentsClient.CreateAIAgent(deploymentName, name: JokerName, instructions: JokerInstructions);
+
+// You can then invoke the agent like any other AIAgent.
+AgentThread thread = existingAgent.GetNewThread();
+Console.WriteLine(await existingAgent.RunAsync("Tell me a joke about a pirate.", thread));
+
+// Cleanup for sample purposes.
+agentsClient.DeleteAgent(agentRecord.Name);
+agentsClient.DeleteAgent(createdAgent.Name);
diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/README.md b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/README.md
new file mode 100644
index 0000000000..df0854ba2f
--- /dev/null
+++ b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/README.md
@@ -0,0 +1,16 @@
+# Prerequisites
+
+Before you begin, ensure you have the following prerequisites:
+
+- .NET 8.0 SDK or later
+- Azure Foundry service endpoint and deployment configured
+- Azure CLI installed and authenticated (for Azure credential authentication)
+
+**Note**: This demo uses Azure CLI credentials for authentication. Make sure you're logged in with `az login` and have access to the Azure Foundry resource. For more information, see the [Azure CLI documentation](https://learn.microsoft.com/cli/azure/authenticate-azure-cli-interactively).
+
+Set the following environment variables:
+
+```powershell
+$env:AZURE_FOUNDRY_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" # Replace with your Azure Foundry resource endpoint
+$env:AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults to gpt-4o-mini
+```
diff --git a/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AgentsClientExtensions.cs b/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AgentsClientExtensions.cs
index 25ba5d87e6..0d10097369 100644
--- a/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AgentsClientExtensions.cs
+++ b/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AgentsClientExtensions.cs
@@ -20,264 +20,203 @@ namespace Azure.AI.Agents;
///
public static class AgentsClientExtensions
{
- /*
///
- /// Gets a runnable agent instance from the provided response containing persistent agent metadata.
+ /// Gets a runnable agent instance from the provided agent record.
///
- /// The client used to interact with persistent agents. Cannot be .
- /// The response containing the persistent agent to be converted. Cannot be .
- /// The default to use when interacting with the agent.
- /// Provides a way to customize the creation of the underlying used by the agent.
- /// A instance that can be used to perform operations on the persistent agent.
- public static ChatClientAgent GetAIAgent(this AgentsClient client, Response persistentAgentResponse, ChatOptions? chatOptions = null, Func? clientFactory = null)
- {
- if (persistentAgentResponse is null)
- {
- throw new ArgumentNullException(nameof(persistentAgentResponse));
- }
-
- return GetAIAgent(client, persistentAgentResponse.Value, chatOptions, clientFactory);
- }
-
- ///
- /// Gets a runnable agent instance from a containing metadata about a persistent agent.
- ///
- /// The client used to interact with persistent agents. Cannot be .
- /// The persistent agent metadata to be converted. Cannot be .
+ /// The client used to interact with persistent agents. Cannot be .
+ /// The model to be used by the agent.
+ /// The agent record to be converted. Cannot be .
/// The default to use when interacting with the agent.
/// Provides a way to customize the creation of the underlying used by the agent.
/// A instance that can be used to perform operations on the persistent agent.
- public static ChatClientAgent GetAIAgent(this PersistentAgentsClient persistentAgentsClient, PersistentAgent persistentAgentMetadata, ChatOptions? chatOptions = null, Func? clientFactory = null)
+ public static ChatClientAgent GetAIAgent(
+ this AgentsClient agentsClient,
+ string model,
+ AgentRecord agentRecord,
+ ChatOptions? chatOptions = null,
+ Func? clientFactory = null)
{
- if (persistentAgentMetadata is null)
- {
- throw new ArgumentNullException(nameof(persistentAgentMetadata));
- }
-
- if (persistentAgentsClient is null)
- {
- throw new ArgumentNullException(nameof(persistentAgentsClient));
- }
-
- var chatClient = persistentAgentsClient.AsIChatClient(persistentAgentMetadata.Id);
+ Throw.IfNull(agentsClient);
+ Throw.IfNull(model);
+ Throw.IfNull(agentRecord);
- if (clientFactory is not null)
+ if (model is null)
{
- chatClient = clientFactory(chatClient);
+ throw new ArgumentException("When not using a PromptAgent the model needs to be provided in the ChatOptions.ModelId property");
}
- return new ChatClientAgent(chatClient, options: new()
- {
- Id = persistentAgentMetadata.Id,
- Name = persistentAgentMetadata.Name,
- Description = persistentAgentMetadata.Description,
- Instructions = persistentAgentMetadata.Instructions,
- ChatOptions = chatOptions
- });
+ return GetAIAgent(agentsClient, model, agentRecord, new ChatClientAgentOptions() { ChatOptions = chatOptions }, clientFactory);
}
///
- /// Retrieves an existing server side agent, wrapped as a using the provided .
+ /// Retrieves an existing server side agent, wrapped as a using the provided .
///
- /// The to create the with.
- /// A for the persistent agent.
- /// The ID of the server side agent to create a for.
+ /// The to create the with.
+ /// A for the Azure AI Agent.
+ /// The model to be used by the agent.
+ /// The name of the server side agent to create a for.
/// Options that should apply to all runs of the agent.
/// Provides a way to customize the creation of the underlying used by the agent.
- /// The to monitor for cancellation requests. The default is .
/// A instance that can be used to perform operations on the persistent agent.
public static ChatClientAgent GetAIAgent(
- this PersistentAgentsClient persistentAgentsClient,
- string agentId,
+ this AgentsClient agentsClient,
+ string model,
+ string agentName,
ChatOptions? chatOptions = null,
- Func? clientFactory = null,
- CancellationToken cancellationToken = default)
+ Func? clientFactory = null)
{
- if (persistentAgentsClient is null)
- {
- throw new ArgumentNullException(nameof(persistentAgentsClient));
- }
+ Throw.IfNull(agentsClient);
+ Throw.IfNullOrWhitespace(model);
+ Throw.IfNullOrWhitespace(agentName);
- if (string.IsNullOrWhiteSpace(agentId))
+ var agentRecord = agentsClient.GetAgent(agentName);
+ if (agentRecord is null)
{
- throw new ArgumentException($"{nameof(agentId)} should not be null or whitespace.", nameof(agentId));
+ throw new InvalidOperationException($"Agent with name '{agentName}' not found.");
}
- var persistentAgentResponse = persistentAgentsClient.Administration.GetAgent(agentId, cancellationToken);
- return persistentAgentsClient.GetAIAgent(persistentAgentResponse, chatOptions, clientFactory);
+ return GetAIAgent(agentsClient, model, agentRecord, chatOptions, clientFactory);
}
///
- /// Retrieves an existing server side agent, wrapped as a using the provided .
+ /// Retrieves an existing server side agent, wrapped as a using the provided .
///
/// The to create the with.
/// A for the persistent agent.
- /// The ID of the server side agent to create a for.
+ /// The model to be used by the agent.
+ /// The name of the server side agent to create a for.
/// Options that should apply to all runs of the agent.
/// Provides a way to customize the creation of the underlying used by the agent.
/// The to monitor for cancellation requests. The default is .
/// A instance that can be used to perform operations on the persistent agent.
public static async Task GetAIAgentAsync(
this AgentsClient agentsClient,
- string name,
+ string model,
+ string agentName,
ChatOptions? chatOptions = null,
Func? clientFactory = null,
CancellationToken cancellationToken = default)
{
Throw.IfNull(agentsClient);
- Throw.IfNullOrWhitespace(name);
+ Throw.IfNullOrWhitespace(model);
+ Throw.IfNullOrWhitespace(agentName);
- var agent = await agentsClient.GetAgentAsync(name, cancellationToken).ConfigureAwait(false);
- if (agent is null)
+ var agentRecord = await agentsClient.GetAgentAsync(agentName, cancellationToken).ConfigureAwait(false);
+ if (agentRecord is null)
{
- agent.ver
- throw new InvalidOperationException($"Agent with name '{name}' not found.");
+ throw new InvalidOperationException($"Agent with name '{agentName}' not found.");
}
- var persistentAgentResponse = await agentsClient.Administration.GetAgentAsync(name, cancellationToken).ConfigureAwait(false);
- return agentsClient.GetAIAgent(persistentAgentResponse, chatOptions, clientFactory);
+ return GetAIAgent(agentsClient, model, agentRecord, chatOptions, clientFactory);
}
///
- /// Gets a runnable agent instance from the provided response containing persistent agent metadata.
+ /// Gets a runnable agent instance from a containing metadata about an Azure AI Agent.
///
- /// The client used to interact with persistent agents. Cannot be .
- /// The response containing the persistent agent to be converted. Cannot be .
+ /// The client used to interact with persistent agents. Cannot be .
+ /// The model to be used by the agent.
+ /// The persistent agent metadata to be converted. Cannot be .
/// Full set of options to configure the agent.
/// Provides a way to customize the creation of the underlying used by the agent.
/// A instance that can be used to perform operations on the persistent agent.
- /// Thrown when or is .
- public static ChatClientAgent GetAIAgent(this PersistentAgentsClient persistentAgentsClient, Response persistentAgentResponse, ChatClientAgentOptions options, Func? clientFactory = null)
+ /// Thrown when , , or is .
+ public static ChatClientAgent GetAIAgent(this AgentsClient agentsClient, string model, AgentRecord agentRecord, ChatClientAgentOptions? options = null, Func? clientFactory = null)
{
- if (persistentAgentResponse is null)
- {
- throw new ArgumentNullException(nameof(persistentAgentResponse));
- }
-
- return GetAIAgent(persistentAgentsClient, persistentAgentResponse.Value, options, clientFactory);
- }
+ Throw.IfNull(agentsClient);
+ Throw.IfNullOrWhitespace(model);
+ Throw.IfNull(agentRecord);
+ Throw.IfNull(options);
- ///
- /// Gets a runnable agent instance from a containing metadata about a persistent agent.
- ///
- /// The client used to interact with persistent agents. Cannot be .
- /// The persistent agent metadata to be converted. Cannot be .
- /// Full set of options to configure the agent.
- /// Provides a way to customize the creation of the underlying used by the agent.
- /// A instance that can be used to perform operations on the persistent agent.
- /// Thrown when or is .
- public static ChatClientAgent GetAIAgent(this PersistentAgentsClient persistentAgentsClient, PersistentAgent persistentAgentMetadata, ChatClientAgentOptions options, Func? clientFactory = null)
- {
- if (persistentAgentMetadata is null)
- {
- throw new ArgumentNullException(nameof(persistentAgentMetadata));
- }
+ IChatClient chatClient = new AzureAIAgentChatClient(agentsClient, agentRecord, model);
- if (persistentAgentsClient is null)
+ if (clientFactory is not null)
{
- throw new ArgumentNullException(nameof(persistentAgentsClient));
+ chatClient = clientFactory(chatClient);
}
- if (options is null)
- {
- throw new ArgumentNullException(nameof(options));
- }
+ ChatClientAgentOptions? agentOptions;
- var chatClient = persistentAgentsClient.AsIChatClient(persistentAgentMetadata.Id);
+ // If options are null, populate from agentRecord definition
+ var version = agentRecord.Versions.Latest;
- if (clientFactory is not null)
+ if (options is null)
{
- chatClient = clientFactory(chatClient);
- }
+ agentOptions = new();
+ agentOptions.Id = agentRecord.Id;
+ agentOptions.Name = agentRecord.Name;
- var agentOptions = new ChatClientAgentOptions()
- {
- Id = persistentAgentMetadata.Id,
- Name = options.Name ?? persistentAgentMetadata.Name,
- Description = options.Description ?? persistentAgentMetadata.Description,
- Instructions = options.Instructions ?? persistentAgentMetadata.Instructions,
- ChatOptions = options.ChatOptions,
- AIContextProviderFactory = options.AIContextProviderFactory,
- ChatMessageStoreFactory = options.ChatMessageStoreFactory,
- UseProvidedChatClientAsIs = options.UseProvidedChatClientAsIs
- };
+ agentOptions.Description = version.Description;
- return new ChatClientAgent(chatClient, agentOptions);
- }
+ if (version.Definition is PromptAgentDefinition promptDef && promptDef.Tools is { Count: > 0 })
+ {
+ agentOptions.ChatOptions = new ChatOptions();
+ agentOptions.ChatOptions.Tools = [];
+ agentOptions.Instructions = promptDef.Instructions;
- ///
- /// Retrieves an existing server side agent, wrapped as a using the provided .
- ///
- /// The to create the with.
- /// The ID of the server side agent to create a for.
- /// Full set of options to configure the agent.
- /// Provides a way to customize the creation of the underlying used by the agent.
- /// The to monitor for cancellation requests. The default is .
- /// A instance that can be used to perform operations on the persistent agent.
- /// Thrown when or is .
- /// Thrown when is empty or whitespace.
- public static ChatClientAgent GetAIAgent(
- this PersistentAgentsClient persistentAgentsClient,
- string agentId,
- ChatClientAgentOptions options,
- Func? clientFactory = null,
- CancellationToken cancellationToken = default)
- {
- if (persistentAgentsClient is null)
- {
- throw new ArgumentNullException(nameof(persistentAgentsClient));
+ foreach (var tool in promptDef.Tools)
+ {
+ agentOptions.ChatOptions.Tools.Add(tool);
+ }
+ }
}
-
- if (string.IsNullOrWhiteSpace(agentId))
+ else
{
- throw new ArgumentException($"{nameof(agentId)} should not be null or whitespace.", nameof(agentId));
- }
+ // When agent options it is used when available otherwise fallback to the agent definition used for the agent record.
+ agentOptions = new ChatClientAgentOptions()
+ {
+ Id = options.Id ?? agentRecord.Id,
+ Name = options.Name ?? agentRecord.Name,
+ Description = options.Description ?? version.Description,
+ Instructions = options.Instructions ?? options.ChatOptions?.Instructions ?? (version.Definition as PromptAgentDefinition)?.Instructions,
+ ChatOptions = options.ChatOptions,
+ AIContextProviderFactory = options.AIContextProviderFactory,
+ ChatMessageStoreFactory = options.ChatMessageStoreFactory,
+ UseProvidedChatClientAsIs = options.UseProvidedChatClientAsIs
+ };
+
+ // If no tools were provided in options, but exist in the agent definition, use those.
+ if (agentOptions.ChatOptions?.Tools is null or { Count: 0 } && version.Definition is PromptAgentDefinition promptDef && promptDef.Tools is { Count: > 0 })
+ {
+ agentOptions.ChatOptions ??= new ChatOptions();
+ agentOptions.ChatOptions.Tools ??= [];
- if (options is null)
- {
- throw new ArgumentNullException(nameof(options));
+ foreach (var tool in promptDef.Tools)
+ {
+ agentOptions.ChatOptions.Tools.Add(tool);
+ }
+ }
}
- var persistentAgentResponse = persistentAgentsClient.Administration.GetAgent(agentId, cancellationToken);
- return persistentAgentsClient.GetAIAgent(persistentAgentResponse, options, clientFactory);
+ return new ChatClientAgent(chatClient, agentOptions);
}
///
- /// Retrieves an existing server side agent, wrapped as a using the provided .
+ /// Retrieves an existing server side agent, wrapped as a using the provided .
///
- /// The to create the with.
- /// The ID of the server side agent to create a for.
+ /// The to create the with.
+ /// The model to be used by the agent.
+ /// The ID of the server side agent to create a for.
/// Full set of options to configure the agent.
/// Provides a way to customize the creation of the underlying used by the agent.
/// The to monitor for cancellation requests. The default is .
/// A instance that can be used to perform operations on the persistent agent.
- /// Thrown when or is .
- /// Thrown when is empty or whitespace.
+ /// Thrown when or is .
+ /// Thrown when is empty or whitespace.
public static async Task GetAIAgentAsync(
- this PersistentAgentsClient persistentAgentsClient,
- string agentId,
+ this AgentsClient agentsClient,
+ string model,
+ string agentName,
ChatClientAgentOptions options,
Func? clientFactory = null,
CancellationToken cancellationToken = default)
{
- if (persistentAgentsClient is null)
- {
- throw new ArgumentNullException(nameof(persistentAgentsClient));
- }
-
- if (string.IsNullOrWhiteSpace(agentId))
- {
- throw new ArgumentException($"{nameof(agentId)} should not be null or whitespace.", nameof(agentId));
- }
-
- if (options is null)
- {
- throw new ArgumentNullException(nameof(options));
- }
+ Throw.IfNull(agentsClient);
+ Throw.IfNullOrWhitespace(agentName);
+ Throw.IfNull(options);
- var persistentAgentResponse = await persistentAgentsClient.Administration.GetAgentAsync(agentId, cancellationToken).ConfigureAwait(false);
- return persistentAgentsClient.GetAIAgent(persistentAgentResponse, options, clientFactory);
- }*/
+ var agentRecord = await agentsClient.GetAgentAsync(agentName, cancellationToken).ConfigureAwait(false);
+ return agentsClient.GetAIAgent(model, agentRecord, options, clientFactory);
+ }
///
/// Creates a new server side agent using the provided .
@@ -404,9 +343,7 @@ public static ChatClientAgent CreateAIAgent(
}
}
- AgentCreationOptions creationOptions = new();
-
- AgentRecord agentRecord = agentsClient.CreateAgent(options.Name, promptAgentDefinition, creationOptions);
+ AgentRecord agentRecord = agentsClient.CreateAgent(options.Name, promptAgentDefinition, new() { Description = options.Description });
IChatClient chatClient = new AzureAIAgentChatClient(agentsClient, agentRecord, model);
if (clientFactory is not null)
@@ -571,6 +508,13 @@ private static (PromptAgentDefinition, AgentCreationOptions?) CreatePromptAgentD
return (promptAgentDefinition, creationOptions);
}
+ #endregion
+
+ #region Polyfill from MEAI.OpenAI for AITool -> ResponseTool conversion
+
+ // This code will be removed and replaced by the utility tool made public in the PR below for Microsoft.Extensions.AI.OpenAI package
+ // PR https://github.com/dotnet/extensions/pull/6958
+
/// Key into AdditionalProperties used to store a strict option.
private const string StrictKey = "strictJsonSchema";
diff --git a/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AzureAIAgentChatClient.cs b/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AzureAIAgentChatClient.cs
index ede35bb0c7..e2b6dbd583 100644
--- a/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AzureAIAgentChatClient.cs
+++ b/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AzureAIAgentChatClient.cs
@@ -54,17 +54,17 @@ internal AzureAIAgentChatClient(AgentsClient agentsClient, AgentRecord agentReco
///
public override async Task GetResponseAsync(IEnumerable messages, ChatOptions? options = null, CancellationToken cancellationToken = default)
{
- var conversation = await this.GetConversationAsync(messages, options, cancellationToken).ConfigureAwait(false);
- this.SetChatOptionsFactory(options, conversation);
+ var conversation = await this.GetOrCreateConversationAsync(messages, options, cancellationToken).ConfigureAwait(false);
+ var conversationOptions = this.GetConversationEnabledChatOptions(options, conversation);
- return await base.GetResponseAsync(messages, options, cancellationToken).ConfigureAwait(false);
+ return await base.GetResponseAsync(messages, conversationOptions, cancellationToken).ConfigureAwait(false);
}
///
public async override IAsyncEnumerable GetStreamingResponseAsync(IEnumerable messages, ChatOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default)
{
- var conversation = await this.GetConversationAsync(messages, options, cancellationToken).ConfigureAwait(false);
- this.SetChatOptionsFactory(options, conversation);
+ var conversation = await this.GetOrCreateConversationAsync(messages, options, cancellationToken).ConfigureAwait(false);
+ this.GetConversationEnabledChatOptions(options, conversation);
await foreach (var chunk in base.GetStreamingResponseAsync(messages, options, cancellationToken).ConfigureAwait(false))
{
@@ -72,52 +72,18 @@ public async override IAsyncEnumerable GetStreamingResponseA
}
}
- private async Task GetConversationAsync(IEnumerable messages, ChatOptions? options, CancellationToken cancellationToken)
- {
- AgentConversation conversation;
- if (string.IsNullOrWhiteSpace(options?.ConversationId))
- {
- var creationOptions = new AgentConversationCreationOptions();
- if (creationOptions.Items is List itemsList)
- {
- itemsList.AddRange(messages.AsOpenAIResponseItems());
- }
- else
- {
- foreach (var item in messages.AsOpenAIResponseItems())
- {
- creationOptions.Items.Add(item);
- }
- }
-
- conversation = await this._agentsClient.GetConversationsClient().CreateConversationAsync(creationOptions, cancellationToken: cancellationToken).ConfigureAwait(false);
- }
- else
- {
- conversation = await this._agentsClient.GetConversationsClient().GetConversationAsync(options!.ConversationId, cancellationToken: cancellationToken).ConfigureAwait(false);
- }
+ private async Task GetOrCreateConversationAsync(IEnumerable messages, ChatOptions? options, CancellationToken cancellationToken)
+ => string.IsNullOrWhiteSpace(options?.ConversationId)
+ ? await this._agentsClient.GetConversationsClient().CreateConversationAsync(cancellationToken: cancellationToken).ConfigureAwait(false)
+ : await this._agentsClient.GetConversationsClient().GetConversationAsync(options.ConversationId, cancellationToken: cancellationToken).ConfigureAwait(false);
- return conversation;
- }
-
- private void SetChatOptionsFactory(ChatOptions? chatOptions, AgentConversation agentConversation)
+ private ChatOptions GetConversationEnabledChatOptions(ChatOptions? chatOptions, AgentConversation agentConversation)
{
- chatOptions ??= new ChatOptions();
- chatOptions.RawRepresentationFactory = (client) =>
- {
- var rawRepresentationFactory = chatOptions.RawRepresentationFactory;
- ResponseCreationOptions? responseCreationOptions = null;
-
- if (rawRepresentationFactory is not null)
- {
- responseCreationOptions = rawRepresentationFactory.Invoke(this) as ResponseCreationOptions;
+ var conversationChatOptions = chatOptions is null ? new ChatOptions() : chatOptions.Clone();
- if (responseCreationOptions is null)
- {
- throw new InvalidOperationException("The provided ChatOptions RawRepresentationFactory did not return a valid ResponseCreationOptions instance.");
- }
- }
- else
+ conversationChatOptions.RawRepresentationFactory = (client) =>
+ {
+ if (conversationChatOptions.RawRepresentationFactory?.Invoke(this) is not ResponseCreationOptions responseCreationOptions)
{
responseCreationOptions = new ResponseCreationOptions();
}
@@ -127,5 +93,10 @@ private void SetChatOptionsFactory(ChatOptions? chatOptions, AgentConversation a
return responseCreationOptions;
};
+
+ // Clear out the conversation ID to prevent the inner client from attempting to use it as a PreviousResponseId
+ conversationChatOptions.ConversationId = null;
+
+ return conversationChatOptions;
}
}
From 4a10881154143646142a41617daa3f0ff46734fb Mon Sep 17 00:00:00 2001
From: Roger Barreto <19890735+rogerbarreto@users.noreply.github.com>
Date: Wed, 29 Oct 2025 14:14:34 +0000
Subject: [PATCH 11/16] Adding support for version as name can conflict 409
using the Agents API with same name
---
dotnet/Directory.Packages.props | 2 +-
.../Agent_With_AzureAIAgent/Program.cs | 43 +++-
.../AgentsClientExtensions.cs | 202 +++++++++++++-----
.../AzureAIAgentChatClient.cs | 42 ++--
4 files changed, 216 insertions(+), 73 deletions(-)
diff --git a/dotnet/Directory.Packages.props b/dotnet/Directory.Packages.props
index af52e976a1..3fda281518 100644
--- a/dotnet/Directory.Packages.props
+++ b/dotnet/Directory.Packages.props
@@ -17,7 +17,7 @@
-
+
diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Program.cs b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Program.cs
index aa9f81178b..0bb62aeefb 100644
--- a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Program.cs
+++ b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Program.cs
@@ -2,6 +2,7 @@
// This sample shows how to create and use a simple AI agent with Azure Foundry Agents as the backend.
+using System.ClientModel.Primitives;
using Azure.AI.Agents;
using Azure.Identity;
using Microsoft.Agents.AI;
@@ -9,23 +10,27 @@
var endpoint = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_FOUNDRY_PROJECT_ENDPOINT is not set.");
var deploymentName = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
-const string JokerName = "Joker";
const string JokerInstructions = "You are good at telling jokes.";
+#pragma warning disable CA5399
+
+using var myHttpHandler = new MyHttpHandler();
+using var httpClient = new HttpClient(myHttpHandler);
+
// Get a client to create/retrieve server side agents with.
-var agentsClient = new AgentsClient(new Uri(endpoint), new AzureCliCredential());
+var agentsClient = new AgentsClient(new Uri(endpoint), new AzureCliCredential(), new() { Transport = new HttpClientPipelineTransport(httpClient) });
// Define the agent you want to create.
var agentDefinition = new PromptAgentDefinition(model: deploymentName) { Instructions = JokerInstructions };
// You can create a server side agent with the Azure.AI.Agents SDK.
-var agentRecord = agentsClient.CreateAgent(name: JokerName, definition: agentDefinition).Value;
+var agentRecord = agentsClient.CreateAgentVersion(agentName: "Joker1", definition: agentDefinition).Value;
// You can retrieve an already created server side agent as an AIAgent.
-AIAgent existingAgent = await agentsClient.GetAIAgentAsync(agentRecord.Name);
+AIAgent existingAgent = await agentsClient.GetAIAgentAsync(deploymentName, agentRecord.Name, openAIClientOptions: new() { Transport = new HttpClientPipelineTransport(httpClient) });
// You can also create a server side persistent agent and return it as an AIAgent directly.
-var createdAgent = agentsClient.CreateAIAgent(deploymentName, name: JokerName, instructions: JokerInstructions);
+//var createdAgent = agentsClient.CreateAIAgent(deploymentName, name: "Joker2", instructions: JokerInstructions);
// You can then invoke the agent like any other AIAgent.
AgentThread thread = existingAgent.GetNewThread();
@@ -33,4 +38,30 @@
// Cleanup for sample purposes.
agentsClient.DeleteAgent(agentRecord.Name);
-agentsClient.DeleteAgent(createdAgent.Name);
+//agentsClient.DeleteAgent(createdAgent.Name);
+
+internal sealed class MyHttpHandler : HttpClientHandler
+{
+ protected async override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
+ {
+ var result = await base.SendAsync(request, cancellationToken);
+
+ if (result.StatusCode >= System.Net.HttpStatusCode.BadRequest)
+ {
+ if (request.Content is not null)
+ {
+ var requestUri = request.RequestUri?.ToString();
+ var requestBody = await request.Content.ReadAsStringAsync(cancellationToken);
+
+ Console.WriteLine($"Request URI: {requestUri}");
+ Console.WriteLine($"Request Body: {requestBody}");
+ }
+
+ var responseBody = await result.Content.ReadAsStringAsync(cancellationToken);
+
+ Console.WriteLine($"Response Body: {responseBody}");
+ }
+
+ return result;
+ }
+}
diff --git a/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AgentsClientExtensions.cs b/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AgentsClientExtensions.cs
index 0d10097369..d6b496b8cd 100644
--- a/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AgentsClientExtensions.cs
+++ b/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AgentsClientExtensions.cs
@@ -8,6 +8,7 @@
using Microsoft.Agents.AI.AzureAIAgents;
using Microsoft.Extensions.AI;
using Microsoft.Shared.Diagnostics;
+using OpenAI;
using OpenAI.Responses;
#pragma warning disable MEAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
@@ -25,27 +26,59 @@ public static class AgentsClientExtensions
///
/// The client used to interact with persistent agents. Cannot be .
/// The model to be used by the agent.
- /// The agent record to be converted. Cannot be .
+ /// The agent record to be converted. The latest version will be used. Cannot be .
/// The default to use when interacting with the agent.
/// Provides a way to customize the creation of the underlying used by the agent.
- /// A instance that can be used to perform operations on the persistent agent.
+ /// An optional for configuring the underlying OpenAI client.
+ /// The to monitor for cancellation requests. The default is .
+ /// A instance that can be used to perform operations based on the latest version of the Azure AI Agent.
public static ChatClientAgent GetAIAgent(
this AgentsClient agentsClient,
string model,
AgentRecord agentRecord,
ChatOptions? chatOptions = null,
- Func? clientFactory = null)
+ Func? clientFactory = null,
+ OpenAIClientOptions? openAIClientOptions = null,
+ CancellationToken cancellationToken = default)
+ => GetAIAgent(
+ agentsClient,
+ model,
+ Throw.IfNull(agentRecord).Versions.Latest,
+ chatOptions,
+ clientFactory,
+ openAIClientOptions,
+ cancellationToken);
+
+ ///
+ /// Gets a runnable agent instance from the provided agent record.
+ ///
+ /// The client used to interact with persistent agents. Cannot be .
+ /// The model to be used by the agent.
+ /// The agent version to be converted. Cannot be .
+ /// The default to use when interacting with the agent.
+ /// Provides a way to customize the creation of the underlying used by the agent.
+ /// An optional for configuring the underlying OpenAI client.
+ /// The to monitor for cancellation requests. The default is .
+ /// A instance that can be used to perform operations based on the specified version of the Azure AI Agent.
+ public static ChatClientAgent GetAIAgent(
+ this AgentsClient agentsClient,
+ string model,
+ AgentVersion agentVersion,
+ ChatOptions? chatOptions = null,
+ Func? clientFactory = null,
+ OpenAIClientOptions? openAIClientOptions = null,
+ CancellationToken cancellationToken = default)
{
Throw.IfNull(agentsClient);
Throw.IfNull(model);
- Throw.IfNull(agentRecord);
+ Throw.IfNull(agentVersion);
if (model is null)
{
throw new ArgumentException("When not using a PromptAgent the model needs to be provided in the ChatOptions.ModelId property");
}
- return GetAIAgent(agentsClient, model, agentRecord, new ChatClientAgentOptions() { ChatOptions = chatOptions }, clientFactory);
+ return GetAIAgent(agentsClient, model, agentVersion, new ChatClientAgentOptions() { ChatOptions = chatOptions }, clientFactory, openAIClientOptions, cancellationToken);
}
///
@@ -54,28 +87,32 @@ public static ChatClientAgent GetAIAgent(
/// The to create the with.
/// A for the Azure AI Agent.
/// The model to be used by the agent.
- /// The name of the server side agent to create a for.
+ /// The name of the server side agent to create a for.
/// Options that should apply to all runs of the agent.
/// Provides a way to customize the creation of the underlying used by the agent.
- /// A instance that can be used to perform operations on the persistent agent.
+ /// An optional for configuring the underlying OpenAI client.
+ /// The to monitor for cancellation requests. The default is .
+ /// A instance that can be used to perform operations based on the latest version of the named Azure AI Agent.
public static ChatClientAgent GetAIAgent(
this AgentsClient agentsClient,
string model,
- string agentName,
+ string name,
ChatOptions? chatOptions = null,
- Func? clientFactory = null)
+ Func? clientFactory = null,
+ OpenAIClientOptions? openAIClientOptions = null,
+ CancellationToken cancellationToken = default)
{
Throw.IfNull(agentsClient);
Throw.IfNullOrWhitespace(model);
- Throw.IfNullOrWhitespace(agentName);
+ Throw.IfNullOrWhitespace(name);
- var agentRecord = agentsClient.GetAgent(agentName);
+ var agentRecord = agentsClient.GetAgent(name, cancellationToken);
if (agentRecord is null)
{
- throw new InvalidOperationException($"Agent with name '{agentName}' not found.");
+ throw new InvalidOperationException($"Agent with name '{name}' not found.");
}
- return GetAIAgent(agentsClient, model, agentRecord, chatOptions, clientFactory);
+ return GetAIAgent(agentsClient, model, agentRecord, chatOptions, clientFactory, openAIClientOptions, cancellationToken);
}
///
@@ -84,30 +121,32 @@ public static ChatClientAgent GetAIAgent(
/// The to create the with.
/// A for the persistent agent.
/// The model to be used by the agent.
- /// The name of the server side agent to create a for.
+ /// The name of the server side agent to create a for.
/// Options that should apply to all runs of the agent.
/// Provides a way to customize the creation of the underlying used by the agent.
+ /// An optional for configuring the underlying OpenAI client.
/// The to monitor for cancellation requests. The default is .
- /// A instance that can be used to perform operations on the persistent agent.
+ /// A instance that can be used to perform operations based on the latest version of the named Azure AI Agent.
public static async Task GetAIAgentAsync(
this AgentsClient agentsClient,
string model,
- string agentName,
+ string name,
ChatOptions? chatOptions = null,
Func? clientFactory = null,
+ OpenAIClientOptions? openAIClientOptions = null,
CancellationToken cancellationToken = default)
{
Throw.IfNull(agentsClient);
Throw.IfNullOrWhitespace(model);
- Throw.IfNullOrWhitespace(agentName);
+ Throw.IfNullOrWhitespace(name);
- var agentRecord = await agentsClient.GetAgentAsync(agentName, cancellationToken).ConfigureAwait(false);
+ var agentRecord = await agentsClient.GetAgentAsync(name, cancellationToken).ConfigureAwait(false);
if (agentRecord is null)
{
- throw new InvalidOperationException($"Agent with name '{agentName}' not found.");
+ throw new InvalidOperationException($"Agent with name '{name}' not found.");
}
- return GetAIAgent(agentsClient, model, agentRecord, chatOptions, clientFactory);
+ return GetAIAgent(agentsClient, model, agentRecord, chatOptions, clientFactory, openAIClientOptions, cancellationToken);
}
///
@@ -115,19 +154,57 @@ public static async Task GetAIAgentAsync(
///
/// The client used to interact with persistent agents. Cannot be .
/// The model to be used by the agent.
- /// The persistent agent metadata to be converted. Cannot be .
+ /// The agent record to be converted. The latest version will be used. Cannot be .
+ /// Full set of options to configure the agent.
+ /// Provides a way to customize the creation of the underlying used by the agent.
+ /// An optional for configuring the underlying OpenAI client.
+ /// The to monitor for cancellation requests. The default is .
+ /// A instance that can be used to perform operations based on the latest version of the Azure AI Agent record.
+ /// Thrown when , , or is .
+ public static ChatClientAgent GetAIAgent(
+ this AgentsClient agentsClient,
+ string model,
+ AgentRecord agentRecord,
+ ChatClientAgentOptions? options = null,
+ Func? clientFactory = null,
+ OpenAIClientOptions? openAIClientOptions = null,
+ CancellationToken cancellationToken = default)
+ => GetAIAgent(
+ agentsClient,
+ model,
+ agentRecord.Versions.Latest,
+ options ?? new ChatClientAgentOptions(),
+ clientFactory,
+ openAIClientOptions,
+ cancellationToken);
+
+ ///
+ /// Gets a runnable agent instance from a containing metadata about an Azure AI Agent.
+ ///
+ /// The client used to interact with persistent agents. Cannot be .
+ /// The model to be used by the agent.
+ /// The agent version to be converted. Cannot be .
/// Full set of options to configure the agent.
/// Provides a way to customize the creation of the underlying used by the agent.
- /// A instance that can be used to perform operations on the persistent agent.
+ /// An optional for configuring the underlying OpenAI client.
+ /// The to monitor for cancellation requests. The default is .
+ /// A instance that can be used to perform operations based on the provided version of the Azure AI Agent.
/// Thrown when , , or is .
- public static ChatClientAgent GetAIAgent(this AgentsClient agentsClient, string model, AgentRecord agentRecord, ChatClientAgentOptions? options = null, Func? clientFactory = null)
+ public static ChatClientAgent GetAIAgent(
+ this AgentsClient agentsClient,
+ string model,
+ AgentVersion agentVersion,
+ ChatClientAgentOptions? options = null,
+ Func? clientFactory = null,
+ OpenAIClientOptions? openAIClientOptions = null,
+ CancellationToken cancellationToken = default)
{
Throw.IfNull(agentsClient);
Throw.IfNullOrWhitespace(model);
- Throw.IfNull(agentRecord);
+ Throw.IfNull(agentVersion);
Throw.IfNull(options);
- IChatClient chatClient = new AzureAIAgentChatClient(agentsClient, agentRecord, model);
+ IChatClient chatClient = new AzureAIAgentChatClient(agentsClient, agentVersion, model, openAIClientOptions);
if (clientFactory is not null)
{
@@ -137,13 +214,13 @@ public static ChatClientAgent GetAIAgent(this AgentsClient agentsClient, string
ChatClientAgentOptions? agentOptions;
// If options are null, populate from agentRecord definition
- var version = agentRecord.Versions.Latest;
+ var version = agentVersion;
if (options is null)
{
agentOptions = new();
- agentOptions.Id = agentRecord.Id;
- agentOptions.Name = agentRecord.Name;
+ agentOptions.Id = GetAgentId(agentVersion);
+ agentOptions.Name = agentVersion.Name;
agentOptions.Description = version.Description;
@@ -164,8 +241,8 @@ public static ChatClientAgent GetAIAgent(this AgentsClient agentsClient, string
// When agent options it is used when available otherwise fallback to the agent definition used for the agent record.
agentOptions = new ChatClientAgentOptions()
{
- Id = options.Id ?? agentRecord.Id,
- Name = options.Name ?? agentRecord.Name,
+ Id = options.Id ?? GetAgentId(agentVersion),
+ Name = options.Name ?? agentVersion.Name,
Description = options.Description ?? version.Description,
Instructions = options.Instructions ?? options.ChatOptions?.Instructions ?? (version.Definition as PromptAgentDefinition)?.Instructions,
ChatOptions = options.ChatOptions,
@@ -195,27 +272,29 @@ public static ChatClientAgent GetAIAgent(this AgentsClient agentsClient, string
///
/// The to create the with.
/// The model to be used by the agent.
- /// The ID of the server side agent to create a for.
+ /// The ID of the server side agent to create a for.
/// Full set of options to configure the agent.
/// Provides a way to customize the creation of the underlying used by the agent.
+ /// An optional for configuring the underlying OpenAI client.
/// The to monitor for cancellation requests. The default is .
- /// A instance that can be used to perform operations on the persistent agent.
+ /// A instance that can be used to perform operations based on the latest version of named the Azure AI Agent.
/// Thrown when or is .
- /// Thrown when is empty or whitespace.
+ /// Thrown when is empty or whitespace.
public static async Task GetAIAgentAsync(
this AgentsClient agentsClient,
string model,
- string agentName,
+ string name,
ChatClientAgentOptions options,
Func? clientFactory = null,
+ OpenAIClientOptions? openAIClientOptions = null,
CancellationToken cancellationToken = default)
{
Throw.IfNull(agentsClient);
- Throw.IfNullOrWhitespace(agentName);
+ Throw.IfNullOrWhitespace(name);
Throw.IfNull(options);
- var agentRecord = await agentsClient.GetAgentAsync(agentName, cancellationToken).ConfigureAwait(false);
- return agentsClient.GetAIAgent(model, agentRecord, options, clientFactory);
+ var agentRecord = await agentsClient.GetAgentAsync(name, cancellationToken).ConfigureAwait(false);
+ return agentsClient.GetAIAgent(model, agentRecord, options, clientFactory, openAIClientOptions, cancellationToken);
}
///
@@ -234,6 +313,7 @@ public static async Task GetAIAgentAsync(
/// The structured inputs for the agent.
/// The metadata for the agent.
/// A factory function to customize the creation of the chat client used by the agent.
+ /// An optional for configuring the underlying OpenAI client.
/// The to monitor for cancellation requests. The default is .
/// A instance that can be used to perform operations on the newly created agent.
public static ChatClientAgent CreateAIAgent(
@@ -250,16 +330,17 @@ public static ChatClientAgent CreateAIAgent(
IDictionary? structuredInputs = null,
IReadOnlyDictionary? metadata = null,
Func? clientFactory = null,
+ OpenAIClientOptions? openAIClientOptions = null,
CancellationToken cancellationToken = default)
{
Throw.IfNull(agentsClient);
Throw.IfNull(model);
- var (promptAgentDefinition, creationOptions) = CreatePromptAgentDefinitionAndOptions(
+ var (promptAgentDefinition, versionCreationOptions) = CreatePromptAgentDefinitionAndOptions(
model, instructions, temperature, topP, raiConfig, reasoningOptions, textOptions, tools, structuredInputs, metadata);
- AgentRecord agentRecord = agentsClient.CreateAgent(name, promptAgentDefinition, creationOptions, cancellationToken);
- IChatClient chatClient = new AzureAIAgentChatClient(agentsClient, agentRecord, model);
+ AgentVersion agentVersion = agentsClient.CreateAgentVersion(name, promptAgentDefinition, versionCreationOptions, cancellationToken);
+ IChatClient chatClient = new AzureAIAgentChatClient(agentsClient, agentVersion, model, openAIClientOptions);
if (clientFactory is not null)
{
@@ -278,6 +359,7 @@ public static ChatClientAgent CreateAIAgent(
/// The name for the agent.
/// Settings that control the creation of the agent.
/// A factory function to customize the creation of the chat client used by the agent.
+ /// An optional for configuring the underlying OpenAI client.
/// A token to monitor for cancellation requests.
/// A instance that can be used to perform operations on the newly created agent.
/// Thrown if neither the 'model' parameter nor a model in the agent definition is provided.
@@ -288,6 +370,7 @@ public static ChatClientAgent CreateAIAgent(
string? name = null,
AgentCreationOptions? creationOptions = null,
Func? clientFactory = null,
+ OpenAIClientOptions? openAIClientOptions = null,
CancellationToken cancellationToken = default)
{
Throw.IfNull(agentsClient);
@@ -300,7 +383,7 @@ public static ChatClientAgent CreateAIAgent(
}
AgentRecord agentRecord = agentsClient.CreateAgent(name, agentDefinition, creationOptions, cancellationToken);
- IChatClient chatClient = new AzureAIAgentChatClient(agentsClient, agentRecord, model);
+ IChatClient chatClient = new AzureAIAgentChatClient(agentsClient, agentRecord, model, openAIClientOptions);
if (clientFactory is not null)
{
@@ -317,13 +400,17 @@ public static ChatClientAgent CreateAIAgent(
/// The name of the model to use for the agent. If not specified, the model must be provided as part of the agent definition.
/// The options for creating the agent.
/// A factory function to customize the creation of the chat client used by the agent.
+ /// An optional for configuring the underlying OpenAI client.
+ /// A to cancel the operation if needed.
/// A instance that can be used to perform operations on the newly created agent.
/// Thrown if neither the 'model' parameter nor a model in the agent definition is provided.
public static ChatClientAgent CreateAIAgent(
this AgentsClient agentsClient,
string model,
ChatClientAgentOptions options,
- Func? clientFactory = null)
+ Func? clientFactory = null,
+ OpenAIClientOptions? openAIClientOptions = null,
+ CancellationToken cancellationToken = default)
{
Throw.IfNull(agentsClient);
Throw.IfNullOrWhitespace(model);
@@ -343,8 +430,8 @@ public static ChatClientAgent CreateAIAgent(
}
}
- AgentRecord agentRecord = agentsClient.CreateAgent(options.Name, promptAgentDefinition, new() { Description = options.Description });
- IChatClient chatClient = new AzureAIAgentChatClient(agentsClient, agentRecord, model);
+ AgentVersion agentVersion = agentsClient.CreateAgentVersion(options.Name, promptAgentDefinition, new() { Description = options.Description }, cancellationToken);
+ IChatClient chatClient = new AzureAIAgentChatClient(agentsClient, agentVersion, model, openAIClientOptions);
if (clientFactory is not null)
{
@@ -364,16 +451,18 @@ public static ChatClientAgent CreateAIAgent(
/// The name for the agent.
/// Settings that control the creation of the agent.
/// A factory function to customize the creation of the chat client used by the agent.
+ /// An optional for configuring the underlying OpenAI client.
/// A token to monitor for cancellation requests.
- /// A task that represents the asynchronous operation. The task result contains the created AI agent.
+ /// A instance that can be used to perform operations on the newly created agent.
/// Thrown if neither the 'model' parameter nor a model in the agent definition is provided.
- public static async Task CreateAIAgentAsync(
+ public static async Task CreateAIAgentAsync(
this AgentsClient agentsClient,
AgentDefinition agentDefinition,
string? model = null,
string? name = null,
AgentCreationOptions? creationOptions = null,
Func? clientFactory = null,
+ OpenAIClientOptions? openAIClientOptions = null,
CancellationToken cancellationToken = default)
{
Throw.IfNull(agentsClient);
@@ -386,7 +475,7 @@ public static async Task CreateAIAgentAsync(
}
AgentRecord agentRecord = await agentsClient.CreateAgentAsync(name, agentDefinition, creationOptions, cancellationToken).ConfigureAwait(false);
- IChatClient chatClient = new AzureAIAgentChatClient(agentsClient, agentRecord, model);
+ IChatClient chatClient = new AzureAIAgentChatClient(agentsClient, agentRecord, model, openAIClientOptions);
if (clientFactory is not null)
{
@@ -412,9 +501,10 @@ public static async Task CreateAIAgentAsync(
/// The structured inputs for the agent.
/// The metadata for the agent.
/// A factory function to customize the creation of the underlying used by the agent.
+ /// An optional for configuring the underlying OpenAI client.
/// The to monitor for cancellation requests.
- /// A task that represents the asynchronous operation. The task result contains a instance that can be used to perform operations on the newly created agent.
- public static async Task CreateAIAgentAsync(
+ /// A instance that can be used to perform operations on the newly created agent.
+ public static async Task CreateAIAgentAsync(
this AgentsClient agentsClient,
string model,
string? name = null,
@@ -428,16 +518,17 @@ public static async Task CreateAIAgentAsync(
IDictionary? structuredInputs = null,
IReadOnlyDictionary? metadata = null,
Func? clientFactory = null,
+ OpenAIClientOptions? openAIClientOptions = null,
CancellationToken cancellationToken = default)
{
Throw.IfNull(agentsClient);
Throw.IfNull(model);
- var (promptAgentDefinition, creationOptions) = CreatePromptAgentDefinitionAndOptions(
+ var (promptAgentDefinition, versionCreationOptions) = CreatePromptAgentDefinitionAndOptions(
model, instructions, temperature, topP, raiConfig, reasoningOptions, textOptions, tools, structuredInputs, metadata);
- AgentRecord agentRecord = await agentsClient.CreateAgentAsync(name, promptAgentDefinition, creationOptions, cancellationToken).ConfigureAwait(false);
- IChatClient chatClient = new AzureAIAgentChatClient(agentsClient, agentRecord, model);
+ AgentVersion agentVersion = await agentsClient.CreateAgentVersionAsync(name, promptAgentDefinition, versionCreationOptions, cancellationToken).ConfigureAwait(false);
+ IChatClient chatClient = new AzureAIAgentChatClient(agentsClient, agentVersion, model, openAIClientOptions);
if (clientFactory is not null)
{
@@ -449,7 +540,7 @@ public static async Task CreateAIAgentAsync(
#region Private
- private static (PromptAgentDefinition, AgentCreationOptions?) CreatePromptAgentDefinitionAndOptions(
+ private static (PromptAgentDefinition, AgentVersionCreationOptions?) CreatePromptAgentDefinitionAndOptions(
string model,
string? instructions,
float? temperature,
@@ -472,7 +563,7 @@ private static (PromptAgentDefinition, AgentCreationOptions?) CreatePromptAgentD
TextOptions = textOptions,
};
- AgentCreationOptions? creationOptions = null;
+ AgentVersionCreationOptions? creationOptions = null;
if (metadata is not null)
{
creationOptions = new();
@@ -508,6 +599,9 @@ private static (PromptAgentDefinition, AgentCreationOptions?) CreatePromptAgentD
return (promptAgentDefinition, creationOptions);
}
+ private static string GetAgentId(AgentVersion agentVersion)
+ => $"{agentVersion.Name}:{agentVersion.Id}";
+
#endregion
#region Polyfill from MEAI.OpenAI for AITool -> ResponseTool conversion
diff --git a/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AzureAIAgentChatClient.cs b/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AzureAIAgentChatClient.cs
index e2b6dbd583..cf736351d0 100644
--- a/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AzureAIAgentChatClient.cs
+++ b/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AzureAIAgentChatClient.cs
@@ -1,9 +1,11 @@
// Copyright (c) Microsoft. All rights reserved.
+using System.ClientModel;
using System.Runtime.CompilerServices;
using Azure.AI.Agents;
using Microsoft.Extensions.AI;
using Microsoft.Shared.Diagnostics;
+using OpenAI;
using OpenAI.Responses;
#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.
@@ -18,7 +20,7 @@ internal sealed class AzureAIAgentChatClient : DelegatingChatClient
{
private readonly ChatClientMetadata? _metadata;
private readonly AgentsClient _agentsClient;
- private readonly AgentRecord _agentRecord;
+ private readonly AgentVersion _agentVersion;
///
/// Initializes a new instance of the class.
@@ -26,14 +28,20 @@ internal sealed class AzureAIAgentChatClient : DelegatingChatClient
/// An instance of to interact with Azure AI Agents services.
/// An instance of representing the specific agent to use.
/// The AI model to use for the chat client.
+ /// An optional for configuring the underlying OpenAI client.
///
/// The provided should be decorated with a for proper functionality.
///
- internal AzureAIAgentChatClient(AgentsClient agentsClient, AgentRecord agentRecord, string model)
- : base(agentsClient.GetOpenAIClient().GetOpenAIResponseClient(Throw.IfNull(model)).AsIChatClient())
+ internal AzureAIAgentChatClient(AgentsClient agentsClient, AgentRecord agentRecord, string model, OpenAIClientOptions? openAIClientOptions = null)
+ : this(agentsClient, Throw.IfNull(agentRecord).Versions.Latest, model, openAIClientOptions)
+ {
+ }
+
+ internal AzureAIAgentChatClient(AgentsClient agentsClient, AgentVersion agentVersion, string model, OpenAIClientOptions? openAIClientOptions = null)
+ : base(agentsClient.GetOpenAIClient(openAIClientOptions).GetOpenAIResponseClient(model).AsIChatClient())
{
this._agentsClient = Throw.IfNull(agentsClient);
- this._agentRecord = Throw.IfNull(agentRecord);
+ this._agentVersion = Throw.IfNull(agentVersion);
this._metadata = new ChatClientMetadata("azure.ai.agents");
}
@@ -45,9 +53,7 @@ internal AzureAIAgentChatClient(AgentsClient agentsClient, AgentRecord agentReco
: (serviceKey is null && serviceType == typeof(AgentsClient))
? this._agentsClient
: (serviceKey is null && serviceType == typeof(AgentVersion))
- ? this._agentRecord.Versions.Latest
- : (serviceKey is null && serviceType == typeof(AgentRecord))
- ? this._agentRecord
+ ? this._agentVersion
: base.GetService(serviceType, serviceKey);
}
@@ -57,7 +63,16 @@ public override async Task GetResponseAsync(IEnumerable
@@ -74,21 +89,22 @@ public async override IAsyncEnumerable GetStreamingResponseA
private async Task GetOrCreateConversationAsync(IEnumerable messages, ChatOptions? options, CancellationToken cancellationToken)
=> string.IsNullOrWhiteSpace(options?.ConversationId)
- ? await this._agentsClient.GetConversationsClient().CreateConversationAsync(cancellationToken: cancellationToken).ConfigureAwait(false)
- : await this._agentsClient.GetConversationsClient().GetConversationAsync(options.ConversationId, cancellationToken: cancellationToken).ConfigureAwait(false);
+ ? await this._agentsClient.GetConversationClient().CreateConversationAsync(cancellationToken: cancellationToken).ConfigureAwait(false)
+ : await this._agentsClient.GetConversationClient().GetConversationAsync(options.ConversationId, cancellationToken: cancellationToken).ConfigureAwait(false);
private ChatOptions GetConversationEnabledChatOptions(ChatOptions? chatOptions, AgentConversation agentConversation)
{
var conversationChatOptions = chatOptions is null ? new ChatOptions() : chatOptions.Clone();
+ var originalFactory = conversationChatOptions.RawRepresentationFactory;
conversationChatOptions.RawRepresentationFactory = (client) =>
{
- if (conversationChatOptions.RawRepresentationFactory?.Invoke(this) is not ResponseCreationOptions responseCreationOptions)
+ if (originalFactory?.Invoke(this) is not ResponseCreationOptions responseCreationOptions)
{
responseCreationOptions = new ResponseCreationOptions();
}
- responseCreationOptions.SetAgentReference(this._agentRecord.Name);
+ responseCreationOptions.SetAgentReference(this._agentVersion.Name);
responseCreationOptions.SetConversationReference(agentConversation);
return responseCreationOptions;
@@ -96,6 +112,8 @@ private ChatOptions GetConversationEnabledChatOptions(ChatOptions? chatOptions,
// Clear out the conversation ID to prevent the inner client from attempting to use it as a PreviousResponseId
conversationChatOptions.ConversationId = null;
+ // Clear out any instructions to avoid conflicts with the agent's instructions
+ conversationChatOptions.Instructions = null;
return conversationChatOptions;
}
From f28fdef855986634648e88d78d64ab6fa5ce8538 Mon Sep 17 00:00:00 2001
From: Roger Barreto <19890735+rogerbarreto@users.noreply.github.com>
Date: Wed, 29 Oct 2025 15:21:57 +0000
Subject: [PATCH 12/16] Addressing more updates to the extensions
---
.../Agent_With_AzureAIAgent/Program.cs | 7 +-
.../AgentsClientExtensions.cs | 164 ++++++++++++------
2 files changed, 114 insertions(+), 57 deletions(-)
diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Program.cs b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Program.cs
index 0bb62aeefb..8c6c0fb0be 100644
--- a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Program.cs
+++ b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Program.cs
@@ -11,6 +11,7 @@
var deploymentName = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
const string JokerInstructions = "You are good at telling jokes.";
+const string JokerName = "JokerAgent";
#pragma warning disable CA5399
@@ -24,13 +25,13 @@
var agentDefinition = new PromptAgentDefinition(model: deploymentName) { Instructions = JokerInstructions };
// You can create a server side agent with the Azure.AI.Agents SDK.
-var agentRecord = agentsClient.CreateAgentVersion(agentName: "Joker1", definition: agentDefinition).Value;
+var agentRecord = agentsClient.CreateAgentVersion(agentName: JokerName, definition: agentDefinition).Value;
// You can retrieve an already created server side agent as an AIAgent.
AIAgent existingAgent = await agentsClient.GetAIAgentAsync(deploymentName, agentRecord.Name, openAIClientOptions: new() { Transport = new HttpClientPipelineTransport(httpClient) });
// You can also create a server side persistent agent and return it as an AIAgent directly.
-//var createdAgent = agentsClient.CreateAIAgent(deploymentName, name: "Joker2", instructions: JokerInstructions);
+var createdAgent = agentsClient.CreateAIAgent(deploymentName, name: JokerName, instructions: JokerInstructions);
// You can then invoke the agent like any other AIAgent.
AgentThread thread = existingAgent.GetNewThread();
@@ -38,7 +39,7 @@
// Cleanup for sample purposes.
agentsClient.DeleteAgent(agentRecord.Name);
-//agentsClient.DeleteAgent(createdAgent.Name);
+agentsClient.DeleteAgent(createdAgent.Name);
internal sealed class MyHttpHandler : HttpClientHandler
{
diff --git a/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AgentsClientExtensions.cs b/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AgentsClientExtensions.cs
index d6b496b8cd..03d3d42da6 100644
--- a/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AgentsClientExtensions.cs
+++ b/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AgentsClientExtensions.cs
@@ -93,6 +93,7 @@ public static ChatClientAgent GetAIAgent(
/// An optional for configuring the underlying OpenAI client.
/// The to monitor for cancellation requests. The default is .
/// A instance that can be used to perform operations based on the latest version of the named Azure AI Agent.
+ /// The agent with the specified name was not found.
public static ChatClientAgent GetAIAgent(
this AgentsClient agentsClient,
string model,
@@ -106,11 +107,8 @@ public static ChatClientAgent GetAIAgent(
Throw.IfNullOrWhitespace(model);
Throw.IfNullOrWhitespace(name);
- var agentRecord = agentsClient.GetAgent(name, cancellationToken);
- if (agentRecord is null)
- {
- throw new InvalidOperationException($"Agent with name '{name}' not found.");
- }
+ var agentRecord = agentsClient.GetAgent(name, cancellationToken)
+ ?? throw new InvalidOperationException($"Agent with name '{name}' not found.");
return GetAIAgent(agentsClient, model, agentRecord, chatOptions, clientFactory, openAIClientOptions, cancellationToken);
}
@@ -127,6 +125,7 @@ public static ChatClientAgent GetAIAgent(
/// An optional for configuring the underlying OpenAI client.
/// The to monitor for cancellation requests. The default is .
/// A instance that can be used to perform operations based on the latest version of the named Azure AI Agent.
+ /// The agent with the specified name was not found.
public static async Task GetAIAgentAsync(
this AgentsClient agentsClient,
string model,
@@ -189,7 +188,7 @@ public static ChatClientAgent GetAIAgent(
/// An optional for configuring the underlying OpenAI client.
/// The to monitor for cancellation requests. The default is .
/// A instance that can be used to perform operations based on the provided version of the Azure AI Agent.
- /// Thrown when , , or is .
+ /// Thrown when , , or is .
public static ChatClientAgent GetAIAgent(
this AgentsClient agentsClient,
string model,
@@ -304,6 +303,7 @@ public static async Task GetAIAgentAsync(
/// The model to be used by the agent.
/// The name of the agent.
/// The instructions for the agent.
+ /// The description for the agent.
/// The tools to be used by the agent.
/// The temperature setting for the agent.
/// The top-p setting for the agent.
@@ -319,9 +319,10 @@ public static async Task GetAIAgentAsync(
public static ChatClientAgent CreateAIAgent(
this AgentsClient agentsClient,
string model,
- string? name = null,
+ string name,
string? instructions = null,
- IEnumerable? tools = null,
+ string? description = null,
+ IList? tools = null,
float? temperature = null,
float? topP = null,
RaiConfig? raiConfig = null,
@@ -336,8 +337,19 @@ public static ChatClientAgent CreateAIAgent(
Throw.IfNull(agentsClient);
Throw.IfNull(model);
- var (promptAgentDefinition, versionCreationOptions) = CreatePromptAgentDefinitionAndOptions(
- model, instructions, temperature, topP, raiConfig, reasoningOptions, textOptions, tools, structuredInputs, metadata);
+ var (promptAgentDefinition, versionCreationOptions, chatClientAgentOptions) = CreatePromptAgentDefinitionAndOptions(
+ name,
+ model,
+ instructions,
+ description,
+ temperature,
+ topP,
+ raiConfig,
+ reasoningOptions,
+ textOptions,
+ tools,
+ structuredInputs,
+ metadata);
AgentVersion agentVersion = agentsClient.CreateAgentVersion(name, promptAgentDefinition, versionCreationOptions, cancellationToken);
IChatClient chatClient = new AzureAIAgentChatClient(agentsClient, agentVersion, model, openAIClientOptions);
@@ -347,16 +359,16 @@ public static ChatClientAgent CreateAIAgent(
chatClient = clientFactory(chatClient);
}
- return new ChatClientAgent(chatClient);
+ return new ChatClientAgent(chatClient, chatClientAgentOptions);
}
///
/// Creates a new AI agent using the specified agent definition and optional configuration parameters.
///
/// The client used to manage and interact with AI agents.
- /// The definition that specifies the configuration and behavior of the agent to create.
- /// The name of the model to use for the agent. If not specified, the model must be provided as part of the agent definition.
/// The name for the agent.
+ /// The definition that specifies the configuration and behavior of the agent to create.
+ /// The name of the model to use for the agent. Model must be provided either directly or as part of a specialization property.
/// Settings that control the creation of the agent.
/// A factory function to customize the creation of the chat client used by the agent.
/// An optional for configuring the underlying OpenAI client.
@@ -365,9 +377,9 @@ public static ChatClientAgent CreateAIAgent(
/// Thrown if neither the 'model' parameter nor a model in the agent definition is provided.
public static ChatClientAgent CreateAIAgent(
this AgentsClient agentsClient,
+ string name,
AgentDefinition agentDefinition,
string? model = null,
- string? name = null,
AgentCreationOptions? creationOptions = null,
Func? clientFactory = null,
OpenAIClientOptions? openAIClientOptions = null,
@@ -416,21 +428,26 @@ public static ChatClientAgent CreateAIAgent(
Throw.IfNullOrWhitespace(model);
Throw.IfNull(options);
- PromptAgentDefinition promptAgentDefinition = new(model)
+ if (string.IsNullOrWhiteSpace(options.Name))
{
- Model = model,
- Instructions = options.Instructions,
- };
+ throw new ArgumentNullException(nameof(options), "Agent name must be provided in the options property");
+ }
+
+ var (agentDefinition, versionCreationOptions, chatClientAgentOptions) = CreatePromptAgentDefinitionAndOptions(
+ options.Name,
+ model,
+ options.Instructions,
+ options.Description);
if (options.ChatOptions?.Tools is { Count: > 0 })
{
foreach (var tool in options.ChatOptions.Tools)
{
- promptAgentDefinition.Tools.Add(ToResponseTool(tool, options.ChatOptions));
+ agentDefinition.Tools.Add(ToResponseTool(tool, options.ChatOptions));
}
}
- AgentVersion agentVersion = agentsClient.CreateAgentVersion(options.Name, promptAgentDefinition, new() { Description = options.Description }, cancellationToken);
+ AgentVersion agentVersion = agentsClient.CreateAgentVersion(options.Name, agentDefinition, versionCreationOptions, cancellationToken);
IChatClient chatClient = new AzureAIAgentChatClient(agentsClient, agentVersion, model, openAIClientOptions);
if (clientFactory is not null)
@@ -438,7 +455,9 @@ public static ChatClientAgent CreateAIAgent(
chatClient = clientFactory(chatClient);
}
- return new ChatClientAgent(chatClient);
+ chatClientAgentOptions.Id = GetAgentId(agentVersion);
+
+ return new ChatClientAgent(chatClient, chatClientAgentOptions);
}
///
@@ -449,7 +468,7 @@ public static ChatClientAgent CreateAIAgent(
/// The definition that specifies the configuration and behavior of the agent to create.
/// The name of the model to use for the agent. If not specified, the model must be provided as part of the agent definition.
/// The name for the agent.
- /// Settings that control the creation of the agent.
+ /// Settings that control the creation of the agent.
/// A factory function to customize the creation of the chat client used by the agent.
/// An optional for configuring the underlying OpenAI client.
/// A token to monitor for cancellation requests.
@@ -460,7 +479,7 @@ public static async Task CreateAIAgentAsync(
AgentDefinition agentDefinition,
string? model = null,
string? name = null,
- AgentCreationOptions? creationOptions = null,
+ AgentVersionCreationOptions? agentVersionCreationOptions = null,
Func? clientFactory = null,
OpenAIClientOptions? openAIClientOptions = null,
CancellationToken cancellationToken = default)
@@ -474,15 +493,31 @@ public static async Task CreateAIAgentAsync(
throw new ArgumentException("Model must be provided either directly or as part of a PromptAgentDefinition specialization.", nameof(model));
}
- AgentRecord agentRecord = await agentsClient.CreateAgentAsync(name, agentDefinition, creationOptions, cancellationToken).ConfigureAwait(false);
- IChatClient chatClient = new AzureAIAgentChatClient(agentsClient, agentRecord, model, openAIClientOptions);
+ AgentVersion agentVersion = await agentsClient.CreateAgentVersionAsync(name, agentDefinition, agentVersionCreationOptions, cancellationToken).ConfigureAwait(false);
+ IChatClient chatClient = new AzureAIAgentChatClient(agentsClient, agentVersion, model, openAIClientOptions);
if (clientFactory is not null)
{
chatClient = clientFactory(chatClient);
}
- return new ChatClientAgent(chatClient);
+ List? aiTools = null;
+ if ((agentDefinition as PromptAgentDefinition)?.Tools is { Count: > 0 } definitionTools)
+ {
+ aiTools = definitionTools.Select(rt => rt.AsAITool()).ToList();
+ }
+
+ return new ChatClientAgent(chatClient, new ChatClientAgentOptions()
+ {
+ Id = GetAgentId(agentVersion),
+ Name = name,
+ Description = agentVersionCreationOptions?.Description,
+ Instructions = (agentDefinition as PromptAgentDefinition)?.Instructions,
+ ChatOptions = new ChatOptions()
+ {
+ Tools = aiTools
+ }
+ });
}
///
@@ -492,6 +527,7 @@ public static async Task CreateAIAgentAsync(
/// The model to be used by the agent.
/// The name of the agent.
/// The instructions for the agent.
+ /// The description for the agent.
/// The tools to be used by the agent.
/// The temperature setting for the agent.
/// The top-p setting for the agent.
@@ -507,9 +543,10 @@ public static async Task CreateAIAgentAsync(
public static async Task CreateAIAgentAsync(
this AgentsClient agentsClient,
string model,
- string? name = null,
+ string name,
string? instructions = null,
- IEnumerable? tools = null,
+ string? description = null,
+ IList? tools = null,
float? temperature = null,
float? topP = null,
RaiConfig? raiConfig = null,
@@ -524,8 +561,7 @@ public static async Task CreateAIAgentAsync(
Throw.IfNull(agentsClient);
Throw.IfNull(model);
- var (promptAgentDefinition, versionCreationOptions) = CreatePromptAgentDefinitionAndOptions(
- model, instructions, temperature, topP, raiConfig, reasoningOptions, textOptions, tools, structuredInputs, metadata);
+ var (promptAgentDefinition, versionCreationOptions, chatClientAgentOptions) = CreatePromptAgentDefinitionAndOptions(name, model, instructions, description, temperature, topP, raiConfig, reasoningOptions, textOptions, tools, structuredInputs, metadata);
AgentVersion agentVersion = await agentsClient.CreateAgentVersionAsync(name, promptAgentDefinition, versionCreationOptions, cancellationToken).ConfigureAwait(false);
IChatClient chatClient = new AzureAIAgentChatClient(agentsClient, agentVersion, model, openAIClientOptions);
@@ -535,22 +571,26 @@ public static async Task CreateAIAgentAsync(
chatClient = clientFactory(chatClient);
}
- return new ChatClientAgent(chatClient);
+ chatClientAgentOptions.Id = GetAgentId(agentVersion);
+
+ return new ChatClientAgent(chatClient, chatClientAgentOptions);
}
#region Private
- private static (PromptAgentDefinition, AgentVersionCreationOptions?) CreatePromptAgentDefinitionAndOptions(
+ private static (PromptAgentDefinition, AgentVersionCreationOptions?, ChatClientAgentOptions) CreatePromptAgentDefinitionAndOptions(
+ string name,
string model,
string? instructions,
- float? temperature,
- float? topP,
- RaiConfig? raiConfig,
- ResponseReasoningOptions? reasoningOptions,
- ResponseTextOptions? textOptions,
- IEnumerable? tools,
- IDictionary? structuredInputs,
- IReadOnlyDictionary? metadata)
+ string? description,
+ float? temperature = null,
+ float? topP = null,
+ RaiConfig? raiConfig = null,
+ ResponseReasoningOptions? reasoningOptions = null,
+ ResponseTextOptions? textOptions = null,
+ IList? tools = null,
+ IDictionary? structuredInputs = null,
+ IReadOnlyDictionary? metadata = null)
{
PromptAgentDefinition promptAgentDefinition = new(model)
{
@@ -563,28 +603,36 @@ private static (PromptAgentDefinition, AgentVersionCreationOptions?) CreatePromp
TextOptions = textOptions,
};
- AgentVersionCreationOptions? creationOptions = null;
+ var chatOptions = new ChatOptions()
+ {
+ TopP = topP,
+ Temperature = temperature,
+ Instructions = instructions,
+ };
+
+ AgentVersionCreationOptions? versionCreationOptions = null;
if (metadata is not null)
{
- creationOptions = new();
+ versionCreationOptions ??= new();
foreach (var kvp in metadata)
{
- creationOptions.Metadata.Add(kvp.Key, kvp.Value);
+ versionCreationOptions.Metadata.Add(kvp.Key, kvp.Value);
}
}
- if (tools is not null)
+ if (!string.IsNullOrWhiteSpace(description))
{
- if (promptAgentDefinition.Tools is List toolsList)
- {
- toolsList.AddRange(tools);
- }
- else
+ (versionCreationOptions ??= new()).Description = description;
+ }
+
+ if (tools is { Count: > 0 })
+ {
+ chatOptions.Tools ??= [];
+
+ foreach (var tool in tools)
{
- foreach (var tool in tools)
- {
- promptAgentDefinition.Tools.Add(tool);
- }
+ chatOptions.Tools.Add(tool);
+ promptAgentDefinition.Tools.Add(tool);
}
}
@@ -596,7 +644,15 @@ private static (PromptAgentDefinition, AgentVersionCreationOptions?) CreatePromp
}
}
- return (promptAgentDefinition, creationOptions);
+ var chatClientAgentOptions = new ChatClientAgentOptions()
+ {
+ Name = name,
+ Instructions = instructions,
+ Description = description,
+ ChatOptions = chatOptions
+ };
+
+ return (promptAgentDefinition, versionCreationOptions, chatClientAgentOptions);
}
private static string GetAgentId(AgentVersion agentVersion)
From 9b0f064ff57dbe0411f46fd438ffbe1d78c07e24 Mon Sep 17 00:00:00 2001
From: Roger Barreto <19890735+rogerbarreto@users.noreply.github.com>
Date: Wed, 29 Oct 2025 16:14:06 +0000
Subject: [PATCH 13/16] More improvements
---
.../AgentsClientExtensions.cs | 109 ++++++++++--------
1 file changed, 58 insertions(+), 51 deletions(-)
diff --git a/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AgentsClientExtensions.cs b/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AgentsClientExtensions.cs
index 03d3d42da6..e1b204f177 100644
--- a/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AgentsClientExtensions.cs
+++ b/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AgentsClientExtensions.cs
@@ -24,7 +24,7 @@ public static class AgentsClientExtensions
///
/// Gets a runnable agent instance from the provided agent record.
///
- /// The client used to interact with persistent agents. Cannot be .
+ /// The client used to interact with Azure AI Agents. Cannot be .
/// The model to be used by the agent.
/// The agent record to be converted. The latest version will be used. Cannot be .
/// The default to use when interacting with the agent.
@@ -40,26 +40,26 @@ public static ChatClientAgent GetAIAgent(
Func? clientFactory = null,
OpenAIClientOptions? openAIClientOptions = null,
CancellationToken cancellationToken = default)
- => GetAIAgent(
- agentsClient,
- model,
- Throw.IfNull(agentRecord).Versions.Latest,
- chatOptions,
- clientFactory,
- openAIClientOptions,
- cancellationToken);
+ {
+ Throw.IfNull(agentsClient);
+ Throw.IfNullOrWhitespace(model);
+ Throw.IfNull(agentRecord);
+
+ return GetAIAgent(agentsClient, model, agentRecord.Versions.Latest, chatOptions, clientFactory, openAIClientOptions, cancellationToken);
+ }
///
/// Gets a runnable agent instance from the provided agent record.
///
- /// The client used to interact with persistent agents. Cannot be .
- /// The model to be used by the agent.
+ /// The client used to interact with Azure AI Agents. Cannot be .
+ /// The model to be used by the agent. Cannot be .
/// The agent version to be converted. Cannot be .
/// The default to use when interacting with the agent.
/// Provides a way to customize the creation of the underlying used by the agent.
/// An optional for configuring the underlying OpenAI client.
/// The to monitor for cancellation requests. The default is .
/// A instance that can be used to perform operations based on the specified version of the Azure AI Agent.
+ /// Thrown when , , or is .
public static ChatClientAgent GetAIAgent(
this AgentsClient agentsClient,
string model,
@@ -70,29 +70,25 @@ public static ChatClientAgent GetAIAgent(
CancellationToken cancellationToken = default)
{
Throw.IfNull(agentsClient);
- Throw.IfNull(model);
+ Throw.IfNullOrWhitespace(model);
Throw.IfNull(agentVersion);
- if (model is null)
- {
- throw new ArgumentException("When not using a PromptAgent the model needs to be provided in the ChatOptions.ModelId property");
- }
-
return GetAIAgent(agentsClient, model, agentVersion, new ChatClientAgentOptions() { ChatOptions = chatOptions }, clientFactory, openAIClientOptions, cancellationToken);
}
///
/// Retrieves an existing server side agent, wrapped as a using the provided .
///
- /// The to create the with.
- /// A for the Azure AI Agent.
- /// The model to be used by the agent.
- /// The name of the server side agent to create a for.
- /// Options that should apply to all runs of the agent.
+ /// The to create the with. Cannot be .
+ /// The model to be used by the agent. Cannot be or whitespace.
+ /// The name of the server side agent to create a for. Cannot be or whitespace.
+ /// The default to use when interacting with the agent.
/// Provides a way to customize the creation of the underlying used by the agent.
/// An optional for configuring the underlying OpenAI client.
/// The to monitor for cancellation requests. The default is .
/// A instance that can be used to perform operations based on the latest version of the named Azure AI Agent.
+ /// Thrown when , , or is .
+ /// Thrown when or is empty or whitespace, or when the agent with the specified name was not found.
/// The agent with the specified name was not found.
public static ChatClientAgent GetAIAgent(
this AgentsClient agentsClient,
@@ -114,17 +110,18 @@ public static ChatClientAgent GetAIAgent(
}
///
- /// Retrieves an existing server side agent, wrapped as a using the provided .
+ /// Asynchronously retrieves an existing server side agent, wrapped as a using the provided .
///
- /// The to create the with.
- /// A for the persistent agent.
- /// The model to be used by the agent.
- /// The name of the server side agent to create a for.
- /// Options that should apply to all runs of the agent.
+ /// The to create the with. Cannot be .
+ /// The model to be used by the agent. Cannot be or whitespace.
+ /// The name of the server side agent to create a for. Cannot be or whitespace.
+ /// The default to use when interacting with the agent.
/// Provides a way to customize the creation of the underlying used by the agent.
/// An optional for configuring the underlying OpenAI client.
/// The to monitor for cancellation requests. The default is .
/// A instance that can be used to perform operations based on the latest version of the named Azure AI Agent.
+ /// Thrown when , , or is .
+ /// Thrown when or is empty or whitespace, or when the agent with the specified name was not found.
/// The agent with the specified name was not found.
public static async Task GetAIAgentAsync(
this AgentsClient agentsClient,
@@ -139,11 +136,8 @@ public static async Task GetAIAgentAsync(
Throw.IfNullOrWhitespace(model);
Throw.IfNullOrWhitespace(name);
- var agentRecord = await agentsClient.GetAgentAsync(name, cancellationToken).ConfigureAwait(false);
- if (agentRecord is null)
- {
- throw new InvalidOperationException($"Agent with name '{name}' not found.");
- }
+ var agentRecord = await agentsClient.GetAgentAsync(name, cancellationToken).ConfigureAwait(false)
+ ?? throw new InvalidOperationException($"Agent with name '{name}' not found.");
return GetAIAgent(agentsClient, model, agentRecord, chatOptions, clientFactory, openAIClientOptions, cancellationToken);
}
@@ -151,7 +145,7 @@ public static async Task GetAIAgentAsync(
///
/// Gets a runnable agent instance from a containing metadata about an Azure AI Agent.
///
- /// The client used to interact with persistent agents. Cannot be .
+ /// The client used to interact with Azure AI Agents. Cannot be .
/// The model to be used by the agent.
/// The agent record to be converted. The latest version will be used. Cannot be .
/// Full set of options to configure the agent.
@@ -168,19 +162,25 @@ public static ChatClientAgent GetAIAgent(
Func? clientFactory = null,
OpenAIClientOptions? openAIClientOptions = null,
CancellationToken cancellationToken = default)
- => GetAIAgent(
+ {
+ Throw.IfNull(agentsClient);
+ Throw.IfNullOrWhitespace(model);
+ Throw.IfNull(agentRecord);
+
+ return GetAIAgent(
agentsClient,
model,
agentRecord.Versions.Latest,
- options ?? new ChatClientAgentOptions(),
+ options,
clientFactory,
openAIClientOptions,
cancellationToken);
+ }
///
/// Gets a runnable agent instance from a containing metadata about an Azure AI Agent.
///
- /// The client used to interact with persistent agents. Cannot be .
+ /// The client used to interact with Azure AI Agents. Cannot be .
/// The model to be used by the agent.
/// The agent version to be converted. Cannot be .
/// Full set of options to configure the agent.
@@ -292,8 +292,10 @@ public static async Task GetAIAgentAsync(
Throw.IfNullOrWhitespace(name);
Throw.IfNull(options);
- var agentRecord = await agentsClient.GetAgentAsync(name, cancellationToken).ConfigureAwait(false);
- return agentsClient.GetAIAgent(model, agentRecord, options, clientFactory, openAIClientOptions, cancellationToken);
+ var agentRecord = await agentsClient.GetAgentAsync(name, cancellationToken).ConfigureAwait(false)
+ ?? throw new InvalidOperationException($"Agent with name '{name}' not found.");
+
+ return GetAIAgent(agentsClient, model, agentRecord, options, clientFactory, openAIClientOptions, cancellationToken);
}
///
@@ -335,7 +337,8 @@ public static ChatClientAgent CreateAIAgent(
CancellationToken cancellationToken = default)
{
Throw.IfNull(agentsClient);
- Throw.IfNull(model);
+ Throw.IfNullOrWhitespace(model);
+ Throw.IfNullOrWhitespace(name);
var (promptAgentDefinition, versionCreationOptions, chatClientAgentOptions) = CreatePromptAgentDefinitionAndOptions(
name,
@@ -365,15 +368,16 @@ public static ChatClientAgent CreateAIAgent(
///
/// Creates a new AI agent using the specified agent definition and optional configuration parameters.
///
- /// The client used to manage and interact with AI agents.
+ /// The client used to manage and interact with AI agents. Cannot be .
/// The name for the agent.
- /// The definition that specifies the configuration and behavior of the agent to create.
+ /// The definition that specifies the configuration and behavior of the agent to create. Cannot be .
/// The name of the model to use for the agent. Model must be provided either directly or as part of a specialization property.
/// Settings that control the creation of the agent.
/// A factory function to customize the creation of the chat client used by the agent.
/// An optional for configuring the underlying OpenAI client.
/// A token to monitor for cancellation requests.
/// A instance that can be used to perform operations on the newly created agent.
+ /// Thrown when or is .
/// Thrown if neither the 'model' parameter nor a model in the agent definition is provided.
public static ChatClientAgent CreateAIAgent(
this AgentsClient agentsClient,
@@ -386,6 +390,7 @@ public static ChatClientAgent CreateAIAgent(
CancellationToken cancellationToken = default)
{
Throw.IfNull(agentsClient);
+ Throw.IfNullOrWhitespace(name);
Throw.IfNull(agentDefinition);
model ??= (agentDefinition as PromptAgentDefinition)?.Model;
@@ -408,14 +413,15 @@ public static ChatClientAgent CreateAIAgent(
///
/// Creates a new Prompt AI Agent using the provided and options.
///
- /// The client used to manage and interact with AI agents.
- /// The name of the model to use for the agent. If not specified, the model must be provided as part of the agent definition.
- /// The options for creating the agent.
+ /// The client used to manage and interact with AI agents. Cannot be .
+ /// The name of the model to use for the agent. Cannot be or whitespace.
+ /// The options for creating the agent. Cannot be .
/// A factory function to customize the creation of the chat client used by the agent.
/// An optional for configuring the underlying OpenAI client.
/// A to cancel the operation if needed.
/// A instance that can be used to perform operations on the newly created agent.
- /// Thrown if neither the 'model' parameter nor a model in the agent definition is provided.
+ /// Thrown when , , or is .
+ /// Thrown when is empty or whitespace, or when the agent name is not provided in the options.
public static ChatClientAgent CreateAIAgent(
this AgentsClient agentsClient,
string model,
@@ -430,7 +436,7 @@ public static ChatClientAgent CreateAIAgent(
if (string.IsNullOrWhiteSpace(options.Name))
{
- throw new ArgumentNullException(nameof(options), "Agent name must be provided in the options property");
+ throw new ArgumentException("Agent name must be provided in the options.Name property", nameof(options));
}
var (agentDefinition, versionCreationOptions, chatClientAgentOptions) = CreatePromptAgentDefinitionAndOptions(
@@ -464,8 +470,8 @@ public static ChatClientAgent CreateAIAgent(
/// Asynchronously creates a new AI agent using the specified agent definition and optional configuration
/// parameters.
///
- /// The client used to manage and interact with AI agents.
- /// The definition that specifies the configuration and behavior of the agent to create.
+ /// The client used to manage and interact with AI agents. Cannot be .
+ /// The definition that specifies the configuration and behavior of the agent to create. Cannot be .
/// The name of the model to use for the agent. If not specified, the model must be provided as part of the agent definition.
/// The name for the agent.
/// Settings that control the creation of the agent.
@@ -473,6 +479,7 @@ public static ChatClientAgent CreateAIAgent(
/// An optional for configuring the underlying OpenAI client.
/// A token to monitor for cancellation requests.
/// A instance that can be used to perform operations on the newly created agent.
+ /// Thrown when or is .
/// Thrown if neither the 'model' parameter nor a model in the agent definition is provided.
public static async Task CreateAIAgentAsync(
this AgentsClient agentsClient,
@@ -502,7 +509,7 @@ public static async Task CreateAIAgentAsync(
}
List? aiTools = null;
- if ((agentDefinition as PromptAgentDefinition)?.Tools is { Count: > 0 } definitionTools)
+ if (agentDefinition is PromptAgentDefinition { Tools: { Count: > 0 } definitionTools })
{
aiTools = definitionTools.Select(rt => rt.AsAITool()).ToList();
}
@@ -559,7 +566,7 @@ public static async Task CreateAIAgentAsync(
CancellationToken cancellationToken = default)
{
Throw.IfNull(agentsClient);
- Throw.IfNull(model);
+ Throw.IfNullOrWhitespace(model);
var (promptAgentDefinition, versionCreationOptions, chatClientAgentOptions) = CreatePromptAgentDefinitionAndOptions(name, model, instructions, description, temperature, topP, raiConfig, reasoningOptions, textOptions, tools, structuredInputs, metadata);
From dc4dd2e9ce4e0548f9154f80a0d1bda405ae16a3 Mon Sep 17 00:00:00 2001
From: Roger Barreto <19890735+rogerbarreto@users.noreply.github.com>
Date: Wed, 29 Oct 2025 16:22:14 +0000
Subject: [PATCH 14/16] Remove debugging code from sample
---
.../Agent_With_AzureAIAgent.csproj | 1 +
.../Agent_With_AzureAIAgent/Program.cs | 43 +++----------------
2 files changed, 6 insertions(+), 38 deletions(-)
diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_With_AzureAIAgent.csproj b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_With_AzureAIAgent.csproj
index 0aa9f48d64..2d475becd4 100644
--- a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_With_AzureAIAgent.csproj
+++ b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_With_AzureAIAgent.csproj
@@ -6,6 +6,7 @@
enable
enable
+ $(NoWarn);IDE0059
diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Program.cs b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Program.cs
index 8c6c0fb0be..d6eb139ebb 100644
--- a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Program.cs
+++ b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Program.cs
@@ -2,7 +2,6 @@
// This sample shows how to create and use a simple AI agent with Azure Foundry Agents as the backend.
-using System.ClientModel.Primitives;
using Azure.AI.Agents;
using Azure.Identity;
using Microsoft.Agents.AI;
@@ -13,22 +12,17 @@
const string JokerInstructions = "You are good at telling jokes.";
const string JokerName = "JokerAgent";
-#pragma warning disable CA5399
-
-using var myHttpHandler = new MyHttpHandler();
-using var httpClient = new HttpClient(myHttpHandler);
-
// Get a client to create/retrieve server side agents with.
-var agentsClient = new AgentsClient(new Uri(endpoint), new AzureCliCredential(), new() { Transport = new HttpClientPipelineTransport(httpClient) });
+var agentsClient = new AgentsClient(new Uri(endpoint), new AzureCliCredential());
// Define the agent you want to create.
var agentDefinition = new PromptAgentDefinition(model: deploymentName) { Instructions = JokerInstructions };
// You can create a server side agent with the Azure.AI.Agents SDK.
-var agentRecord = agentsClient.CreateAgentVersion(agentName: JokerName, definition: agentDefinition).Value;
+var agentVersion = agentsClient.CreateAgentVersion(agentName: JokerName, definition: agentDefinition).Value;
// You can retrieve an already created server side agent as an AIAgent.
-AIAgent existingAgent = await agentsClient.GetAIAgentAsync(deploymentName, agentRecord.Name, openAIClientOptions: new() { Transport = new HttpClientPipelineTransport(httpClient) });
+AIAgent existingAgent = await agentsClient.GetAIAgentAsync(deploymentName, agentVersion.Name);
// You can also create a server side persistent agent and return it as an AIAgent directly.
var createdAgent = agentsClient.CreateAIAgent(deploymentName, name: JokerName, instructions: JokerInstructions);
@@ -37,32 +31,5 @@
AgentThread thread = existingAgent.GetNewThread();
Console.WriteLine(await existingAgent.RunAsync("Tell me a joke about a pirate.", thread));
-// Cleanup for sample purposes.
-agentsClient.DeleteAgent(agentRecord.Name);
-agentsClient.DeleteAgent(createdAgent.Name);
-
-internal sealed class MyHttpHandler : HttpClientHandler
-{
- protected async override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
- {
- var result = await base.SendAsync(request, cancellationToken);
-
- if (result.StatusCode >= System.Net.HttpStatusCode.BadRequest)
- {
- if (request.Content is not null)
- {
- var requestUri = request.RequestUri?.ToString();
- var requestBody = await request.Content.ReadAsStringAsync(cancellationToken);
-
- Console.WriteLine($"Request URI: {requestUri}");
- Console.WriteLine($"Request Body: {requestBody}");
- }
-
- var responseBody = await result.Content.ReadAsStringAsync(cancellationToken);
-
- Console.WriteLine($"Response Body: {responseBody}");
- }
-
- return result;
- }
-}
+// Cleanup by agent name (removes both agent versions created by existingAgent + createdAgent).
+await agentsClient.DeleteAgentAsync(agentVersion.Name);
From d6ee1b7f4af8a3bbe3e81fb38087f81cbf7833a2 Mon Sep 17 00:00:00 2001
From: Roger Barreto <19890735+rogerbarreto@users.noreply.github.com>
Date: Wed, 29 Oct 2025 17:20:26 +0000
Subject: [PATCH 15/16] Address copilot feedback
---
.../AzureAIAgentChatClient.cs | 16 +++-------------
1 file changed, 3 insertions(+), 13 deletions(-)
diff --git a/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AzureAIAgentChatClient.cs b/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AzureAIAgentChatClient.cs
index cf736351d0..9177a63e06 100644
--- a/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AzureAIAgentChatClient.cs
+++ b/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AzureAIAgentChatClient.cs
@@ -1,6 +1,5 @@
// Copyright (c) Microsoft. All rights reserved.
-using System.ClientModel;
using System.Runtime.CompilerServices;
using Azure.AI.Agents;
using Microsoft.Extensions.AI;
@@ -63,25 +62,16 @@ public override async Task GetResponseAsync(IEnumerable
public async override IAsyncEnumerable GetStreamingResponseAsync(IEnumerable messages, ChatOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default)
{
var conversation = await this.GetOrCreateConversationAsync(messages, options, cancellationToken).ConfigureAwait(false);
- this.GetConversationEnabledChatOptions(options, conversation);
+ var conversationOptions = this.GetConversationEnabledChatOptions(options, conversation);
- await foreach (var chunk in base.GetStreamingResponseAsync(messages, options, cancellationToken).ConfigureAwait(false))
+ await foreach (var chunk in base.GetStreamingResponseAsync(messages, conversationOptions, cancellationToken).ConfigureAwait(false))
{
yield return chunk;
}
From 60b9ad3a683a416b39d5cb4655c9b103c1808a01 Mon Sep 17 00:00:00 2001
From: Roger Barreto <19890735+rogerbarreto@users.noreply.github.com>
Date: Wed, 29 Oct 2025 17:20:48 +0000
Subject: [PATCH 16/16] Apply suggestions from co-pilot code review
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
---
.../AgentsClientExtensions.cs | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AgentsClientExtensions.cs b/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AgentsClientExtensions.cs
index e1b204f177..4795ff1403 100644
--- a/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AgentsClientExtensions.cs
+++ b/dotnet/src/Microsoft.Agents.AI.AzureAIAgents/AgentsClientExtensions.cs
@@ -201,8 +201,6 @@ public static ChatClientAgent GetAIAgent(
Throw.IfNull(agentsClient);
Throw.IfNullOrWhitespace(model);
Throw.IfNull(agentVersion);
- Throw.IfNull(options);
-
IChatClient chatClient = new AzureAIAgentChatClient(agentsClient, agentVersion, model, openAIClientOptions);
if (clientFactory is not null)
@@ -317,7 +315,7 @@ public static async Task GetAIAgentAsync(
/// A factory function to customize the creation of the chat client used by the agent.
/// An optional for configuring the underlying OpenAI client.
/// The to monitor for cancellation requests. The default is .
- /// A instance that can be used to perform operations on the newly created agent.
+ /// A instance that can be used to perform operations on the newly created agent.
public static ChatClientAgent CreateAIAgent(
this AgentsClient agentsClient,
string model,
@@ -376,7 +374,7 @@ public static ChatClientAgent CreateAIAgent(
/// A factory function to customize the creation of the chat client used by the agent.
/// An optional for configuring the underlying OpenAI client.
/// A token to monitor for cancellation requests.
- /// A instance that can be used to perform operations on the newly created agent.
+ /// A instance that can be used to perform operations on the newly created agent.
/// Thrown when or is .
/// Thrown if neither the 'model' parameter nor a model in the agent definition is provided.
public static ChatClientAgent CreateAIAgent(