.NET: Delete sync extension methods for agent (#3291)

* Delete sync extension methods for agent

* Fix comments and obsolete attribute

* Remove more sync methods.

* Fix naming and comments.

* Fix unit tests
This commit is contained in:
westey
2026-01-20 11:24:58 +00:00
committed by GitHub
Unverified
parent 8ee379d344
commit 3ec881509c
22 changed files with 237 additions and 2026 deletions
@@ -26,14 +26,14 @@ var createdAgentVersion = aiProjectClient.Agents.CreateAgentVersion(agentName: J
// agentVersion.Version = <versionNumber>,
// agentVersion.Name = <agentName>
// You can retrieve an AIAgent for an already created server side agent version.
// You can use an AIAgent with an already created server side agent version.
AIAgent existingJokerAgent = aiProjectClient.AsAIAgent(createdAgentVersion);
// You can also create another AIAgent version by providing the same name with a different definition.
AIAgent newJokerAgent = aiProjectClient.CreateAIAgent(name: JokerName, model: deploymentName, instructions: "You are extremely hilarious at telling jokes.");
AIAgent newJokerAgent = await aiProjectClient.CreateAIAgentAsync(name: JokerName, model: deploymentName, instructions: "You are extremely hilarious at telling jokes.");
// You can also get the AIAgent latest version just providing its name.
AIAgent jokerAgentLatest = aiProjectClient.GetAIAgent(name: JokerName);
AIAgent jokerAgentLatest = await aiProjectClient.GetAIAgentAsync(name: JokerName);
var latestAgentVersion = jokerAgentLatest.GetService<AgentVersion>()!;
// The AIAgent version can be accessed via the GetService method.
@@ -28,14 +28,14 @@ AgentVersion createdAgentVersion = aiProjectClient.Agents.CreateAgentVersion(age
// agentVersion.Version = <versionNumber>,
// agentVersion.Name = <agentName>
// You can retrieve an AIAgent for an already created server side agent version.
AIAgent existingJokerAgent = aiProjectClient.GetAIAgent(createdAgentVersion);
// You can use an AIAgent with an already created server side agent version.
AIAgent existingJokerAgent = aiProjectClient.AsAIAgent(createdAgentVersion);
// You can also create another AIAgent version by providing the same name with a different definition/instruction.
AIAgent newJokerAgent = aiProjectClient.CreateAIAgent(name: JokerName, model: deploymentName, instructions: "You are extremely hilarious at telling jokes.");
AIAgent newJokerAgent = await aiProjectClient.CreateAIAgentAsync(name: JokerName, model: deploymentName, instructions: "You are extremely hilarious at telling jokes.");
// You can also get the AIAgent latest version by just providing its name.
AIAgent jokerAgentLatest = aiProjectClient.GetAIAgent(name: JokerName);
AIAgent jokerAgentLatest = await aiProjectClient.GetAIAgentAsync(name: JokerName);
AgentVersion latestAgentVersion = jokerAgentLatest.GetService<AgentVersion>()!;
// The AIAgent version can be accessed via the GetService method.
@@ -23,8 +23,8 @@ AgentVersionCreationOptions options = new(new PromptAgentDefinition(model: deplo
// You can create a server side agent version with the Azure.AI.Agents SDK client below.
AgentVersion agentVersion = aiProjectClient.Agents.CreateAgentVersion(agentName: JokerName, options);
// You can retrieve an AIAgent for a already created server side agent version.
AIAgent jokerAgent = aiProjectClient.GetAIAgent(agentVersion);
// You can use an AIAgent with an already created server side agent version.
AIAgent jokerAgent = aiProjectClient.AsAIAgent(agentVersion);
// Invoke the agent with streaming support.
await foreach (AgentResponseUpdate update in jokerAgent.RunStreamingAsync("Tell me a joke about a pirate."))
@@ -22,8 +22,8 @@ AgentVersionCreationOptions options = new(new PromptAgentDefinition(model: deplo
// Create a server side agent version with the Azure.AI.Agents SDK client.
AgentVersion agentVersion = aiProjectClient.Agents.CreateAgentVersion(agentName: JokerName, options);
// Retrieve an AIAgent for the created server side agent version.
AIAgent jokerAgent = aiProjectClient.GetAIAgent(agentVersion);
// Use an AIAgent with an already created server side agent version.
AIAgent jokerAgent = aiProjectClient.AsAIAgent(agentVersion);
// Invoke the agent with a multi-turn conversation, where the context is preserved in the thread object.
AgentThread thread = await jokerAgent.GetNewThreadAsync();
@@ -44,7 +44,7 @@ Console.WriteLine($"Age: {response.Result.Age}");
Console.WriteLine($"Occupation: {response.Result.Occupation}");
// Create the ChatClientAgent with the specified name, instructions, and expected structured output the agent should produce.
ChatClientAgent agentWithPersonInfo = aiProjectClient.CreateAIAgent(
ChatClientAgent agentWithPersonInfo = await aiProjectClient.CreateAIAgentAsync(
model: deploymentName,
new ChatClientAgentOptions()
{
@@ -32,7 +32,7 @@ using var tracerProvider = tracerProviderBuilder.Build();
AIProjectClient aiProjectClient = new(new Uri(endpoint), new AzureCliCredential());
// Define the agent you want to create. (Prompt Agent in this case)
AIAgent agent = aiProjectClient.CreateAIAgent(name: JokerName, model: deploymentName, instructions: JokerInstructions)
AIAgent agent = (await aiProjectClient.CreateAIAgentAsync(name: JokerName, model: deploymentName, instructions: JokerInstructions))
.AsBuilder()
.UseOpenTelemetry(sourceName: sourceName)
.Build();
@@ -2,6 +2,7 @@
// This sample shows how to use dependency injection to register an AIAgent and use it from a hosted service with a user input chat loop.
using System.ClientModel;
using Azure.AI.Projects;
using Azure.Identity;
using Microsoft.Agents.AI;
@@ -14,16 +15,27 @@ string deploymentName = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJEC
const string JokerInstructions = "You are good at telling jokes.";
const string JokerName = "JokerAgent";
AIProjectClient aIProjectClient = new(new Uri(endpoint), new AzureCliCredential());
// Create a new agent if one doesn't exist already.
ChatClientAgent agent;
try
{
agent = await aIProjectClient.GetAIAgentAsync(name: JokerName);
}
catch (ClientResultException ex) when (ex.Status == 404)
{
agent = await aIProjectClient.CreateAIAgentAsync(name: JokerName, model: deploymentName, instructions: JokerInstructions);
}
// Create a host builder that we will register services with and then run.
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
// Add the agents client to the service collection.
builder.Services.AddSingleton((sp) => new AIProjectClient(new Uri(endpoint), new AzureCliCredential()));
builder.Services.AddSingleton((sp) => aIProjectClient);
// Add the AI agent to the service collection.
builder.Services.AddSingleton<AIAgent>((sp)
=> sp.GetRequiredService<AIProjectClient>()
.CreateAIAgent(name: JokerName, model: deploymentName, instructions: JokerInstructions));
builder.Services.AddSingleton<AIAgent>((sp) => agent);
// Add a sample service that will use the agent to respond to user input.
builder.Services.AddHostedService<SampleService>();
@@ -30,7 +30,7 @@ AIProjectClient aiProjectClient = new(new Uri(endpoint), new AzureCliCredential(
Console.WriteLine($"Creating the agent '{agentName}' ...");
// Define the agent you want to create. (Prompt Agent in this case)
AIAgent agent = aiProjectClient.CreateAIAgent(
AIAgent agent = await aiProjectClient.CreateAIAgentAsync(
name: agentName,
model: deploymentName,
instructions: "You answer questions related to GitHub repositories only.",
@@ -17,7 +17,7 @@ const string VisionName = "VisionAgent";
AIProjectClient aiProjectClient = new(new Uri(endpoint), new AzureCliCredential());
// Define the agent you want to create. (Prompt Agent in this case)
AIAgent agent = aiProjectClient.CreateAIAgent(name: VisionName, model: deploymentName, instructions: VisionInstructions);
AIAgent agent = await aiProjectClient.CreateAIAgentAsync(name: VisionName, model: deploymentName, instructions: VisionInstructions);
ChatMessage message = new(ChatRole.User, [
new TextContent("What do you see in this image?"),
@@ -25,14 +25,14 @@ AIProjectClient aiProjectClient = new(new Uri(endpoint), new AzureCliCredential(
// Create the weather agent with function tools.
AITool weatherTool = AIFunctionFactory.Create(GetWeather);
AIAgent weatherAgent = aiProjectClient.CreateAIAgent(
AIAgent weatherAgent = await aiProjectClient.CreateAIAgentAsync(
name: WeatherName,
model: deploymentName,
instructions: WeatherInstructions,
tools: [weatherTool]);
// Create the main agent, and provide the weather agent as a function tool.
AIAgent agent = aiProjectClient.CreateAIAgent(
AIAgent agent = await aiProjectClient.CreateAIAgentAsync(
name: MainName,
model: deploymentName,
instructions: MainInstructions,
@@ -34,7 +34,7 @@ AITool dateTimeTool = AIFunctionFactory.Create(GetDateTime, name: nameof(GetDate
AITool getWeatherTool = AIFunctionFactory.Create(GetWeather, name: nameof(GetWeather));
// Define the agent you want to create. (Prompt Agent in this case)
AIAgent originalAgent = aiProjectClient.CreateAIAgent(
AIAgent originalAgent = await aiProjectClient.CreateAIAgentAsync(
name: AssistantName,
model: deploymentName,
instructions: AssistantInstructions,
@@ -69,7 +69,7 @@ Console.WriteLine($"Function calling response: {functionCallResponse}");
// Special per-request middleware agent.
Console.WriteLine("\n\n=== Example 4: Middleware with human in the loop function approval ===");
AIAgent humanInTheLoopAgent = aiProjectClient.CreateAIAgent(
AIAgent humanInTheLoopAgent = await aiProjectClient.CreateAIAgentAsync(
name: "HumanInTheLoopAgent",
model: deploymentName,
instructions: "You are an Human in the loop testing AI assistant that helps people find information.",
@@ -34,7 +34,7 @@ AIProjectClient aiProjectClient = new(new Uri(endpoint), new AzureCliCredential(
// Define the agent with plugin tools
// Define the agent you want to create. (Prompt Agent in this case)
AIAgent agent = aiProjectClient.CreateAIAgent(
AIAgent agent = await aiProjectClient.CreateAIAgentAsync(
name: AssistantName,
model: deploymentName,
instructions: AssistantInstructions,
@@ -45,7 +45,7 @@ internal sealed class Program
string workflowInput = GetWorkflowInput(args);
AIAgent agent = aiProjectClient.GetAIAgent(agentVersion);
AIAgent agent = aiProjectClient.AsAIAgent(agentVersion);
AgentThread thread = await agent.GetNewThreadAsync();
@@ -82,39 +82,6 @@ public static class PersistentAgentsClientExtensions
}, services: services);
}
/// <summary>
/// Retrieves an existing server side agent, wrapped as a <see cref="ChatClientAgent"/> using the provided <see cref="PersistentAgentsClient"/>.
/// </summary>
/// <param name="persistentAgentsClient">The <see cref="PersistentAgentsClient"/> to create the <see cref="ChatClientAgent"/> with.</param>
/// <returns>A <see cref="ChatClientAgent"/> for the persistent agent.</returns>
/// <param name="agentId"> The ID of the server side agent to create a <see cref="ChatClientAgent"/> for.</param>
/// <param name="chatOptions">Options that should apply to all runs of the agent.</param>
/// <param name="clientFactory">Provides a way to customize the creation of the underlying <see cref="IChatClient"/> used by the agent.</param>
/// <param name="services">An optional <see cref="IServiceProvider"/> to use for resolving services required by the <see cref="AIFunction"/> instances being invoked.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>A <see cref="ChatClientAgent"/> instance that can be used to perform operations on the persistent agent.</returns>
public static ChatClientAgent GetAIAgent(
this PersistentAgentsClient persistentAgentsClient,
string agentId,
ChatOptions? chatOptions = null,
Func<IChatClient, IChatClient>? clientFactory = null,
IServiceProvider? services = 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));
}
var persistentAgentResponse = persistentAgentsClient.Administration.GetAgent(agentId, cancellationToken);
return persistentAgentsClient.AsAIAgent(persistentAgentResponse, chatOptions, clientFactory, services);
}
/// <summary>
/// Retrieves an existing server side agent, wrapped as a <see cref="ChatClientAgent"/> using the provided <see cref="PersistentAgentsClient"/>.
/// </summary>
@@ -232,45 +199,6 @@ public static class PersistentAgentsClientExtensions
return new ChatClientAgent(chatClient, agentOptions, services: services);
}
/// <summary>
/// Retrieves an existing server side agent, wrapped as a <see cref="ChatClientAgent"/> using the provided <see cref="PersistentAgentsClient"/>.
/// </summary>
/// <param name="persistentAgentsClient">The <see cref="PersistentAgentsClient"/> to create the <see cref="ChatClientAgent"/> with.</param>
/// <param name="agentId">The ID of the server side agent to create a <see cref="ChatClientAgent"/> for.</param>
/// <param name="options">Full set of options to configure the agent.</param>
/// <param name="clientFactory">Provides a way to customize the creation of the underlying <see cref="IChatClient"/> used by the agent.</param>
/// <param name="services">An optional <see cref="IServiceProvider"/> to use for resolving services required by the <see cref="AIFunction"/> instances being invoked.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>A <see cref="ChatClientAgent"/> instance that can be used to perform operations on the persistent agent.</returns>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="persistentAgentsClient"/> or <paramref name="options"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentException">Thrown when <paramref name="agentId"/> is empty or whitespace.</exception>
public static ChatClientAgent GetAIAgent(
this PersistentAgentsClient persistentAgentsClient,
string agentId,
ChatClientAgentOptions options,
Func<IChatClient, IChatClient>? clientFactory = null,
IServiceProvider? services = 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));
}
var persistentAgentResponse = persistentAgentsClient.Administration.GetAgent(agentId, cancellationToken);
return persistentAgentsClient.AsAIAgent(persistentAgentResponse, options, clientFactory, services);
}
/// <summary>
/// Retrieves an existing server side agent, wrapped as a <see cref="ChatClientAgent"/> using the provided <see cref="PersistentAgentsClient"/>.
/// </summary>
@@ -366,122 +294,6 @@ public static class PersistentAgentsClientExtensions
return await persistentAgentsClient.GetAIAgentAsync(createPersistentAgentResponse.Value.Id, clientFactory: clientFactory, services: services, cancellationToken: cancellationToken).ConfigureAwait(false);
}
/// <summary>
/// Creates a new server side agent using the provided <see cref="PersistentAgentsClient"/>.
/// </summary>
/// <param name="persistentAgentsClient">The <see cref="PersistentAgentsClient"/> to create the agent with.</param>
/// <param name="model">The model to be used by the agent.</param>
/// <param name="name">The name of the agent.</param>
/// <param name="description">The description of the agent.</param>
/// <param name="instructions">The instructions for the agent.</param>
/// <param name="tools">The tools to be used by the agent.</param>
/// <param name="toolResources">The resources for the tools.</param>
/// <param name="temperature">The temperature setting for the agent.</param>
/// <param name="topP">The top-p setting for the agent.</param>
/// <param name="responseFormat">The response format for the agent.</param>
/// <param name="metadata">The metadata for the agent.</param>
/// <param name="clientFactory">Provides a way to customize the creation of the underlying <see cref="IChatClient"/> used by the agent.</param>
/// <param name="services">An optional <see cref="IServiceProvider"/> to use for resolving services required by the <see cref="AIFunction"/> instances being invoked.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>A <see cref="ChatClientAgent"/> instance that can be used to perform operations on the newly created agent.</returns>
public static ChatClientAgent CreateAIAgent(
this PersistentAgentsClient persistentAgentsClient,
string model,
string? name = null,
string? description = null,
string? instructions = null,
IEnumerable<ToolDefinition>? tools = null,
ToolResources? toolResources = null,
float? temperature = null,
float? topP = null,
BinaryData? responseFormat = null,
IReadOnlyDictionary<string, string>? metadata = null,
Func<IChatClient, IChatClient>? clientFactory = null,
IServiceProvider? services = 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, services: services, cancellationToken: cancellationToken);
}
/// <summary>
/// Creates a new server side agent using the provided <see cref="PersistentAgentsClient"/>.
/// </summary>
/// <param name="persistentAgentsClient">The <see cref="PersistentAgentsClient"/> to create the agent with.</param>
/// <param name="model">The model to be used by the agent.</param>
/// <param name="options">Full set of options to configure the agent.</param>
/// <param name="clientFactory">Provides a way to customize the creation of the underlying <see cref="IChatClient"/> used by the agent.</param>
/// <param name="services">An optional <see cref="IServiceProvider"/> to use for resolving services required by the <see cref="AIFunction"/> instances being invoked.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>A <see cref="ChatClientAgent"/> instance that can be used to perform operations on the newly created agent.</returns>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="persistentAgentsClient"/> or <paramref name="model"/> or <paramref name="options"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentException">Thrown when <paramref name="model"/> is empty or whitespace.</exception>
public static ChatClientAgent CreateAIAgent(
this PersistentAgentsClient persistentAgentsClient,
string model,
ChatClientAgentOptions options,
Func<IChatClient, IChatClient>? clientFactory = null,
IServiceProvider? services = 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));
}
var toolDefinitionsAndResources = ConvertAIToolsToToolDefinitions(options.ChatOptions?.Tools);
var createPersistentAgentResponse = persistentAgentsClient.Administration.CreateAgent(
model: model,
name: options.Name,
description: options.Description,
instructions: options.ChatOptions?.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;
}
// Get a local proxy for the agent to work with.
return persistentAgentsClient.GetAIAgent(createPersistentAgentResponse.Value.Id, options, clientFactory: clientFactory, services: services, cancellationToken: cancellationToken);
}
/// <summary>
/// Creates a new server side agent using the provided <see cref="PersistentAgentsClient"/>.
/// </summary>
@@ -27,7 +27,7 @@ namespace Azure.AI.Projects;
public static partial class AzureAIProjectChatClientExtensions
{
/// <summary>
/// Retrieves an existing server side agent, wrapped as a <see cref="ChatClientAgent"/> using the provided <see cref="AIProjectClient"/>.
/// Uses an existing server side agent, wrapped as a <see cref="ChatClientAgent"/> using the provided <see cref="AIProjectClient"/> and <see cref="AgentReference"/>.
/// </summary>
/// <param name="aiProjectClient">The <see cref="AIProjectClient"/> to create the <see cref="ChatClientAgent"/> with. Cannot be <see langword="null"/>.</param>
/// <param name="agentReference">The <see cref="AgentReference"/> representing the name and version of the server side agent to create a <see cref="ChatClientAgent"/> for. Cannot be <see langword="null"/>.</param>
@@ -38,10 +38,10 @@ public static partial class AzureAIProjectChatClientExtensions
/// <exception cref="ArgumentNullException">Thrown when <paramref name="aiProjectClient"/> or <paramref name="agentReference"/> is <see langword="null"/>.</exception>
/// <exception cref="InvalidOperationException">The agent with the specified name was not found.</exception>
/// <remarks>
/// When retrieving an agent by using an <see cref="AgentReference"/>, minimal information will be available about the agent in the instance level, and any logic that relies
/// When instantiating a <see cref="ChatClientAgent"/> by using an <see cref="AgentReference"/>, minimal information will be available about the agent in the instance level, and any logic that relies
/// on <see cref="AIAgent.GetService(Type, object?)"/> to retrieve information about the agent like <see cref="AgentVersion" /> will receive <see langword="null"/> as the result.
/// </remarks>
public static ChatClientAgent GetAIAgent(
public static ChatClientAgent AsAIAgent(
this AIProjectClient aiProjectClient,
AgentReference agentReference,
IList<AITool>? tools = null,
@@ -52,7 +52,7 @@ public static partial class AzureAIProjectChatClientExtensions
Throw.IfNull(agentReference);
ThrowIfInvalidAgentName(agentReference.Name);
return CreateChatClientAgent(
return AsChatClientAgent(
aiProjectClient,
agentReference,
new ChatClientAgentOptions()
@@ -65,40 +65,6 @@ public static partial class AzureAIProjectChatClientExtensions
services);
}
/// <summary>
/// Retrieves an existing server side agent, wrapped as a <see cref="ChatClientAgent"/> using the provided <see cref="AIProjectClient"/>.
/// </summary>
/// <param name="aiProjectClient">The <see cref="AIProjectClient"/> to create the <see cref="ChatClientAgent"/> with. Cannot be <see langword="null"/>.</param>
/// <param name="name">The name of the server side agent to create a <see cref="ChatClientAgent"/> for. Cannot be <see langword="null"/> or whitespace.</param>
/// <param name="tools">The tools to use when interacting with the agent. This is required when using prompt agent definitions with tools.</param>
/// <param name="clientFactory">Provides a way to customize the creation of the underlying <see cref="IChatClient"/> used by the agent.</param>
/// <param name="services">An optional <see cref="IServiceProvider"/> to use for resolving services required by the <see cref="AIFunction"/> instances being invoked.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>A <see cref="ChatClientAgent"/> instance that can be used to perform operations based on the latest version of the named Azure AI Agent.</returns>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="aiProjectClient"/> or <paramref name="name"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentException">Thrown when <paramref name="name"/> is empty or whitespace, or when the agent with the specified name was not found.</exception>
/// <exception cref="InvalidOperationException">The agent with the specified name was not found.</exception>
public static ChatClientAgent GetAIAgent(
this AIProjectClient aiProjectClient,
string name,
IList<AITool>? tools = null,
Func<IChatClient, IChatClient>? clientFactory = null,
IServiceProvider? services = null,
CancellationToken cancellationToken = default)
{
Throw.IfNull(aiProjectClient);
ThrowIfInvalidAgentName(name);
AgentRecord agentRecord = GetAgentRecordByName(aiProjectClient, name, cancellationToken);
return AsAIAgent(
aiProjectClient,
agentRecord,
tools,
clientFactory,
services);
}
/// <summary>
/// Asynchronously retrieves an existing server side agent, wrapped as a <see cref="ChatClientAgent"/> using the provided <see cref="AIProjectClient"/>.
/// </summary>
@@ -134,7 +100,7 @@ public static partial class AzureAIProjectChatClientExtensions
}
/// <summary>
/// Gets a runnable agent instance from the provided agent record.
/// Uses an existing server side agent, wrapped as a <see cref="ChatClientAgent"/> using the provided <see cref="AIProjectClient"/> and <see cref="AgentRecord"/>.
/// </summary>
/// <param name="aiProjectClient">The client used to interact with Azure AI Agents. Cannot be <see langword="null"/>.</param>
/// <param name="agentRecord">The agent record to be converted. The latest version will be used. Cannot be <see langword="null"/>.</param>
@@ -155,7 +121,7 @@ public static partial class AzureAIProjectChatClientExtensions
var allowDeclarativeMode = tools is not { Count: > 0 };
return CreateChatClientAgent(
return AsChatClientAgent(
aiProjectClient,
agentRecord,
tools,
@@ -165,7 +131,7 @@ public static partial class AzureAIProjectChatClientExtensions
}
/// <summary>
/// Gets a runnable agent instance from a <see cref="AgentVersion"/> containing metadata about an Azure AI Agent.
/// Uses an existing server side agent, wrapped as a <see cref="ChatClientAgent"/> using the provided <see cref="AIProjectClient"/> and <see cref="AgentVersion"/>.
/// </summary>
/// <param name="aiProjectClient">The client used to interact with Azure AI Agents. Cannot be <see langword="null"/>.</param>
/// <param name="agentVersion">The agent version to be converted. Cannot be <see langword="null"/>.</param>
@@ -186,7 +152,7 @@ public static partial class AzureAIProjectChatClientExtensions
var allowDeclarativeMode = tools is not { Count: > 0 };
return CreateChatClientAgent(
return AsChatClientAgent(
aiProjectClient,
agentVersion,
tools,
@@ -196,47 +162,7 @@ public static partial class AzureAIProjectChatClientExtensions
}
/// <summary>
/// Creates a new Prompt AI Agent using the provided <see cref="AIProjectClient"/> and options.
/// </summary>
/// <param name="aiProjectClient">The client used to manage and interact with AI agents. Cannot be <see langword="null"/>.</param>
/// <param name="options">The options for creating the agent. Cannot be <see langword="null"/>.</param>
/// <param name="clientFactory">A factory function to customize the creation of the chat client used by the agent.</param>
/// <param name="services">An optional <see cref="IServiceProvider"/> to use for resolving services required by the <see cref="AIFunction"/> instances being invoked.</param>
/// <param name="cancellationToken">A <see cref="CancellationToken"/> to cancel the operation if needed.</param>
/// <returns>A <see cref="ChatClientAgent"/> instance that can be used to perform operations on the newly created agent.</returns>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="aiProjectClient"/> or <paramref name="options"/> is <see langword="null"/>.</exception>
public static ChatClientAgent GetAIAgent(
this AIProjectClient aiProjectClient,
ChatClientAgentOptions options,
Func<IChatClient, IChatClient>? clientFactory = null,
IServiceProvider? services = null,
CancellationToken cancellationToken = default)
{
Throw.IfNull(aiProjectClient);
Throw.IfNull(options);
if (string.IsNullOrWhiteSpace(options.Name))
{
throw new ArgumentException("Agent name must be provided in the options.Name property", nameof(options));
}
ThrowIfInvalidAgentName(options.Name);
AgentRecord agentRecord = GetAgentRecordByName(aiProjectClient, options.Name, cancellationToken);
var agentVersion = agentRecord.Versions.Latest;
var agentOptions = CreateChatClientAgentOptions(agentVersion, options, requireInvocableTools: true);
return CreateChatClientAgent(
aiProjectClient,
agentVersion,
agentOptions,
clientFactory,
services);
}
/// <summary>
/// Creates a new Prompt AI Agent using the provided <see cref="AIProjectClient"/> and options.
/// Asynchronously retrieves an existing server side agent, wrapped as a <see cref="ChatClientAgent"/> using the provided <see cref="AIProjectClient"/>.
/// </summary>
/// <param name="aiProjectClient">The client used to manage and interact with AI agents. Cannot be <see langword="null"/>.</param>
/// <param name="options">The options for creating the agent. Cannot be <see langword="null"/>.</param>
@@ -267,7 +193,7 @@ public static partial class AzureAIProjectChatClientExtensions
var agentOptions = CreateChatClientAgentOptions(agentVersion, options, requireInvocableTools: true);
return CreateChatClientAgent(
return AsChatClientAgent(
aiProjectClient,
agentVersion,
agentOptions,
@@ -276,49 +202,7 @@ public static partial class AzureAIProjectChatClientExtensions
}
/// <summary>
/// Creates a new Prompt AI agent using the specified configuration parameters.
/// </summary>
/// <param name="aiProjectClient">The client used to manage and interact with AI agents. Cannot be <see langword="null"/>.</param>
/// <param name="name">The name for the agent.</param>
/// <param name="model">The name of the model to use for the agent. Cannot be <see langword="null"/> or whitespace.</param>
/// <param name="instructions">The instructions that guide the agent's behavior. Cannot be <see langword="null"/> or whitespace.</param>
/// <param name="description">The description for the agent.</param>
/// <param name="tools">The tools to use when interacting with the agent, this is required when using prompt agent definitions with tools.</param>
/// <param name="clientFactory">A factory function to customize the creation of the chat client used by the agent.</param>
/// <param name="services">An optional <see cref="IServiceProvider"/> to use for resolving services required by the <see cref="AIFunction"/> instances being invoked.</param>
/// <param name="cancellationToken">A token to monitor for cancellation requests.</param>
/// <returns>A <see cref="ChatClientAgent"/> instance that can be used to perform operations on the newly created agent.</returns>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="aiProjectClient"/>, <paramref name="model"/>, or <paramref name="instructions"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentException">Thrown when <paramref name="model"/> or <paramref name="instructions"/> is empty or whitespace.</exception>
/// <remarks>When using prompt agent definitions with tools the parameter <paramref name="tools"/> needs to be provided.</remarks>
public static ChatClientAgent CreateAIAgent(
this AIProjectClient aiProjectClient,
string name,
string model,
string instructions,
string? description = null,
IList<AITool>? tools = null,
Func<IChatClient, IChatClient>? clientFactory = null,
IServiceProvider? services = null,
CancellationToken cancellationToken = default)
{
Throw.IfNull(aiProjectClient);
ThrowIfInvalidAgentName(name);
Throw.IfNullOrWhitespace(model);
Throw.IfNullOrWhitespace(instructions);
return CreateAIAgent(
aiProjectClient,
name,
tools,
new AgentVersionCreationOptions(new PromptAgentDefinition(model) { Instructions = instructions }) { Description = description },
clientFactory,
services,
cancellationToken);
}
/// <summary>
/// Creates a new Prompt AI agent using the specified configuration parameters.
/// Creates a new Prompt AI agent in the Foundry service using the specified configuration parameters, and exposes it as a <see cref="ChatClientAgent"/>.
/// </summary>
/// <param name="aiProjectClient">The client used to manage and interact with AI agents. Cannot be <see langword="null"/>.</param>
/// <param name="name">The name for the agent.</param>
@@ -360,73 +244,7 @@ public static partial class AzureAIProjectChatClientExtensions
}
/// <summary>
/// Creates a new Prompt AI Agent using the provided <see cref="AIProjectClient"/> and options.
/// </summary>
/// <param name="aiProjectClient">The client used to manage and interact with AI agents. Cannot be <see langword="null"/>.</param>
/// <param name="model">The name of the model to use for the agent. Cannot be <see langword="null"/> or whitespace.</param>
/// <param name="options">The options for creating the agent. Cannot be <see langword="null"/>.</param>
/// <param name="clientFactory">A factory function to customize the creation of the chat client used by the agent.</param>
/// <param name="services">An optional <see cref="IServiceProvider"/> to use for resolving services required by the <see cref="AIFunction"/> instances being invoked.</param>
/// <param name="cancellationToken">A <see cref="CancellationToken"/> to cancel the operation if needed.</param>
/// <returns>A <see cref="ChatClientAgent"/> instance that can be used to perform operations on the newly created agent.</returns>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="aiProjectClient"/> or <paramref name="options"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentException">Thrown when <paramref name="model"/> is empty or whitespace, or when the agent name is not provided in the options.</exception>
public static ChatClientAgent CreateAIAgent(
this AIProjectClient aiProjectClient,
string model,
ChatClientAgentOptions options,
Func<IChatClient, IChatClient>? clientFactory = null,
IServiceProvider? services = null,
CancellationToken cancellationToken = default)
{
Throw.IfNull(aiProjectClient);
Throw.IfNull(options);
Throw.IfNullOrWhitespace(model);
const bool RequireInvocableTools = true;
if (string.IsNullOrWhiteSpace(options.Name))
{
throw new ArgumentException("Agent name must be provided in the options.Name property", nameof(options));
}
ThrowIfInvalidAgentName(options.Name);
PromptAgentDefinition agentDefinition = new(model)
{
Instructions = options.ChatOptions?.Instructions,
Temperature = options.ChatOptions?.Temperature,
TopP = options.ChatOptions?.TopP,
TextOptions = new() { TextFormat = ToOpenAIResponseTextFormat(options.ChatOptions?.ResponseFormat, options.ChatOptions) }
};
// Attempt to capture breaking glass options from the raw representation factory that match the agent definition.
if (options.ChatOptions?.RawRepresentationFactory?.Invoke(new NoOpChatClient()) is CreateResponseOptions respCreationOptions)
{
agentDefinition.ReasoningOptions = respCreationOptions.ReasoningOptions;
}
ApplyToolsToAgentDefinition(agentDefinition, options.ChatOptions?.Tools);
AgentVersionCreationOptions? creationOptions = new(agentDefinition);
if (!string.IsNullOrWhiteSpace(options.Description))
{
creationOptions.Description = options.Description;
}
AgentVersion agentVersion = CreateAgentVersionWithProtocol(aiProjectClient, options.Name, creationOptions, cancellationToken);
var agentOptions = CreateChatClientAgentOptions(agentVersion, options, RequireInvocableTools);
return CreateChatClientAgent(
aiProjectClient,
agentVersion,
agentOptions,
clientFactory,
services);
}
/// <summary>
/// Creates a new Prompt AI Agent using the provided <see cref="AIProjectClient"/> and options.
/// Creates a new Prompt AI agent in the Foundry service using the specified configuration parameters, and exposes it as a <see cref="ChatClientAgent"/>.
/// </summary>
/// <param name="aiProjectClient">The client used to manage and interact with AI agents. Cannot be <see langword="null"/>.</param>
/// <param name="model">The name of the model to use for the agent. Cannot be <see langword="null"/> or whitespace.</param>
@@ -483,7 +301,7 @@ public static partial class AzureAIProjectChatClientExtensions
var agentOptions = CreateChatClientAgentOptions(agentVersion, options, RequireInvocableTools);
return CreateChatClientAgent(
return AsChatClientAgent(
aiProjectClient,
agentVersion,
agentOptions,
@@ -492,42 +310,7 @@ public static partial class AzureAIProjectChatClientExtensions
}
/// <summary>
/// Creates a new AI agent using the specified agent definition and optional configuration parameters.
/// </summary>
/// <param name="aiProjectClient">The client used to manage and interact with AI agents. Cannot be <see langword="null"/>.</param>
/// <param name="name">The name for the agent.</param>
/// <param name="creationOptions">Settings that control the creation of the agent.</param>
/// <param name="clientFactory">A factory function to customize the creation of the chat client used by the agent.</param>
/// <param name="cancellationToken">A token to monitor for cancellation requests.</param>
/// <returns>A <see cref="ChatClientAgent"/> instance that can be used to perform operations on the newly created agent.</returns>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="aiProjectClient"/> or <paramref name="creationOptions"/> is <see langword="null"/>.</exception>
/// <remarks>
/// When using this extension method with a <see cref="PromptAgentDefinition"/> the tools are only declarative and not invocable.
/// Invocation of any in-process tools will need to be handled manually.
/// </remarks>
public static ChatClientAgent CreateAIAgent(
this AIProjectClient aiProjectClient,
string name,
AgentVersionCreationOptions creationOptions,
Func<IChatClient, IChatClient>? clientFactory = null,
CancellationToken cancellationToken = default)
{
Throw.IfNull(aiProjectClient);
ThrowIfInvalidAgentName(name);
Throw.IfNull(creationOptions);
return CreateAIAgent(
aiProjectClient,
name,
tools: null,
creationOptions,
clientFactory,
services: null,
cancellationToken);
}
/// <summary>
/// Asynchronously creates a new AI agent using the specified agent definition and optional configuration
/// Creates a new Prompt AI agent in the Foundry service using the specified configuration parameters, and exposes it as a <see cref="ChatClientAgent"/>.
/// parameters.
/// </summary>
/// <param name="aiProjectClient">The client used to manage and interact with AI agents. Cannot be <see langword="null"/>.</param>
@@ -566,18 +349,6 @@ public static partial class AzureAIProjectChatClientExtensions
private static readonly ModelReaderWriterOptions s_modelWriterOptionsWire = new("W");
/// <summary>
/// Retrieves an agent record by name using the Protocol method with user-agent header.
/// </summary>
private static AgentRecord GetAgentRecordByName(AIProjectClient aiProjectClient, string agentName, CancellationToken cancellationToken)
{
ClientResult protocolResponse = aiProjectClient.Agents.GetAgent(agentName, cancellationToken.ToRequestOptions(false));
var rawResponse = protocolResponse.GetRawResponse();
AgentRecord? result = ModelReaderWriter.Read<AgentRecord>(rawResponse.Content, s_modelWriterOptionsWire, AzureAIProjectsOpenAIContext.Default);
return ClientResult.FromOptionalValue(result, rawResponse).Value!
?? throw new InvalidOperationException($"Agent with name '{agentName}' not found.");
}
/// <summary>
/// Asynchronously retrieves an agent record by name using the Protocol method with user-agent header.
/// </summary>
@@ -590,19 +361,6 @@ public static partial class AzureAIProjectChatClientExtensions
?? throw new InvalidOperationException($"Agent with name '{agentName}' not found.");
}
/// <summary>
/// Creates an agent version using the Protocol method with user-agent header.
/// </summary>
private static AgentVersion CreateAgentVersionWithProtocol(AIProjectClient aiProjectClient, string agentName, AgentVersionCreationOptions creationOptions, CancellationToken cancellationToken)
{
using BinaryContent protocolRequest = BinaryContent.Create(ModelReaderWriter.Write(creationOptions, ModelReaderWriterOptions.Json, AzureAIProjectsContext.Default));
ClientResult protocolResponse = aiProjectClient.Agents.CreateAgentVersion(agentName, protocolRequest, cancellationToken.ToRequestOptions(false));
var rawResponse = protocolResponse.GetRawResponse();
AgentVersion? result = ModelReaderWriter.Read<AgentVersion>(rawResponse.Content, s_modelWriterOptionsWire, AzureAIProjectsOpenAIContext.Default);
return ClientResult.FromValue(result, rawResponse).Value!;
}
/// <summary>
/// Asynchronously creates an agent version using the Protocol method with user-agent header.
/// </summary>
@@ -616,33 +374,6 @@ public static partial class AzureAIProjectChatClientExtensions
return ClientResult.FromValue(result, rawResponse).Value!;
}
private static ChatClientAgent CreateAIAgent(
this AIProjectClient aiProjectClient,
string name,
IList<AITool>? tools,
AgentVersionCreationOptions creationOptions,
Func<IChatClient, IChatClient>? clientFactory,
IServiceProvider? services,
CancellationToken cancellationToken)
{
var allowDeclarativeMode = tools is not { Count: > 0 };
if (!allowDeclarativeMode)
{
ApplyToolsToAgentDefinition(creationOptions.Definition, tools);
}
AgentVersion agentVersion = CreateAgentVersionWithProtocol(aiProjectClient, name, creationOptions, cancellationToken);
return CreateChatClientAgent(
aiProjectClient,
agentVersion,
tools,
clientFactory,
!allowDeclarativeMode,
services);
}
private static async Task<ChatClientAgent> CreateAIAgentAsync(
this AIProjectClient aiProjectClient,
string name,
@@ -661,7 +392,7 @@ public static partial class AzureAIProjectChatClientExtensions
AgentVersion agentVersion = await CreateAgentVersionWithProtocolAsync(aiProjectClient, name, creationOptions, cancellationToken).ConfigureAwait(false);
return CreateChatClientAgent(
return AsChatClientAgent(
aiProjectClient,
agentVersion,
tools,
@@ -671,7 +402,7 @@ public static partial class AzureAIProjectChatClientExtensions
}
/// <summary>This method creates an <see cref="ChatClientAgent"/> with the specified ChatClientAgentOptions.</summary>
private static ChatClientAgent CreateChatClientAgent(
private static ChatClientAgent AsChatClientAgent(
AIProjectClient aiProjectClient,
AgentVersion agentVersion,
ChatClientAgentOptions agentOptions,
@@ -689,7 +420,7 @@ public static partial class AzureAIProjectChatClientExtensions
}
/// <summary>This method creates an <see cref="ChatClientAgent"/> with the specified ChatClientAgentOptions.</summary>
private static ChatClientAgent CreateChatClientAgent(
private static ChatClientAgent AsChatClientAgent(
AIProjectClient aiProjectClient,
AgentRecord agentRecord,
ChatClientAgentOptions agentOptions,
@@ -707,7 +438,7 @@ public static partial class AzureAIProjectChatClientExtensions
}
/// <summary>This method creates an <see cref="ChatClientAgent"/> with the specified ChatClientAgentOptions.</summary>
private static ChatClientAgent CreateChatClientAgent(
private static ChatClientAgent AsChatClientAgent(
AIProjectClient aiProjectClient,
AgentReference agentReference,
ChatClientAgentOptions agentOptions,
@@ -725,14 +456,14 @@ public static partial class AzureAIProjectChatClientExtensions
}
/// <summary>This method creates an <see cref="ChatClientAgent"/> with a auto-generated ChatClientAgentOptions from the specified configuration parameters.</summary>
private static ChatClientAgent CreateChatClientAgent(
private static ChatClientAgent AsChatClientAgent(
AIProjectClient AIProjectClient,
AgentVersion agentVersion,
IList<AITool>? tools,
Func<IChatClient, IChatClient>? clientFactory,
bool requireInvocableTools,
IServiceProvider? services)
=> CreateChatClientAgent(
=> AsChatClientAgent(
AIProjectClient,
agentVersion,
CreateChatClientAgentOptions(agentVersion, new ChatOptions() { Tools = tools }, requireInvocableTools),
@@ -740,14 +471,14 @@ public static partial class AzureAIProjectChatClientExtensions
services);
/// <summary>This method creates an <see cref="ChatClientAgent"/> with a auto-generated ChatClientAgentOptions from the specified configuration parameters.</summary>
private static ChatClientAgent CreateChatClientAgent(
private static ChatClientAgent AsChatClientAgent(
AIProjectClient AIProjectClient,
AgentRecord agentRecord,
IList<AITool>? tools,
Func<IChatClient, IChatClient>? clientFactory,
bool requireInvocableTools,
IServiceProvider? services)
=> CreateChatClientAgent(
=> AsChatClientAgent(
AIProjectClient,
agentRecord,
CreateChatClientAgentOptions(agentRecord.Versions.Latest, new ChatOptions() { Tools = tools }, requireInvocableTools),
@@ -93,39 +93,6 @@ public static class OpenAIAssistantClientExtensions
}, services: services);
}
/// <summary>
/// Retrieves an existing server side agent, wrapped as a <see cref="ChatClientAgent"/> using the provided <see cref="AssistantClient"/>.
/// </summary>
/// <param name="assistantClient">The <see cref="AssistantClient"/> to create the <see cref="ChatClientAgent"/> with.</param>
/// <param name="agentId">The ID of the server side agent to create a <see cref="ChatClientAgent"/> for.</param>
/// <param name="chatOptions">Options that should apply to all runs of the agent.</param>
/// <param name="clientFactory">Provides a way to customize the creation of the underlying <see cref="IChatClient"/> used by the agent.</param>
/// <param name="services">An optional <see cref="IServiceProvider"/> to use for resolving services required by the <see cref="AIFunction"/> instances being invoked.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>A <see cref="ChatClientAgent"/> instance that can be used to perform operations on the assistant agent.</returns>
[Obsolete("The Assistants API has been deprecated. Please use the Responses API instead.")]
public static ChatClientAgent GetAIAgent(
this AssistantClient assistantClient,
string agentId,
ChatOptions? chatOptions = null,
Func<IChatClient, IChatClient>? clientFactory = null,
IServiceProvider? services = null,
CancellationToken cancellationToken = default)
{
if (assistantClient is null)
{
throw new ArgumentNullException(nameof(assistantClient));
}
if (string.IsNullOrWhiteSpace(agentId))
{
throw new ArgumentException($"{nameof(agentId)} should not be null or whitespace.", nameof(agentId));
}
var assistant = assistantClient.GetAssistant(agentId, cancellationToken);
return assistantClient.AsAIAgent(assistant, chatOptions, clientFactory, services);
}
/// <summary>
/// Retrieves an existing server side agent, wrapped as a <see cref="ChatClientAgent"/> using the provided <see cref="AssistantClient"/>.
/// </summary>
@@ -245,46 +212,6 @@ public static class OpenAIAssistantClientExtensions
return new ChatClientAgent(chatClient, mergedOptions, services: services);
}
/// <summary>
/// Retrieves an existing server side agent, wrapped as a <see cref="ChatClientAgent"/> using the provided <see cref="AssistantClient"/>.
/// </summary>
/// <param name="assistantClient">The <see cref="AssistantClient"/> to create the <see cref="ChatClientAgent"/> with.</param>
/// <param name="agentId">The ID of the server side agent to create a <see cref="ChatClientAgent"/> for.</param>
/// <param name="options">Full set of options to configure the agent.</param>
/// <param name="clientFactory">Provides a way to customize the creation of the underlying <see cref="IChatClient"/> used by the agent.</param>
/// <param name="services">An optional <see cref="IServiceProvider"/> to use for resolving services required by the <see cref="AIFunction"/> instances being invoked.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>A <see cref="ChatClientAgent"/> instance that can be used to perform operations on the assistant agent.</returns>
/// <exception cref="ArgumentNullException"><paramref name="assistantClient"/> or <paramref name="options"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentException"><paramref name="agentId"/> is empty or whitespace.</exception>
[Obsolete("The Assistants API has been deprecated. Please use the Responses API instead.")]
public static ChatClientAgent GetAIAgent(
this AssistantClient assistantClient,
string agentId,
ChatClientAgentOptions options,
Func<IChatClient, IChatClient>? clientFactory = null,
IServiceProvider? services = null,
CancellationToken cancellationToken = default)
{
if (assistantClient is null)
{
throw new ArgumentNullException(nameof(assistantClient));
}
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));
}
var assistant = assistantClient.GetAssistant(agentId, cancellationToken);
return assistantClient.AsAIAgent(assistant, options, clientFactory, services);
}
/// <summary>
/// Retrieves an existing server side agent, wrapped as a <see cref="ChatClientAgent"/> using the provided <see cref="AssistantClient"/>.
/// </summary>
@@ -325,111 +252,6 @@ public static class OpenAIAssistantClientExtensions
return assistantClient.AsAIAgent(assistantResponse, options, clientFactory, services);
}
/// <summary>
/// Creates an AI agent from an <see cref="AssistantClient"/> using the OpenAI Assistant API.
/// </summary>
/// <param name="client">The OpenAI <see cref="AssistantClient" /> to use for the agent.</param>
/// <param name="model">The model identifier to use (e.g., "gpt-4").</param>
/// <param name="instructions">Optional system instructions that define the agent's behavior and personality.</param>
/// <param name="name">Optional name for the agent for identification purposes.</param>
/// <param name="description">Optional description of the agent's capabilities and purpose.</param>
/// <param name="tools">Optional collection of AI tools that the agent can use during conversations.</param>
/// <param name="clientFactory">Provides a way to customize the creation of the underlying <see cref="IChatClient"/> used by the agent.</param>
/// <param name="loggerFactory">Optional logger factory for enabling logging within the agent.</param>
/// <param name="services">An optional <see cref="IServiceProvider"/> to use for resolving services required by the <see cref="AIFunction"/> instances being invoked.</param>
/// <returns>An <see cref="ChatClientAgent"/> instance backed by the OpenAI Assistant service.</returns>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="client"/> or <paramref name="model"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentException">Thrown when <paramref name="model"/> is empty or whitespace.</exception>
[Obsolete("The Assistants API has been deprecated. Please use the Responses API instead.")]
public static ChatClientAgent CreateAIAgent(
this AssistantClient client,
string model,
string? instructions = null,
string? name = null,
string? description = null,
IList<AITool>? tools = null,
Func<IChatClient, IChatClient>? clientFactory = null,
ILoggerFactory? loggerFactory = null,
IServiceProvider? services = null) =>
client.CreateAIAgent(
model,
new ChatClientAgentOptions()
{
Name = name,
Description = description,
ChatOptions = tools is null && string.IsNullOrWhiteSpace(instructions) ? null : new ChatOptions()
{
Tools = tools,
Instructions = instructions
}
},
clientFactory,
loggerFactory,
services);
/// <summary>
/// Creates an AI agent from an <see cref="AssistantClient"/> using the OpenAI Assistant API.
/// </summary>
/// <param name="client">The OpenAI <see cref="AssistantClient" /> to use for the agent.</param>
/// <param name="model">The model identifier to use (e.g., "gpt-4").</param>
/// <param name="options">Full set of options to configure the agent.</param>
/// <param name="clientFactory">Provides a way to customize the creation of the underlying <see cref="IChatClient"/> used by the agent.</param>
/// <param name="loggerFactory">Optional logger factory for enabling logging within the agent.</param>
/// <param name="services">An optional <see cref="IServiceProvider"/> to use for resolving services required by the <see cref="AIFunction"/> instances being invoked.</param>
/// <returns>An <see cref="ChatClientAgent"/> instance backed by the OpenAI Assistant service.</returns>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="client"/> or <paramref name="model"/> or <paramref name="options"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentException">Thrown when <paramref name="model"/> is empty or whitespace.</exception>
[Obsolete("The Assistants API has been deprecated. Please use the Responses API instead.")]
public static ChatClientAgent CreateAIAgent(
this AssistantClient client,
string model,
ChatClientAgentOptions options,
Func<IChatClient, IChatClient>? clientFactory = null,
ILoggerFactory? loggerFactory = null,
IServiceProvider? services = null)
{
Throw.IfNull(client);
Throw.IfNullOrEmpty(model);
Throw.IfNull(options);
var assistantOptions = new AssistantCreationOptions()
{
Name = options.Name,
Description = options.Description,
Instructions = options.ChatOptions?.Instructions,
};
// Convert AITools to ToolDefinitions and ToolResources
var toolDefinitionsAndResources = ConvertAIToolsToToolDefinitions(options.ChatOptions?.Tools);
if (toolDefinitionsAndResources.ToolDefinitions is { Count: > 0 })
{
toolDefinitionsAndResources.ToolDefinitions.ForEach(x => assistantOptions.Tools.Add(x));
}
if (toolDefinitionsAndResources.ToolResources is not null)
{
assistantOptions.ToolResources = toolDefinitionsAndResources.ToolResources;
}
// Create the assistant in the assistant service.
var assistantCreateResult = client.CreateAssistant(model, assistantOptions);
var assistantId = assistantCreateResult.Value.Id;
// Build the local agent object.
var chatClient = client.AsIChatClient(assistantId);
if (clientFactory is not null)
{
chatClient = clientFactory(chatClient);
}
var agentOptions = options.Clone();
agentOptions.Id = assistantId;
options.ChatOptions ??= new ChatOptions();
options.ChatOptions!.Tools = toolDefinitionsAndResources.FunctionToolsAndOtherTools;
return new ChatClientAgent(chatClient, agentOptions, loggerFactory, services);
}
/// <summary>
/// Creates an AI agent from an <see cref="AssistantClient"/> using the OpenAI Assistant API.
/// </summary>
@@ -22,9 +22,7 @@ public class AIProjectClientCreateTests
[Theory]
[InlineData("CreateWithChatClientAgentOptionsAsync")]
[InlineData("CreateWithChatClientAgentOptionsSync")]
[InlineData("CreateWithFoundryOptionsAsync")]
[InlineData("CreateWithFoundryOptionsSync")]
public async Task CreateAgent_CreatesAgentWithCorrectMetadataAsync(string createMechanism)
{
// Arrange.
@@ -43,20 +41,9 @@ public class AIProjectClientCreateTests
Description = AgentDescription,
ChatOptions = new() { Instructions = AgentInstructions }
}),
"CreateWithChatClientAgentOptionsSync" => this._client.CreateAIAgent(
model: s_config.DeploymentName,
options: new ChatClientAgentOptions()
{
Name = AgentName,
Description = AgentDescription,
ChatOptions = new() { Instructions = AgentInstructions }
}),
"CreateWithFoundryOptionsAsync" => await this._client.CreateAIAgentAsync(
name: AgentName,
creationOptions: new AgentVersionCreationOptions(new PromptAgentDefinition(s_config.DeploymentName) { Instructions = AgentInstructions }) { Description = AgentDescription }),
"CreateWithFoundryOptionsSync" => this._client.CreateAIAgent(
name: AgentName,
creationOptions: new AgentVersionCreationOptions(new PromptAgentDefinition(s_config.DeploymentName) { Instructions = AgentInstructions }) { Description = AgentDescription }),
_ => throw new InvalidOperationException($"Unknown create mechanism: {createMechanism}")
};
@@ -84,9 +71,7 @@ public class AIProjectClientCreateTests
[Theory(Skip = "For manual testing only")]
[InlineData("CreateWithChatClientAgentOptionsAsync")]
[InlineData("CreateWithChatClientAgentOptionsSync")]
[InlineData("CreateWithFoundryOptionsAsync")]
[InlineData("CreateWithFoundryOptionsSync")]
public async Task CreateAgent_CreatesAgentWithVectorStoresAsync(string createMechanism)
{
// Arrange.
@@ -120,21 +105,11 @@ public class AIProjectClientCreateTests
name: AgentName,
instructions: AgentInstructions,
tools: [new HostedFileSearchTool() { Inputs = [new HostedVectorStoreContent(vectorStoreMetadata.Value.Id)] }]),
"CreateWithChatClientAgentOptionsSync" => this._client.CreateAIAgent(
model: s_config.DeploymentName,
name: AgentName,
instructions: AgentInstructions,
tools: [new HostedFileSearchTool() { Inputs = [new HostedVectorStoreContent(vectorStoreMetadata.Value.Id)] }]),
"CreateWithFoundryOptionsAsync" => await this._client.CreateAIAgentAsync(
model: s_config.DeploymentName,
name: AgentName,
instructions: AgentInstructions,
tools: [ResponseTool.CreateFileSearchTool(vectorStoreIds: [vectorStoreMetadata.Value.Id]).AsAITool()]),
"CreateWithFoundryOptionsSync" => this._client.CreateAIAgent(
model: s_config.DeploymentName,
name: AgentName,
instructions: AgentInstructions,
tools: [ResponseTool.CreateFileSearchTool(vectorStoreIds: [vectorStoreMetadata.Value.Id]).AsAITool()]),
_ => throw new InvalidOperationException($"Unknown create mechanism: {createMechanism}")
};
@@ -157,9 +132,7 @@ public class AIProjectClientCreateTests
[Theory]
[InlineData("CreateWithChatClientAgentOptionsAsync")]
[InlineData("CreateWithChatClientAgentOptionsSync")]
[InlineData("CreateWithFoundryOptionsAsync")]
[InlineData("CreateWithFoundryOptionsSync")]
public async Task CreateAgent_CreatesAgentWithCodeInterpreterAsync(string createMechanism)
{
// Arrange.
@@ -192,22 +165,12 @@ public class AIProjectClientCreateTests
name: AgentName,
instructions: AgentInstructions,
tools: [new HostedCodeInterpreterTool() { Inputs = [new HostedFileContent(uploadedCodeFile.Id)] }]),
"CreateWithChatClientAgentOptionsSync" => this._client.CreateAIAgent(
model: s_config.DeploymentName,
name: AgentName,
instructions: AgentInstructions,
tools: [new HostedCodeInterpreterTool() { Inputs = [new HostedFileContent(uploadedCodeFile.Id)] }]),
// Foundry (definitions + resources provided directly)
"CreateWithFoundryOptionsAsync" => await this._client.CreateAIAgentAsync(
model: s_config.DeploymentName,
name: AgentName,
instructions: AgentInstructions,
tools: [ResponseTool.CreateCodeInterpreterTool(new CodeInterpreterToolContainer(CodeInterpreterToolContainerConfiguration.CreateAutomaticContainerConfiguration([uploadedCodeFile.Id]))).AsAITool()]),
"CreateWithFoundryOptionsSync" => this._client.CreateAIAgent(
model: s_config.DeploymentName,
name: AgentName,
instructions: AgentInstructions,
tools: [ResponseTool.CreateCodeInterpreterTool(new CodeInterpreterToolContainer(CodeInterpreterToolContainerConfiguration.CreateAutomaticContainerConfiguration([uploadedCodeFile.Id]))).AsAITool()]),
_ => throw new InvalidOperationException($"Unknown create mechanism: {createMechanism}")
};
@@ -229,7 +192,6 @@ public class AIProjectClientCreateTests
[Theory]
[InlineData("CreateWithChatClientAgentOptionsAsync")]
[InlineData("CreateWithChatClientAgentOptionsSync")]
public async Task CreateAgent_CreatesAgentWithAIFunctionToolsAsync(string createMechanism)
{
// Arrange.
@@ -248,13 +210,6 @@ public class AIProjectClientCreateTests
Name = AgentName,
ChatOptions = new() { Instructions = AgentInstructions, Tools = [weatherFunction] }
}),
"CreateWithChatClientAgentOptionsSync" => this._client.CreateAIAgent(
s_config.DeploymentName,
options: new ChatClientAgentOptions()
{
Name = AgentName,
ChatOptions = new() { Instructions = AgentInstructions, Tools = [weatherFunction] }
}),
_ => throw new InvalidOperationException($"Unknown create mechanism: {createMechanism}")
};
@@ -20,9 +20,7 @@ public class AzureAIAgentsPersistentCreateTests
[Theory]
[InlineData("CreateWithChatClientAgentOptionsAsync")]
[InlineData("CreateWithChatClientAgentOptionsSync")]
[InlineData("CreateWithFoundryOptionsAsync")]
[InlineData("CreateWithFoundryOptionsSync")]
public async Task CreateAgent_CreatesAgentWithCorrectMetadataAsync(string createMechanism)
{
// Arrange.
@@ -41,24 +39,11 @@ public class AzureAIAgentsPersistentCreateTests
Name = AgentName,
Description = AgentDescription
}),
"CreateWithChatClientAgentOptionsSync" => this._persistentAgentsClient.CreateAIAgent(
s_config.DeploymentName,
options: new ChatClientAgentOptions()
{
ChatOptions = new() { Instructions = AgentInstructions },
Name = AgentName,
Description = AgentDescription
}),
"CreateWithFoundryOptionsAsync" => await this._persistentAgentsClient.CreateAIAgentAsync(
s_config.DeploymentName,
instructions: AgentInstructions,
name: AgentName,
description: AgentDescription),
"CreateWithFoundryOptionsSync" => this._persistentAgentsClient.CreateAIAgent(
s_config.DeploymentName,
instructions: AgentInstructions,
name: AgentName,
description: AgentDescription),
_ => throw new InvalidOperationException($"Unknown create mechanism: {createMechanism}")
};
@@ -85,9 +70,7 @@ public class AzureAIAgentsPersistentCreateTests
[Theory(Skip = "For manual testing only")]
[InlineData("CreateWithChatClientAgentOptionsAsync")]
[InlineData("CreateWithChatClientAgentOptionsSync")]
[InlineData("CreateWithFoundryOptionsAsync")]
[InlineData("CreateWithFoundryOptionsSync")]
public async Task CreateAgent_CreatesAgentWithVectorStoresAsync(string createMechanism)
{
// Arrange.
@@ -125,26 +108,11 @@ public class AzureAIAgentsPersistentCreateTests
Tools = [new HostedFileSearchTool() { Inputs = [new HostedVectorStoreContent(vectorStoreMetadata.Value.Id)] }]
}
}),
"CreateWithChatClientAgentOptionsSync" => this._persistentAgentsClient.CreateAIAgent(
s_config.DeploymentName,
options: new ChatClientAgentOptions()
{
ChatOptions = new()
{
Instructions = AgentInstructions,
Tools = [new HostedFileSearchTool() { Inputs = [new HostedVectorStoreContent(vectorStoreMetadata.Value.Id)] }]
}
}),
"CreateWithFoundryOptionsAsync" => await this._persistentAgentsClient.CreateAIAgentAsync(
s_config.DeploymentName,
instructions: AgentInstructions,
tools: [new FileSearchToolDefinition()],
toolResources: new ToolResources() { FileSearch = new([vectorStoreMetadata.Value.Id], null) }),
"CreateWithFoundryOptionsSync" => this._persistentAgentsClient.CreateAIAgent(
s_config.DeploymentName,
instructions: AgentInstructions,
tools: [new FileSearchToolDefinition()],
toolResources: new ToolResources() { FileSearch = new([vectorStoreMetadata.Value.Id], null) }),
_ => throw new InvalidOperationException($"Unknown create mechanism: {createMechanism}")
};
@@ -167,9 +135,7 @@ public class AzureAIAgentsPersistentCreateTests
[Theory]
[InlineData("CreateWithChatClientAgentOptionsAsync")]
[InlineData("CreateWithChatClientAgentOptionsSync")]
[InlineData("CreateWithFoundryOptionsAsync")]
[InlineData("CreateWithFoundryOptionsSync")]
public async Task CreateAgent_CreatesAgentWithCodeInterpreterAsync(string createMechanism)
{
// Arrange.
@@ -205,26 +171,11 @@ public class AzureAIAgentsPersistentCreateTests
Tools = [new HostedCodeInterpreterTool() { Inputs = [new HostedFileContent(uploadedCodeFile.Id)] }]
}
}),
"CreateWithChatClientAgentOptionsSync" => this._persistentAgentsClient.CreateAIAgent(
s_config.DeploymentName,
options: new ChatClientAgentOptions()
{
ChatOptions = new()
{
Instructions = AgentInstructions,
Tools = [new HostedCodeInterpreterTool() { Inputs = [new HostedFileContent(uploadedCodeFile.Id)] }]
}
}),
"CreateWithFoundryOptionsAsync" => await this._persistentAgentsClient.CreateAIAgentAsync(
s_config.DeploymentName,
instructions: AgentInstructions,
tools: [new CodeInterpreterToolDefinition()],
toolResources: new ToolResources() { CodeInterpreter = toolResource }),
"CreateWithFoundryOptionsSync" => this._persistentAgentsClient.CreateAIAgent(
s_config.DeploymentName,
instructions: AgentInstructions,
tools: [new CodeInterpreterToolDefinition()],
toolResources: new ToolResources() { CodeInterpreter = toolResource }),
_ => throw new InvalidOperationException($"Unknown create mechanism: {createMechanism}")
};
@@ -246,7 +197,6 @@ public class AzureAIAgentsPersistentCreateTests
[Theory]
[InlineData("CreateWithChatClientAgentOptionsAsync")]
[InlineData("CreateWithChatClientAgentOptionsSync")]
public async Task CreateAgent_CreatesAgentWithAIFunctionToolsAsync(string createMechanism)
{
// Arrange.
@@ -267,16 +217,6 @@ public class AzureAIAgentsPersistentCreateTests
Tools = [weatherFunction]
}
}),
"CreateWithChatClientAgentOptionsSync" => this._persistentAgentsClient.CreateAIAgent(
s_config.DeploymentName,
options: new ChatClientAgentOptions()
{
ChatOptions = new()
{
Instructions = AgentInstructions,
Tools = [weatherFunction]
}
}),
_ => throw new InvalidOperationException($"Unknown create mechanism: {createMechanism}")
};
@@ -18,44 +18,6 @@ namespace Microsoft.Agents.AI.AzureAI.Persistent.UnitTests.Extensions;
public sealed class PersistentAgentsClientExtensionsTests
{
/// <summary>
/// Verify that GetAIAgent throws ArgumentNullException when client is null.
/// </summary>
[Fact]
public void GetAIAgent_WithNullClient_ThrowsArgumentNullException()
{
// Act & Assert
var exception = Assert.Throws<ArgumentNullException>(() =>
((PersistentAgentsClient)null!).GetAIAgent("test-agent"));
Assert.Equal("persistentAgentsClient", exception.ParamName);
}
/// <summary>
/// Verify that GetAIAgent throws ArgumentException when agentId is null or whitespace.
/// </summary>
[Fact]
public void GetAIAgent_WithNullOrWhitespaceAgentId_ThrowsArgumentException()
{
// Arrange
var mockClient = new Mock<PersistentAgentsClient>();
// Act & Assert - null agentId
var exception1 = Assert.Throws<ArgumentException>(() =>
mockClient.Object.GetAIAgent(null!));
Assert.Equal("agentId", exception1.ParamName);
// Act & Assert - empty agentId
var exception2 = Assert.Throws<ArgumentException>(() =>
mockClient.Object.GetAIAgent(""));
Assert.Equal("agentId", exception2.ParamName);
// Act & Assert - whitespace agentId
var exception3 = Assert.Throws<ArgumentException>(() =>
mockClient.Object.GetAIAgent(" "));
Assert.Equal("agentId", exception3.ParamName);
}
/// <summary>
/// Verify that GetAIAgentAsync throws ArgumentNullException when client is null.
/// </summary>
@@ -94,19 +56,6 @@ public sealed class PersistentAgentsClientExtensionsTests
Assert.Equal("agentId", exception3.ParamName);
}
/// <summary>
/// Verify that CreateAIAgent throws ArgumentNullException when client is null.
/// </summary>
[Fact]
public void CreateAIAgent_WithNullClient_ThrowsArgumentNullException()
{
// Act & Assert
var exception = Assert.Throws<ArgumentNullException>(() =>
((PersistentAgentsClient)null!).CreateAIAgent("test-model"));
Assert.Equal("persistentAgentsClient", exception.ParamName);
}
/// <summary>
/// Verify that CreateAIAgentAsync throws ArgumentNullException when client is null.
/// </summary>
@@ -124,14 +73,14 @@ public sealed class PersistentAgentsClientExtensionsTests
/// Verify that GetAIAgent with clientFactory parameter correctly applies the factory.
/// </summary>
[Fact]
public void GetAIAgent_WithClientFactory_AppliesFactoryCorrectly()
public async Task GetAIAgentAsync_WithClientFactory_AppliesFactoryCorrectlyAsync()
{
// Arrange
var client = CreateFakePersistentAgentsClient();
TestChatClient? testChatClient = null;
// Act
var agent = client.GetAIAgent(
var agent = await client.GetAIAgentAsync(
agentId: "test-agent-id",
clientFactory: (innerClient) => testChatClient = new TestChatClient(innerClient));
@@ -146,13 +95,13 @@ public sealed class PersistentAgentsClientExtensionsTests
/// Verify that GetAIAgent without clientFactory works normally.
/// </summary>
[Fact]
public void GetAIAgent_WithoutClientFactory_WorksNormally()
public async Task GetAIAgentAsync_WithoutClientFactory_WorksNormallyAsync()
{
// Arrange
var client = CreateFakePersistentAgentsClient();
// Act
var agent = client.GetAIAgent(agentId: "test-agent-id");
var agent = await client.GetAIAgentAsync(agentId: "test-agent-id");
// Assert
Assert.NotNull(agent);
@@ -164,13 +113,13 @@ public sealed class PersistentAgentsClientExtensionsTests
/// Verify that GetAIAgent with null clientFactory works normally.
/// </summary>
[Fact]
public void GetAIAgent_WithNullClientFactory_WorksNormally()
public async Task GetAIAgentAsync_WithNullClientFactory_WorksNormallyAsync()
{
// Arrange
PersistentAgentsClient client = CreateFakePersistentAgentsClient();
// Act
var agent = client.GetAIAgent(agentId: "test-agent-id", clientFactory: null);
var agent = await client.GetAIAgentAsync(agentId: "test-agent-id", clientFactory: null);
// Assert
Assert.NotNull(agent);
@@ -178,29 +127,6 @@ public sealed class PersistentAgentsClientExtensionsTests
Assert.Null(retrievedTestClient);
}
/// <summary>
/// Verify that CreateAIAgent with clientFactory parameter correctly applies the factory.
/// </summary>
[Fact]
public void CreateAIAgent_WithClientFactory_AppliesFactoryCorrectly()
{
// Arrange
// Arrange
var client = CreateFakePersistentAgentsClient();
TestChatClient? testChatClient = null;
// Act
var agent = client.CreateAIAgent(
model: "test-model",
clientFactory: (innerClient) => testChatClient = new TestChatClient(innerClient));
// Assert
Assert.NotNull(agent);
var retrievedTestClient = agent.GetService<TestChatClient>();
Assert.NotNull(retrievedTestClient);
Assert.Same(testChatClient, retrievedTestClient);
}
/// <summary>
/// Verify that CreateAIAgentAsync with clientFactory parameter correctly applies the factory.
/// </summary>
@@ -223,42 +149,6 @@ public sealed class PersistentAgentsClientExtensionsTests
Assert.Same(testChatClient, retrievedTestClient);
}
/// <summary>
/// Verify that CreateAIAgent without clientFactory works normally.
/// </summary>
[Fact]
public void CreateAIAgent_WithoutClientFactory_WorksNormally()
{
// Arrange
var client = CreateFakePersistentAgentsClient();
// Act
var agent = client.CreateAIAgent(model: "test-model");
// Assert
Assert.NotNull(agent);
var retrievedTestClient = agent.GetService<TestChatClient>();
Assert.Null(retrievedTestClient);
}
/// <summary>
/// Verify that CreateAIAgent with null clientFactory works normally.
/// </summary>
[Fact]
public void CreateAIAgent_WithNullClientFactory_WorksNormally()
{
// Arrange
var client = CreateFakePersistentAgentsClient();
// Act
var agent = client.CreateAIAgent(model: "test-model", clientFactory: null);
// Assert
Assert.NotNull(agent);
var retrievedTestClient = agent.GetService<TestChatClient>();
Assert.Null(retrievedTestClient);
}
/// <summary>
/// Verify that CreateAIAgent without clientFactory works normally.
/// </summary>
@@ -372,33 +262,6 @@ public sealed class PersistentAgentsClientExtensionsTests
Assert.Equal("Original Instructions", agent.Instructions);
}
/// <summary>
/// Verify that GetAIAgent with agentId and options works correctly.
/// </summary>
[Fact]
public void GetAIAgent_WithAgentIdAndOptions_WorksCorrectly()
{
// Arrange
var client = CreateFakePersistentAgentsClient();
const string AgentId = "agent_abc123";
var options = new ChatClientAgentOptions
{
Name = "Override Name",
Description = "Override Description",
ChatOptions = new() { Instructions = "Override Instructions" }
};
// Act
var agent = client.GetAIAgent(AgentId, options);
// Assert
Assert.NotNull(agent);
Assert.Equal("Override Name", agent.Name);
Assert.Equal("Override Description", agent.Description);
Assert.Equal("Override Instructions", agent.Instructions);
}
/// <summary>
/// Verify that GetAIAgentAsync with agentId and options works correctly.
/// </summary>
@@ -509,23 +372,6 @@ public sealed class PersistentAgentsClientExtensionsTests
Assert.Equal("options", exception.ParamName);
}
/// <summary>
/// Verify that GetAIAgent throws ArgumentException when agentId is empty.
/// </summary>
[Fact]
public void GetAIAgent_WithOptionsAndEmptyAgentId_ThrowsArgumentException()
{
// Arrange
var client = CreateFakePersistentAgentsClient();
var options = new ChatClientAgentOptions();
// Act & Assert
var exception = Assert.Throws<ArgumentException>(() =>
client.GetAIAgent(string.Empty, options));
Assert.Equal("agentId", exception.ParamName);
}
/// <summary>
/// Verify that GetAIAgentAsync throws ArgumentException when agentId is empty.
/// </summary>
@@ -543,33 +389,6 @@ public sealed class PersistentAgentsClientExtensionsTests
Assert.Equal("agentId", exception.ParamName);
}
/// <summary>
/// Verify that CreateAIAgent with options works correctly.
/// </summary>
[Fact]
public void CreateAIAgent_WithOptions_WorksCorrectly()
{
// Arrange
var client = CreateFakePersistentAgentsClient();
const string Model = "test-model";
var options = new ChatClientAgentOptions
{
Name = "Test Agent",
Description = "Test description",
ChatOptions = new() { Instructions = "Test instructions" }
};
// Act
var agent = client.CreateAIAgent(Model, options);
// Assert
Assert.NotNull(agent);
Assert.Equal("Test Agent", agent.Name);
Assert.Equal("Test description", agent.Description);
Assert.Equal("Test instructions", agent.Instructions);
}
/// <summary>
/// Verify that CreateAIAgentAsync with options works correctly.
/// </summary>
@@ -597,38 +416,6 @@ public sealed class PersistentAgentsClientExtensionsTests
Assert.Equal("Test instructions", agent.Instructions);
}
/// <summary>
/// Verify that CreateAIAgent with options and clientFactory applies the factory correctly.
/// </summary>
[Fact]
public void CreateAIAgent_WithOptionsAndClientFactory_AppliesFactoryCorrectly()
{
// Arrange
var client = CreateFakePersistentAgentsClient();
TestChatClient? testChatClient = null;
const string Model = "test-model";
var options = new ChatClientAgentOptions
{
Name = "Test Agent"
};
// Act
var agent = client.CreateAIAgent(
Model,
options,
clientFactory: (innerClient) => testChatClient = new TestChatClient(innerClient));
// Assert
Assert.NotNull(agent);
Assert.Equal("Test Agent", agent.Name);
// Verify that the custom chat client can be retrieved from the agent's service collection
var retrievedTestClient = agent.GetService<TestChatClient>();
Assert.NotNull(retrievedTestClient);
Assert.Same(testChatClient, retrievedTestClient);
}
/// <summary>
/// Verify that CreateAIAgentAsync with options and clientFactory applies the factory correctly.
/// </summary>
@@ -661,22 +448,6 @@ public sealed class PersistentAgentsClientExtensionsTests
Assert.Same(testChatClient, retrievedTestClient);
}
/// <summary>
/// Verify that CreateAIAgent throws ArgumentNullException when options is null.
/// </summary>
[Fact]
public void CreateAIAgent_WithNullOptions_ThrowsArgumentNullException()
{
// Arrange
var client = CreateFakePersistentAgentsClient();
// Act & Assert
var exception = Assert.Throws<ArgumentNullException>(() =>
client.CreateAIAgent("test-model", (ChatClientAgentOptions)null!));
Assert.Equal("options", exception.ParamName);
}
/// <summary>
/// Verify that CreateAIAgentAsync throws ArgumentNullException when options is null.
/// </summary>
@@ -693,23 +464,6 @@ public sealed class PersistentAgentsClientExtensionsTests
Assert.Equal("options", exception.ParamName);
}
/// <summary>
/// Verify that CreateAIAgent throws ArgumentException when model is empty.
/// </summary>
[Fact]
public void CreateAIAgent_WithEmptyModel_ThrowsArgumentException()
{
// Arrange
var client = CreateFakePersistentAgentsClient();
var options = new ChatClientAgentOptions();
// Act & Assert
var exception = Assert.Throws<ArgumentException>(() =>
client.CreateAIAgent(string.Empty, options));
Assert.Equal("model", exception.ParamName);
}
/// <summary>
/// Verify that CreateAIAgentAsync throws ArgumentException when model is empty.
/// </summary>
@@ -727,35 +481,6 @@ public sealed class PersistentAgentsClientExtensionsTests
Assert.Equal("model", exception.ParamName);
}
/// <summary>
/// Verify that CreateAIAgent with services parameter correctly passes it through to the ChatClientAgent.
/// </summary>
[Fact]
public void CreateAIAgent_WithServices_PassesServicesToAgent()
{
// Arrange
var client = CreateFakePersistentAgentsClient();
var serviceProvider = new TestServiceProvider();
const string Model = "test-model";
// Act
var agent = client.CreateAIAgent(
Model,
instructions: "Test instructions",
name: "Test Agent",
services: serviceProvider);
// Assert
Assert.NotNull(agent);
// Verify the IServiceProvider was passed through to the FunctionInvokingChatClient
var chatClient = agent.GetService<IChatClient>();
Assert.NotNull(chatClient);
var functionInvokingClient = chatClient.GetService<FunctionInvokingChatClient>();
Assert.NotNull(functionInvokingClient);
Assert.Same(serviceProvider, GetFunctionInvocationServices(functionInvokingClient));
}
/// <summary>
/// Verify that CreateAIAgentAsync with services parameter correctly passes it through to the ChatClientAgent.
/// </summary>
@@ -785,30 +510,6 @@ public sealed class PersistentAgentsClientExtensionsTests
Assert.Same(serviceProvider, GetFunctionInvocationServices(functionInvokingClient));
}
/// <summary>
/// Verify that GetAIAgent with services parameter correctly passes it through to the ChatClientAgent.
/// </summary>
[Fact]
public void GetAIAgent_WithServices_PassesServicesToAgent()
{
// Arrange
var client = CreateFakePersistentAgentsClient();
var serviceProvider = new TestServiceProvider();
// Act
var agent = client.GetAIAgent("agent_abc123", services: serviceProvider);
// Assert
Assert.NotNull(agent);
// Verify the IServiceProvider was passed through to the FunctionInvokingChatClient
var chatClient = agent.GetService<IChatClient>();
Assert.NotNull(chatClient);
var functionInvokingClient = chatClient.GetService<FunctionInvokingChatClient>();
Assert.NotNull(functionInvokingClient);
Assert.Same(serviceProvider, GetFunctionInvocationServices(functionInvokingClient));
}
/// <summary>
/// Verify that GetAIAgentAsync with services parameter correctly passes it through to the ChatClientAgent.
/// </summary>
@@ -837,7 +538,7 @@ public sealed class PersistentAgentsClientExtensionsTests
/// Verify that CreateAIAgent with both clientFactory and services works correctly.
/// </summary>
[Fact]
public void CreateAIAgent_WithClientFactoryAndServices_AppliesBothCorrectly()
public async Task CreateAIAgentAsync_WithClientFactoryAndServices_AppliesBothCorrectlyAsync()
{
// Arrange
var client = CreateFakePersistentAgentsClient();
@@ -846,7 +547,7 @@ public sealed class PersistentAgentsClientExtensionsTests
const string Model = "test-model";
// Act
var agent = client.CreateAIAgent(
var agent = await client.CreateAIAgentAsync(
Model,
instructions: "Test instructions",
name: "Test Agent",
@@ -23,7 +23,7 @@ public sealed class OpenAIAssistantClientExtensionsTests
/// Verify that CreateAIAgent with clientFactory parameter correctly applies the factory.
/// </summary>
[Fact]
public void CreateAIAgent_WithClientFactory_AppliesFactoryCorrectly()
public async Task CreateAIAgentAsync_WithClientFactory_AppliesFactoryCorrectlyAsync()
{
// Arrange
var assistantClient = new TestAssistantClient();
@@ -31,7 +31,7 @@ public sealed class OpenAIAssistantClientExtensionsTests
const string ModelId = "test-model";
// Act
var agent = assistantClient.CreateAIAgent(
var agent = await assistantClient.CreateAIAgentAsync(
ModelId,
instructions: "Test instructions",
name: "Test Agent",
@@ -53,7 +53,7 @@ public sealed class OpenAIAssistantClientExtensionsTests
/// Verify that CreateAIAgent with clientFactory using AsBuilder pattern works correctly.
/// </summary>
[Fact]
public void CreateAIAgent_WithClientFactoryUsingAsBuilder_AppliesFactoryCorrectly()
public async Task CreateAIAgentAsync_WithClientFactoryUsingAsBuilder_AppliesFactoryCorrectlyAsync()
{
// Arrange
var assistantClient = new TestAssistantClient();
@@ -62,7 +62,7 @@ public sealed class OpenAIAssistantClientExtensionsTests
const string ModelId = "test-model";
// Act
var agent = assistantClient.CreateAIAgent(
var agent = await assistantClient.CreateAIAgentAsync(
ModelId,
instructions: "Test instructions",
clientFactory: (innerClient) =>
@@ -83,7 +83,7 @@ public sealed class OpenAIAssistantClientExtensionsTests
/// Verify that CreateAIAgent with options and clientFactory parameter correctly applies the factory.
/// </summary>
[Fact]
public void CreateAIAgent_WithOptionsAndClientFactory_AppliesFactoryCorrectly()
public async Task CreateAIAgentAsync_WithOptionsAndClientFactory_AppliesFactoryCorrectlyAsync()
{
// Arrange
var assistantClient = new TestAssistantClient();
@@ -97,7 +97,7 @@ public sealed class OpenAIAssistantClientExtensionsTests
};
// Act
var agent = assistantClient.CreateAIAgent(
var agent = await assistantClient.CreateAIAgentAsync(
ModelId,
options,
clientFactory: (innerClient) => testChatClient);
@@ -117,14 +117,14 @@ public sealed class OpenAIAssistantClientExtensionsTests
/// Verify that CreateAIAgent without clientFactory works normally.
/// </summary>
[Fact]
public void CreateAIAgent_WithoutClientFactory_WorksNormally()
public async Task CreateAIAgentAsync_WithoutClientFactory_WorksNormallyAsync()
{
// Arrange
var assistantClient = new TestAssistantClient();
const string ModelId = "test-model";
// Act
var agent = assistantClient.CreateAIAgent(
var agent = await assistantClient.CreateAIAgentAsync(
ModelId,
instructions: "Test instructions",
name: "Test Agent");
@@ -142,14 +142,14 @@ public sealed class OpenAIAssistantClientExtensionsTests
/// Verify that CreateAIAgent with null clientFactory works normally.
/// </summary>
[Fact]
public void CreateAIAgent_WithNullClientFactory_WorksNormally()
public async Task CreateAIAgentAsync_WithNullClientFactory_WorksNormallyAsync()
{
// Arrange
var assistantClient = new TestAssistantClient();
const string ModelId = "test-model";
// Act
var agent = assistantClient.CreateAIAgent(
var agent = await assistantClient.CreateAIAgentAsync(
ModelId,
instructions: "Test instructions",
name: "Test Agent",
@@ -168,11 +168,11 @@ public sealed class OpenAIAssistantClientExtensionsTests
/// Verify that CreateAIAgent throws ArgumentNullException when client is null.
/// </summary>
[Fact]
public void CreateAIAgent_WithNullClient_ThrowsArgumentNullException()
public async Task CreateAIAgentAsync_WithNullClient_ThrowsArgumentNullExceptionAsync()
{
// Act & Assert
var exception = Assert.Throws<ArgumentNullException>(() =>
((AssistantClient)null!).CreateAIAgent("test-model"));
var exception = await Assert.ThrowsAsync<ArgumentNullException>(() =>
((AssistantClient)null!).CreateAIAgentAsync("test-model"));
Assert.Equal("client", exception.ParamName);
}
@@ -181,14 +181,14 @@ public sealed class OpenAIAssistantClientExtensionsTests
/// Verify that CreateAIAgent throws ArgumentNullException when model is null.
/// </summary>
[Fact]
public void CreateAIAgent_WithNullModel_ThrowsArgumentNullException()
public async Task CreateAIAgentAsync_WithNullModel_ThrowsArgumentNullExceptionAsync()
{
// Arrange
var assistantClient = new TestAssistantClient();
// Act & Assert
var exception = Assert.Throws<ArgumentNullException>(() =>
assistantClient.CreateAIAgent(null!));
var exception = await Assert.ThrowsAsync<ArgumentNullException>(() =>
assistantClient.CreateAIAgentAsync(null!));
Assert.Equal("model", exception.ParamName);
}
@@ -197,14 +197,14 @@ public sealed class OpenAIAssistantClientExtensionsTests
/// Verify that CreateAIAgent with options throws ArgumentNullException when options is null.
/// </summary>
[Fact]
public void CreateAIAgent_WithNullOptions_ThrowsArgumentNullException()
public async Task CreateAIAgentAsync_WithNullOptions_ThrowsArgumentNullExceptionAsync()
{
// Arrange
var assistantClient = new TestAssistantClient();
// Act & Assert
var exception = Assert.Throws<ArgumentNullException>(() =>
assistantClient.CreateAIAgent("test-model", (ChatClientAgentOptions)null!));
var exception = await Assert.ThrowsAsync<ArgumentNullException>(() =>
assistantClient.CreateAIAgentAsync("test-model", (ChatClientAgentOptions)null!));
Assert.Equal("options", exception.ParamName);
}
@@ -286,33 +286,6 @@ public sealed class OpenAIAssistantClientExtensionsTests
Assert.Equal("Original Instructions", agent.Instructions);
}
/// <summary>
/// Verify that GetAIAgent with agentId and options works correctly.
/// </summary>
[Fact]
public void GetAIAgent_WithAgentIdAndOptions_WorksCorrectly()
{
// Arrange
var assistantClient = new TestAssistantClient();
const string AgentId = "asst_abc123";
var options = new ChatClientAgentOptions
{
Name = "Override Name",
Description = "Override Description",
ChatOptions = new() { Instructions = "Override Instructions" }
};
// Act
var agent = assistantClient.GetAIAgent(AgentId, options);
// Assert
Assert.NotNull(agent);
Assert.Equal("Override Name", agent.Name);
Assert.Equal("Override Description", agent.Description);
Assert.Equal("Override Instructions", agent.Instructions);
}
/// <summary>
/// Verify that GetAIAgentAsync with agentId and options works correctly.
/// </summary>
@@ -423,23 +396,6 @@ public sealed class OpenAIAssistantClientExtensionsTests
Assert.Equal("options", exception.ParamName);
}
/// <summary>
/// Verify that GetAIAgent throws ArgumentException when agentId is empty.
/// </summary>
[Fact]
public void GetAIAgent_WithEmptyAgentId_ThrowsArgumentException()
{
// Arrange
var assistantClient = new TestAssistantClient();
var options = new ChatClientAgentOptions();
// Act & Assert
var exception = Assert.Throws<ArgumentException>(() =>
assistantClient.GetAIAgent(string.Empty, options));
Assert.Equal("agentId", exception.ParamName);
}
/// <summary>
/// Verify that GetAIAgentAsync throws ArgumentException when agentId is empty.
/// </summary>
@@ -461,7 +417,7 @@ public sealed class OpenAIAssistantClientExtensionsTests
/// Verify that CreateAIAgent with services parameter correctly passes it through to the ChatClientAgent.
/// </summary>
[Fact]
public void CreateAIAgent_WithServices_PassesServicesToAgent()
public async Task CreateAIAgentAsync_WithServices_PassesServicesToAgentAsync()
{
// Arrange
var assistantClient = new TestAssistantClient();
@@ -469,7 +425,7 @@ public sealed class OpenAIAssistantClientExtensionsTests
const string ModelId = "test-model";
// Act
var agent = assistantClient.CreateAIAgent(
var agent = await assistantClient.CreateAIAgentAsync(
ModelId,
instructions: "Test instructions",
name: "Test Agent",
@@ -490,7 +446,7 @@ public sealed class OpenAIAssistantClientExtensionsTests
/// Verify that CreateAIAgent with options and services parameter correctly passes it through to the ChatClientAgent.
/// </summary>
[Fact]
public void CreateAIAgent_WithOptionsAndServices_PassesServicesToAgent()
public async Task CreateAIAgentAsync_WithOptionsAndServices_PassesServicesToAgentAsync()
{
// Arrange
var assistantClient = new TestAssistantClient();
@@ -503,7 +459,7 @@ public sealed class OpenAIAssistantClientExtensionsTests
};
// Act
var agent = assistantClient.CreateAIAgent(ModelId, options, services: serviceProvider);
var agent = await assistantClient.CreateAIAgentAsync(ModelId, options, services: serviceProvider);
// Assert
Assert.NotNull(agent);
@@ -570,7 +526,7 @@ public sealed class OpenAIAssistantClientExtensionsTests
/// Verify that CreateAIAgent with both clientFactory and services works correctly.
/// </summary>
[Fact]
public void CreateAIAgent_WithClientFactoryAndServices_AppliesBothCorrectly()
public async Task CreateAIAgentAsync_WithClientFactoryAndServices_AppliesBothCorrectlyAsync()
{
// Arrange
var assistantClient = new TestAssistantClient();
@@ -579,7 +535,7 @@ public sealed class OpenAIAssistantClientExtensionsTests
const string ModelId = "test-model";
// Act
var agent = assistantClient.CreateAIAgent(
var agent = await assistantClient.CreateAIAgentAsync(
ModelId,
instructions: "Test instructions",
name: "Test Agent",
@@ -622,14 +578,9 @@ public sealed class OpenAIAssistantClientExtensionsTests
{
}
public override ClientResult<Assistant> CreateAssistant(string model, AssistantCreationOptions? options = null, CancellationToken cancellationToken = default)
public override Task<ClientResult<Assistant>> CreateAssistantAsync(string model, AssistantCreationOptions? options = null, CancellationToken cancellationToken = default)
{
return ClientResult.FromValue(ModelReaderWriter.Read<Assistant>(BinaryData.FromString("""{"id": "asst_abc123"}""")), new FakePipelineResponse())!;
}
public override ClientResult<Assistant> GetAssistant(string assistantId, CancellationToken cancellationToken = default)
{
return ClientResult.FromValue(ModelReaderWriter.Read<Assistant>(BinaryData.FromString("""{"id": "asst_abc123", "name": "Original Name", "description": "Original Description", "instructions": "Original Instructions"}""")), new FakePipelineResponse())!;
return Task.FromResult<ClientResult<Assistant>>(ClientResult.FromValue(ModelReaderWriter.Read<Assistant>(BinaryData.FromString("""{"id": "asst_abc123"}""")), new FakePipelineResponse())!);
}
public override async Task<ClientResult<Assistant>> GetAssistantAsync(string assistantId, CancellationToken cancellationToken = default)
@@ -48,7 +48,7 @@ public class OpenAIAssistantClientExtensionsTests
Tools = [weatherFunction]
}
}),
"CreateWithChatClientAgentOptionsSync" => this._assistantClient.CreateAIAgent(
"CreateWithChatClientAgentOptionsSync" => await this._assistantClient.CreateAIAgentAsync(
model: s_config.ChatModelId!,
options: new ChatClientAgentOptions()
{
@@ -115,7 +115,7 @@ public class OpenAIAssistantClientExtensionsTests
Tools = [codeInterpreterTool]
}
}),
"CreateWithChatClientAgentOptionsSync" => this._assistantClient.CreateAIAgent(
"CreateWithChatClientAgentOptionsSync" => await this._assistantClient.CreateAIAgentAsync(
model: s_config.ChatModelId!,
options: new ChatClientAgentOptions()
{
@@ -193,7 +193,7 @@ public class OpenAIAssistantClientExtensionsTests
Tools = [fileSearchTool]
}
}),
"CreateWithChatClientAgentOptionsSync" => this._assistantClient.CreateAIAgent(
"CreateWithChatClientAgentOptionsSync" => await this._assistantClient.CreateAIAgentAsync(
model: s_config.ChatModelId!,
options: new ChatClientAgentOptions()
{