mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
.NET: Azure.AI.Agents Package Split + Initial Extensions (#1657)
* Move packages * Update nuget.config * Address Xmldoc * Remove format from branches checks * Address Xmldocs * Add more details to the implementation * Moving Agent logic to ChatClient * Adding Name and Id overrides to AzureAIAgent * Updating extensions * Add GetAiAgent extensions * Adding support for version as name can conflict 409 using the Agents API with same name * Addressing more updates to the extensions * More improvements * Remove debugging code from sample * Address copilot feedback * Apply suggestions from co-pilot code review
This commit is contained in:
@@ -7,7 +7,7 @@ name: dotnet-format
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
branches: ["main", "feature*"]
|
||||
branches: ["main"]
|
||||
paths:
|
||||
- dotnet/**
|
||||
- '.github/workflows/dotnet-format.yml'
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
<PackageVersion Include="Aspire.Microsoft.Azure.Cosmos" Version="$(AspireAppHostSdkVersion)" />
|
||||
<PackageVersion Include="CommunityToolkit.Aspire.OllamaSharp" Version="9.8.0" />
|
||||
<!-- Azure.* -->
|
||||
<PackageVersion Include="Azure.AI.Agents" Version="2.0.0-alpha.20251016.2" />
|
||||
<PackageVersion Include="Azure.AI.Agents.Persistent" Version="1.2.0-beta.7" />
|
||||
<PackageVersion Include="Azure.AI.Agents" Version="2.0.0-alpha.20251024.3" />
|
||||
<PackageVersion Include="Azure.AI.Agents.Persistent" Version="1.2.0-beta.6" />
|
||||
<PackageVersion Include="Azure.AI.OpenAI" Version="2.5.0-beta.1" />
|
||||
<PackageVersion Include="Azure.Identity" Version="1.17.0" />
|
||||
<PackageVersion Include="Azure.Monitor.OpenTelemetry.Exporter" Version="1.4.0" />
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
<Folder Name="/Samples/GettingStarted/AgentProviders/">
|
||||
<File Path="samples/GettingStarted/AgentProviders/README.md" />
|
||||
<Project Path="samples/GettingStarted/AgentProviders/Agent_With_A2A/Agent_With_A2A.csproj" />
|
||||
<Project Path="samples/GettingStarted/AgentProviders/Agent_With_AzureAIAgent/Agent_With_AzureAIAgent.csproj" />
|
||||
<Project Path="samples/GettingStarted/AgentProviders/Agent_With_AzureFoundryAgent/Agent_With_AzureFoundryAgent.csproj" />
|
||||
<Project Path="samples/GettingStarted/AgentProviders/Agent_With_AzureFoundryModel/Agent_With_AzureFoundryModel.csproj" />
|
||||
<Project Path="samples/GettingStarted/AgentProviders/Agent_With_AzureOpenAIChatCompletion/Agent_With_AzureOpenAIChatCompletion.csproj" />
|
||||
|
||||
+3
-3
@@ -3,14 +3,14 @@
|
||||
<packageSources>
|
||||
<clear />
|
||||
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
|
||||
<add key="local" value="C:\localpackages" />
|
||||
<add key="azure-sdk-for-net-pr" value="https://pkgs.dev.azure.com/azure-sdk/internal/_packaging/azure-sdk-for-net-pr/nuget/v3/index.json" />
|
||||
</packageSources>
|
||||
<packageSourceMapping>
|
||||
<packageSource key="nuget.org">
|
||||
<package pattern="*" />
|
||||
</packageSource>
|
||||
<packageSource key="local">
|
||||
<package pattern="*" />
|
||||
<packageSource key="azure-sdk-for-net-pr">
|
||||
<package pattern="Azure.AI.Agents" />
|
||||
</packageSource>
|
||||
</packageSourceMapping>
|
||||
</configuration>
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<NoWarn>$(NoWarn);IDE0059</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Azure.AI.Agents" />
|
||||
<PackageReference Include="Azure.Identity" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\..\src\Microsoft.Agents.AI.AzureAIAgents\Microsoft.Agents.AI.AzureAIAgents.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,35 @@
|
||||
// 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 JokerInstructions = "You are good at telling jokes.";
|
||||
const string JokerName = "JokerAgent";
|
||||
|
||||
// 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 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, 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);
|
||||
|
||||
// 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 by agent name (removes both agent versions created by existingAgent + createdAgent).
|
||||
await agentsClient.DeleteAgentAsync(agentVersion.Name);
|
||||
@@ -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
|
||||
```
|
||||
+1
-3
@@ -1,4 +1,4 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>$(ProjectsTargetFrameworks)</TargetFrameworks>
|
||||
@@ -11,9 +11,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Azure.AI.Agents.Persistent" />
|
||||
<PackageReference Include="Azure.AI.Agents" />
|
||||
<PackageReference Include="Microsoft.Extensions.AI" />
|
||||
<PackageReference Include="Microsoft.Extensions.AI.OpenAI" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -1,665 +0,0 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using Microsoft.Agents.AI;
|
||||
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 Azure.AI.Agents.Persistent;
|
||||
|
||||
/// <summary>
|
||||
/// Provides extension methods for <see cref="PersistentAgentsClient"/>.
|
||||
/// </summary>
|
||||
public static class AgentsClientExtensions
|
||||
{
|
||||
/*
|
||||
/// <summary>
|
||||
/// Gets a runnable agent instance from the provided response containing persistent agent metadata.
|
||||
/// </summary>
|
||||
/// <param name="client">The client used to interact with persistent agents. Cannot be <see langword="null"/>.</param>
|
||||
/// <param name="persistentAgentResponse">The response containing the persistent agent to be converted. Cannot be <see langword="null"/>.</param>
|
||||
/// <param name="chatOptions">The default <see cref="ChatOptions"/> to use when interacting with the agent.</param>
|
||||
/// <param name="clientFactory">Provides a way to customize the creation of the underlying <see cref="IChatClient"/> used by the agent.</param>
|
||||
/// <returns>A <see cref="ChatClientAgent"/> instance that can be used to perform operations on the persistent agent.</returns>
|
||||
public static ChatClientAgent GetAIAgent(this AgentsClient client, Response<PersistentAgent> persistentAgentResponse, ChatOptions? chatOptions = null, Func<IChatClient, IChatClient>? clientFactory = null)
|
||||
{
|
||||
if (persistentAgentResponse is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(persistentAgentResponse));
|
||||
}
|
||||
|
||||
return GetAIAgent(client, persistentAgentResponse.Value, chatOptions, clientFactory);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a runnable agent instance from a <see cref="PersistentAgent"/> containing metadata about a persistent agent.
|
||||
/// </summary>
|
||||
/// <param name="persistentAgentsClient">The client used to interact with persistent agents. Cannot be <see langword="null"/>.</param>
|
||||
/// <param name="persistentAgentMetadata">The persistent agent metadata to be converted. Cannot be <see langword="null"/>.</param>
|
||||
/// <param name="chatOptions">The default <see cref="ChatOptions"/> to use when interacting with the agent.</param>
|
||||
/// <param name="clientFactory">Provides a way to customize the creation of the underlying <see cref="IChatClient"/> used by the agent.</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, PersistentAgent persistentAgentMetadata, ChatOptions? chatOptions = null, Func<IChatClient, IChatClient>? 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);
|
||||
|
||||
if (clientFactory is not null)
|
||||
{
|
||||
chatClient = clientFactory(chatClient);
|
||||
}
|
||||
|
||||
return new ChatClientAgent(chatClient, options: new()
|
||||
{
|
||||
Id = persistentAgentMetadata.Id,
|
||||
Name = persistentAgentMetadata.Name,
|
||||
Description = persistentAgentMetadata.Description,
|
||||
Instructions = persistentAgentMetadata.Instructions,
|
||||
ChatOptions = chatOptions
|
||||
});
|
||||
}
|
||||
|
||||
/// <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="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,
|
||||
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.GetAIAgent(persistentAgentResponse, chatOptions, clientFactory);
|
||||
}
|
||||
|
||||
/// <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="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 async Task<ChatClientAgent> GetAIAgentAsync(
|
||||
this PersistentAgentsClient persistentAgentsClient,
|
||||
string agentId,
|
||||
ChatOptions? chatOptions = null,
|
||||
Func<IChatClient, IChatClient>? 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));
|
||||
}
|
||||
|
||||
var persistentAgentResponse = await persistentAgentsClient.Administration.GetAgentAsync(agentId, cancellationToken).ConfigureAwait(false);
|
||||
return persistentAgentsClient.GetAIAgent(persistentAgentResponse, chatOptions, clientFactory);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a runnable agent instance from the provided response containing persistent agent metadata.
|
||||
/// </summary>
|
||||
/// <param name="persistentAgentsClient">The client used to interact with persistent agents. Cannot be <see langword="null"/>.</param>
|
||||
/// <param name="persistentAgentResponse">The response containing the persistent agent to be converted. Cannot be <see langword="null"/>.</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>
|
||||
/// <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="persistentAgentResponse"/> or <paramref name="options"/> is <see langword="null"/>.</exception>
|
||||
public static ChatClientAgent GetAIAgent(this PersistentAgentsClient persistentAgentsClient, Response<PersistentAgent> persistentAgentResponse, ChatClientAgentOptions options, Func<IChatClient, IChatClient>? clientFactory = null)
|
||||
{
|
||||
if (persistentAgentResponse is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(persistentAgentResponse));
|
||||
}
|
||||
|
||||
return GetAIAgent(persistentAgentsClient, persistentAgentResponse.Value, options, clientFactory);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a runnable agent instance from a <see cref="PersistentAgent"/> containing metadata about a persistent agent.
|
||||
/// </summary>
|
||||
/// <param name="persistentAgentsClient">The client used to interact with persistent agents. Cannot be <see langword="null"/>.</param>
|
||||
/// <param name="persistentAgentMetadata">The persistent agent metadata to be converted. Cannot be <see langword="null"/>.</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>
|
||||
/// <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="persistentAgentMetadata"/> or <paramref name="options"/> is <see langword="null"/>.</exception>
|
||||
public static ChatClientAgent GetAIAgent(this PersistentAgentsClient persistentAgentsClient, PersistentAgent persistentAgentMetadata, ChatClientAgentOptions options, Func<IChatClient, IChatClient>? clientFactory = null)
|
||||
{
|
||||
if (persistentAgentMetadata is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(persistentAgentMetadata));
|
||||
}
|
||||
|
||||
if (persistentAgentsClient is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(persistentAgentsClient));
|
||||
}
|
||||
|
||||
if (options is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(options));
|
||||
}
|
||||
|
||||
var chatClient = persistentAgentsClient.AsIChatClient(persistentAgentMetadata.Id);
|
||||
|
||||
if (clientFactory is not null)
|
||||
{
|
||||
chatClient = clientFactory(chatClient);
|
||||
}
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
return new ChatClientAgent(chatClient, agentOptions);
|
||||
}
|
||||
|
||||
/// <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="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,
|
||||
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.GetAIAgent(persistentAgentResponse, options, clientFactory);
|
||||
}
|
||||
|
||||
/// <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="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 async Task<ChatClientAgent> GetAIAgentAsync(
|
||||
this PersistentAgentsClient persistentAgentsClient,
|
||||
string agentId,
|
||||
ChatClientAgentOptions options,
|
||||
Func<IChatClient, IChatClient>? 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));
|
||||
}
|
||||
|
||||
var persistentAgentResponse = await persistentAgentsClient.Administration.GetAgentAsync(agentId, cancellationToken).ConfigureAwait(false);
|
||||
return persistentAgentsClient.GetAIAgent(persistentAgentResponse, options, clientFactory);
|
||||
}*/
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new server side agent using the provided <see cref="PersistentAgentsClient"/>.
|
||||
/// </summary>
|
||||
/// <param name="client">The <see cref="AgentsClient"/> 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="instructions">The instructions for the agent.</param>
|
||||
/// <param name="tools">The tools to be used by the agent.</param>
|
||||
/// <param name="temperature">The temperature setting for the agent.</param>
|
||||
/// <param name="topP">The top-p setting for the agent.</param>
|
||||
/// <param name="raiConfig">The responsible AI config</param>
|
||||
/// <param name="reasoningOptions">The reasoning options for the agent.</param>
|
||||
/// <param name="textOptions">The text options for the agent.</param>
|
||||
/// <param name="structuredInputs">The structured inputs 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="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 async Task<ChatClientAgent> CreateAIAgentAsync(
|
||||
this AgentsClient client,
|
||||
string model,
|
||||
string? name = null,
|
||||
string? instructions = null,
|
||||
IEnumerable<ResponseTool>? tools = null,
|
||||
float? temperature = null,
|
||||
float? topP = null,
|
||||
RaiConfig? raiConfig = null,
|
||||
ResponseReasoningOptions? reasoningOptions = null,
|
||||
ResponseTextOptions? textOptions = null,
|
||||
IDictionary<string, StructuredInputDefinition>? structuredInputs = null,
|
||||
IReadOnlyDictionary<string, string>? metadata = null,
|
||||
Func<IChatClient, IChatClient>? 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);
|
||||
|
||||
if (tools is not null)
|
||||
{
|
||||
if (promptAgentDefinition.Tools is List<ResponseTool> toolsList)
|
||||
{
|
||||
toolsList.AddRange(tools);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var tool in tools)
|
||||
{
|
||||
promptAgentDefinition.Tools.Add(tool);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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<ChatMessage> messages, AgentThread? thread, AgentRunOptions? options, Func<IEnumerable<ChatMessage>, 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;
|
||||
}
|
||||
|
||||
/// <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="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,
|
||||
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);
|
||||
}
|
||||
|
||||
/// <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="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,
|
||||
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.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, 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="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 async Task<ChatClientAgent> CreateAIAgentAsync(
|
||||
this PersistentAgentsClient persistentAgentsClient,
|
||||
string model,
|
||||
ChatClientAgentOptions options,
|
||||
Func<IChatClient, IChatClient>? 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));
|
||||
}
|
||||
|
||||
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))
|
||||
{
|
||||
options = options.Clone();
|
||||
options.ChatOptions!.Tools = toolDefinitionsAndResources.FunctionToolsAndOtherTools;
|
||||
}
|
||||
|
||||
// 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<ToolDefinition>? ToolDefinitions, ToolResources? ToolResources, List<AITool>? FunctionToolsAndOtherTools) ConvertAIToolsToToolDefinitions(IList<AITool>? tools)
|
||||
{
|
||||
List<ToolDefinition>? toolDefinitions = null;
|
||||
ToolResources? toolResources = null;
|
||||
List<AITool>? functionToolsAndOtherTools = null;
|
||||
|
||||
if (tools is not null)
|
||||
{
|
||||
foreach (AITool tool in tools)
|
||||
{
|
||||
switch (tool)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (toolDefinitions, toolResources, functionToolsAndOtherTools);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,893 @@
|
||||
// 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;
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// Provides extension methods for <see cref="AgentsClient"/>.
|
||||
/// </summary>
|
||||
public static class AgentsClientExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a runnable agent instance from the provided agent record.
|
||||
/// </summary>
|
||||
/// <param name="agentsClient">The client used to interact with Azure AI Agents. Cannot be <see langword="null"/>.</param>
|
||||
/// <param name="model">The model to be used by the agent.</param>
|
||||
/// <param name="agentRecord">The agent record to be converted. The latest version will be used. Cannot be <see langword="null"/>.</param>
|
||||
/// <param name="chatOptions">The default <see cref="ChatOptions"/> to use when interacting with 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="openAIClientOptions">An optional <see cref="OpenAIClientOptions"/> for configuring the underlying OpenAI client.</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 Azure AI Agent.</returns>
|
||||
public static ChatClientAgent GetAIAgent(
|
||||
this AgentsClient agentsClient,
|
||||
string model,
|
||||
AgentRecord agentRecord,
|
||||
ChatOptions? chatOptions = null,
|
||||
Func<IChatClient, IChatClient>? clientFactory = null,
|
||||
OpenAIClientOptions? openAIClientOptions = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
Throw.IfNull(agentsClient);
|
||||
Throw.IfNullOrWhitespace(model);
|
||||
Throw.IfNull(agentRecord);
|
||||
|
||||
return GetAIAgent(agentsClient, model, agentRecord.Versions.Latest, chatOptions, clientFactory, openAIClientOptions, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a runnable agent instance from the provided agent record.
|
||||
/// </summary>
|
||||
/// <param name="agentsClient">The client used to interact with Azure AI Agents. Cannot be <see langword="null"/>.</param>
|
||||
/// <param name="model">The model to be used by the agent. Cannot be <see langword="null"/>.</param>
|
||||
/// <param name="agentVersion">The agent version to be converted. Cannot be <see langword="null"/>.</param>
|
||||
/// <param name="chatOptions">The default <see cref="ChatOptions"/> to use when interacting with 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="openAIClientOptions">An optional <see cref="OpenAIClientOptions"/> for configuring the underlying OpenAI client.</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 specified version of the Azure AI Agent.</returns>
|
||||
/// <exception cref="ArgumentNullException">Thrown when <paramref name="agentsClient"/>, <paramref name="model"/>, or <paramref name="agentVersion"/> is <see langword="null"/>.</exception>
|
||||
public static ChatClientAgent GetAIAgent(
|
||||
this AgentsClient agentsClient,
|
||||
string model,
|
||||
AgentVersion agentVersion,
|
||||
ChatOptions? chatOptions = null,
|
||||
Func<IChatClient, IChatClient>? clientFactory = null,
|
||||
OpenAIClientOptions? openAIClientOptions = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
Throw.IfNull(agentsClient);
|
||||
Throw.IfNullOrWhitespace(model);
|
||||
Throw.IfNull(agentVersion);
|
||||
|
||||
return GetAIAgent(agentsClient, model, agentVersion, new ChatClientAgentOptions() { ChatOptions = chatOptions }, clientFactory, openAIClientOptions, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves an existing server side agent, wrapped as a <see cref="ChatClientAgent"/> using the provided <see cref="AgentsClient"/>.
|
||||
/// </summary>
|
||||
/// <param name="agentsClient">The <see cref="AgentsClient"/> to create the <see cref="ChatClientAgent"/> with. Cannot be <see langword="null"/>.</param>
|
||||
/// <param name="model">The model to be used by the agent. Cannot be <see langword="null"/> or whitespace.</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="chatOptions">The default <see cref="ChatOptions"/> to use when interacting with 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="openAIClientOptions">An optional <see cref="OpenAIClientOptions"/> for configuring the underlying OpenAI client.</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="agentsClient"/>, <paramref name="model"/>, or <paramref name="name"/> is <see langword="null"/>.</exception>
|
||||
/// <exception cref="ArgumentException">Thrown when <paramref name="model"/> or <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 AgentsClient agentsClient,
|
||||
string model,
|
||||
string name,
|
||||
ChatOptions? chatOptions = null,
|
||||
Func<IChatClient, IChatClient>? clientFactory = null,
|
||||
OpenAIClientOptions? openAIClientOptions = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
Throw.IfNull(agentsClient);
|
||||
Throw.IfNullOrWhitespace(model);
|
||||
Throw.IfNullOrWhitespace(name);
|
||||
|
||||
var agentRecord = agentsClient.GetAgent(name, cancellationToken)
|
||||
?? throw new InvalidOperationException($"Agent with name '{name}' not found.");
|
||||
|
||||
return GetAIAgent(agentsClient, model, agentRecord, chatOptions, clientFactory, openAIClientOptions, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves an existing server side agent, wrapped as a <see cref="ChatClientAgent"/> using the provided <see cref="AgentsClient"/>.
|
||||
/// </summary>
|
||||
/// <param name="agentsClient">The <see cref="AgentsClient"/> to create the <see cref="ChatClientAgent"/> with. Cannot be <see langword="null"/>.</param>
|
||||
/// <param name="model">The model to be used by the agent. Cannot be <see langword="null"/> or whitespace.</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="chatOptions">The default <see cref="ChatOptions"/> to use when interacting with 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="openAIClientOptions">An optional <see cref="OpenAIClientOptions"/> for configuring the underlying OpenAI client.</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="agentsClient"/>, <paramref name="model"/>, or <paramref name="name"/> is <see langword="null"/>.</exception>
|
||||
/// <exception cref="ArgumentException">Thrown when <paramref name="model"/> or <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 async Task<ChatClientAgent> GetAIAgentAsync(
|
||||
this AgentsClient agentsClient,
|
||||
string model,
|
||||
string name,
|
||||
ChatOptions? chatOptions = null,
|
||||
Func<IChatClient, IChatClient>? clientFactory = null,
|
||||
OpenAIClientOptions? openAIClientOptions = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
Throw.IfNull(agentsClient);
|
||||
Throw.IfNullOrWhitespace(model);
|
||||
Throw.IfNullOrWhitespace(name);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a runnable agent instance from a <see cref="AgentVersion"/> containing metadata about an Azure AI Agent.
|
||||
/// </summary>
|
||||
/// <param name="agentsClient">The client used to interact with Azure AI Agents. Cannot be <see langword="null"/>.</param>
|
||||
/// <param name="model">The model to be used by the agent.</param>
|
||||
/// <param name="agentRecord">The agent record to be converted. The latest version will be used. Cannot be <see langword="null"/>.</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="openAIClientOptions">An optional <see cref="OpenAIClientOptions"/> for configuring the underlying OpenAI client.</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 Azure AI Agent record.</returns>
|
||||
/// <exception cref="ArgumentNullException">Thrown when <paramref name="agentsClient"/>, <paramref name="model"/>, <paramref name="agentRecord"/> or <paramref name="options"/> is <see langword="null"/>.</exception>
|
||||
public static ChatClientAgent GetAIAgent(
|
||||
this AgentsClient agentsClient,
|
||||
string model,
|
||||
AgentRecord agentRecord,
|
||||
ChatClientAgentOptions? options = null,
|
||||
Func<IChatClient, IChatClient>? clientFactory = null,
|
||||
OpenAIClientOptions? openAIClientOptions = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
Throw.IfNull(agentsClient);
|
||||
Throw.IfNullOrWhitespace(model);
|
||||
Throw.IfNull(agentRecord);
|
||||
|
||||
return GetAIAgent(
|
||||
agentsClient,
|
||||
model,
|
||||
agentRecord.Versions.Latest,
|
||||
options,
|
||||
clientFactory,
|
||||
openAIClientOptions,
|
||||
cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a runnable agent instance from a <see cref="AgentVersion"/> containing metadata about an Azure AI Agent.
|
||||
/// </summary>
|
||||
/// <param name="agentsClient">The client used to interact with Azure AI Agents. Cannot be <see langword="null"/>.</param>
|
||||
/// <param name="model">The model to be used by the agent.</param>
|
||||
/// <param name="agentVersion">The agent version to be converted. Cannot be <see langword="null"/>.</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="openAIClientOptions">An optional <see cref="OpenAIClientOptions"/> for configuring the underlying OpenAI client.</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 provided version of the Azure AI Agent.</returns>
|
||||
/// <exception cref="ArgumentNullException">Thrown when <paramref name="agentsClient"/>, <paramref name="model"/>, <paramref name="agentVersion"/> or <paramref name="options"/> is <see langword="null"/>.</exception>
|
||||
public static ChatClientAgent GetAIAgent(
|
||||
this AgentsClient agentsClient,
|
||||
string model,
|
||||
AgentVersion agentVersion,
|
||||
ChatClientAgentOptions? options = null,
|
||||
Func<IChatClient, IChatClient>? clientFactory = null,
|
||||
OpenAIClientOptions? openAIClientOptions = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
Throw.IfNull(agentsClient);
|
||||
Throw.IfNullOrWhitespace(model);
|
||||
Throw.IfNull(agentVersion);
|
||||
IChatClient chatClient = new AzureAIAgentChatClient(agentsClient, agentVersion, model, openAIClientOptions);
|
||||
|
||||
if (clientFactory is not null)
|
||||
{
|
||||
chatClient = clientFactory(chatClient);
|
||||
}
|
||||
|
||||
ChatClientAgentOptions? agentOptions;
|
||||
|
||||
// If options are null, populate from agentRecord definition
|
||||
var version = agentVersion;
|
||||
|
||||
if (options is null)
|
||||
{
|
||||
agentOptions = new();
|
||||
agentOptions.Id = GetAgentId(agentVersion);
|
||||
agentOptions.Name = agentVersion.Name;
|
||||
|
||||
agentOptions.Description = version.Description;
|
||||
|
||||
if (version.Definition is PromptAgentDefinition promptDef && promptDef.Tools is { Count: > 0 })
|
||||
{
|
||||
agentOptions.ChatOptions = new ChatOptions();
|
||||
agentOptions.ChatOptions.Tools = [];
|
||||
agentOptions.Instructions = promptDef.Instructions;
|
||||
|
||||
foreach (var tool in promptDef.Tools)
|
||||
{
|
||||
agentOptions.ChatOptions.Tools.Add(tool);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 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 ?? 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,
|
||||
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 ??= [];
|
||||
|
||||
foreach (var tool in promptDef.Tools)
|
||||
{
|
||||
agentOptions.ChatOptions.Tools.Add(tool);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new ChatClientAgent(chatClient, agentOptions);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves an existing server side agent, wrapped as a <see cref="ChatClientAgent"/> using the provided <see cref="AgentsClient"/>.
|
||||
/// </summary>
|
||||
/// <param name="agentsClient">The <see cref="AgentsClient"/> to create the <see cref="ChatClientAgent"/> with.</param>
|
||||
/// <param name="model">The model to be used by the agent.</param>
|
||||
/// <param name="name">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="openAIClientOptions">An optional <see cref="OpenAIClientOptions"/> for configuring the underlying OpenAI client.</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 named the Azure AI Agent.</returns>
|
||||
/// <exception cref="ArgumentNullException">Thrown when <paramref name="agentsClient"/> or <paramref name="options"/> is <see langword="null"/>.</exception>
|
||||
/// <exception cref="ArgumentException">Thrown when <paramref name="name"/> is empty or whitespace.</exception>
|
||||
public static async Task<ChatClientAgent> GetAIAgentAsync(
|
||||
this AgentsClient agentsClient,
|
||||
string model,
|
||||
string name,
|
||||
ChatClientAgentOptions options,
|
||||
Func<IChatClient, IChatClient>? clientFactory = null,
|
||||
OpenAIClientOptions? openAIClientOptions = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
Throw.IfNull(agentsClient);
|
||||
Throw.IfNullOrWhitespace(name);
|
||||
Throw.IfNull(options);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new server side agent using the provided <see cref="AgentsClient"/>.
|
||||
/// </summary>
|
||||
/// <param name="agentsClient">The <see cref="AgentsClient"/> 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="instructions">The instructions for the agent.</param>
|
||||
/// <param name="description">The description for the agent.</param>
|
||||
/// <param name="tools">The tools to be used by the agent.</param>
|
||||
/// <param name="temperature">The temperature setting for the agent.</param>
|
||||
/// <param name="topP">The top-p setting for the agent.</param>
|
||||
/// <param name="raiConfig">The responsible AI configuration for the agent.</param>
|
||||
/// <param name="reasoningOptions">The reasoning options for the agent.</param>
|
||||
/// <param name="textOptions">The text options for the agent.</param>
|
||||
/// <param name="structuredInputs">The structured inputs for the agent.</param>
|
||||
/// <param name="metadata">The metadata for the agent.</param>
|
||||
/// <param name="clientFactory">A factory function to customize the creation of the chat client used by the agent.</param>
|
||||
/// <param name="openAIClientOptions">An optional <see cref="OpenAIClientOptions"/> for configuring the underlying OpenAI client.</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 AgentsClient agentsClient,
|
||||
string model,
|
||||
string name,
|
||||
string? instructions = null,
|
||||
string? description = null,
|
||||
IList<ResponseTool>? tools = null,
|
||||
float? temperature = null,
|
||||
float? topP = null,
|
||||
RaiConfig? raiConfig = null,
|
||||
ResponseReasoningOptions? reasoningOptions = null,
|
||||
ResponseTextOptions? textOptions = null,
|
||||
IDictionary<string, StructuredInputDefinition>? structuredInputs = null,
|
||||
IReadOnlyDictionary<string, string>? metadata = null,
|
||||
Func<IChatClient, IChatClient>? clientFactory = null,
|
||||
OpenAIClientOptions? openAIClientOptions = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
Throw.IfNull(agentsClient);
|
||||
Throw.IfNullOrWhitespace(model);
|
||||
Throw.IfNullOrWhitespace(name);
|
||||
|
||||
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);
|
||||
|
||||
if (clientFactory is not null)
|
||||
{
|
||||
chatClient = clientFactory(chatClient);
|
||||
}
|
||||
|
||||
return new ChatClientAgent(chatClient, chatClientAgentOptions);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new AI agent using the specified agent definition and optional configuration parameters.
|
||||
/// </summary>
|
||||
/// <param name="agentsClient">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="agentDefinition">The definition that specifies the configuration and behavior of the agent to create. Cannot be <see langword="null"/>.</param>
|
||||
/// <param name="model">The name of the model to use for the agent. Model must be provided either directly or as part of a <see cref="PromptAgentDefinition.Model"/> specialization property.</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="openAIClientOptions">An optional <see cref="OpenAIClientOptions"/> for configuring the underlying OpenAI client.</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="agentsClient"/> or <paramref name="agentDefinition"/> is <see langword="null"/>.</exception>
|
||||
/// <exception cref="ArgumentException">Thrown if neither the 'model' parameter nor a model in the agent definition is provided.</exception>
|
||||
public static ChatClientAgent CreateAIAgent(
|
||||
this AgentsClient agentsClient,
|
||||
string name,
|
||||
AgentDefinition agentDefinition,
|
||||
string? model = null,
|
||||
AgentCreationOptions? creationOptions = null,
|
||||
Func<IChatClient, IChatClient>? clientFactory = null,
|
||||
OpenAIClientOptions? openAIClientOptions = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
Throw.IfNull(agentsClient);
|
||||
Throw.IfNullOrWhitespace(name);
|
||||
Throw.IfNull(agentDefinition);
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
AgentRecord agentRecord = agentsClient.CreateAgent(name, agentDefinition, creationOptions, cancellationToken);
|
||||
IChatClient chatClient = new AzureAIAgentChatClient(agentsClient, agentRecord, model, openAIClientOptions);
|
||||
|
||||
if (clientFactory is not null)
|
||||
{
|
||||
chatClient = clientFactory(chatClient);
|
||||
}
|
||||
|
||||
return new ChatClientAgent(chatClient);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new Prompt AI Agent using the provided <see cref="AgentsClient"/> and options.
|
||||
/// </summary>
|
||||
/// <param name="agentsClient">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="openAIClientOptions">An optional <see cref="OpenAIClientOptions"/> for configuring the underlying OpenAI client.</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="agentsClient"/>, <paramref name="model"/>, 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 AgentsClient agentsClient,
|
||||
string model,
|
||||
ChatClientAgentOptions options,
|
||||
Func<IChatClient, IChatClient>? clientFactory = null,
|
||||
OpenAIClientOptions? openAIClientOptions = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
Throw.IfNull(agentsClient);
|
||||
Throw.IfNullOrWhitespace(model);
|
||||
Throw.IfNull(options);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(options.Name))
|
||||
{
|
||||
throw new ArgumentException("Agent name must be provided in the options.Name property", nameof(options));
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
agentDefinition.Tools.Add(ToResponseTool(tool, options.ChatOptions));
|
||||
}
|
||||
}
|
||||
|
||||
AgentVersion agentVersion = agentsClient.CreateAgentVersion(options.Name, agentDefinition, versionCreationOptions, cancellationToken);
|
||||
IChatClient chatClient = new AzureAIAgentChatClient(agentsClient, agentVersion, model, openAIClientOptions);
|
||||
|
||||
if (clientFactory is not null)
|
||||
{
|
||||
chatClient = clientFactory(chatClient);
|
||||
}
|
||||
|
||||
chatClientAgentOptions.Id = GetAgentId(agentVersion);
|
||||
|
||||
return new ChatClientAgent(chatClient, chatClientAgentOptions);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously creates a new AI agent using the specified agent definition and optional configuration
|
||||
/// parameters.
|
||||
/// </summary>
|
||||
/// <param name="agentsClient">The client used to manage and interact with AI agents. Cannot be <see langword="null"/>.</param>
|
||||
/// <param name="agentDefinition">The definition that specifies the configuration and behavior of the agent to create. Cannot be <see langword="null"/>.</param>
|
||||
/// <param name="model">The name of the model to use for the agent. If not specified, the model must be provided as part of the agent definition.</param>
|
||||
/// <param name="name">The name for the agent.</param>
|
||||
/// <param name="agentVersionCreationOptions">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="openAIClientOptions">An optional <see cref="OpenAIClientOptions"/> for configuring the underlying OpenAI client.</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="agentsClient"/> or <paramref name="agentDefinition"/> is <see langword="null"/>.</exception>
|
||||
/// <exception cref="ArgumentException">Thrown if neither the 'model' parameter nor a model in the agent definition is provided.</exception>
|
||||
public static async Task<ChatClientAgent> CreateAIAgentAsync(
|
||||
this AgentsClient agentsClient,
|
||||
AgentDefinition agentDefinition,
|
||||
string? model = null,
|
||||
string? name = null,
|
||||
AgentVersionCreationOptions? agentVersionCreationOptions = null,
|
||||
Func<IChatClient, IChatClient>? clientFactory = null,
|
||||
OpenAIClientOptions? openAIClientOptions = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
Throw.IfNull(agentsClient);
|
||||
Throw.IfNull(agentDefinition);
|
||||
|
||||
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, agentVersionCreationOptions, cancellationToken).ConfigureAwait(false);
|
||||
IChatClient chatClient = new AzureAIAgentChatClient(agentsClient, agentVersion, model, openAIClientOptions);
|
||||
|
||||
if (clientFactory is not null)
|
||||
{
|
||||
chatClient = clientFactory(chatClient);
|
||||
}
|
||||
|
||||
List<AITool>? aiTools = null;
|
||||
if (agentDefinition is PromptAgentDefinition { Tools: { 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
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new server side prompt agent using the provided <see cref="AgentsClient"/>.
|
||||
/// </summary>
|
||||
/// <param name="agentsClient">The <see cref="AgentsClient"/> 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="instructions">The instructions for the agent.</param>
|
||||
/// <param name="description">The description for the agent.</param>
|
||||
/// <param name="tools">The tools to be used by the agent.</param>
|
||||
/// <param name="temperature">The temperature setting for the agent.</param>
|
||||
/// <param name="topP">The top-p setting for the agent.</param>
|
||||
/// <param name="raiConfig">The responsible AI configuration for the agent.</param>
|
||||
/// <param name="reasoningOptions">The reasoning options for the agent.</param>
|
||||
/// <param name="textOptions">The text options for the agent.</param>
|
||||
/// <param name="structuredInputs">The structured inputs for the agent.</param>
|
||||
/// <param name="metadata">The metadata for the agent.</param>
|
||||
/// <param name="clientFactory">A factory function to customize the creation of the underlying <see cref="IChatClient"/> used by the agent.</param>
|
||||
/// <param name="openAIClientOptions">An optional <see cref="OpenAIClientOptions"/> for configuring the underlying OpenAI client.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> 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>
|
||||
public static async Task<ChatClientAgent> CreateAIAgentAsync(
|
||||
this AgentsClient agentsClient,
|
||||
string model,
|
||||
string name,
|
||||
string? instructions = null,
|
||||
string? description = null,
|
||||
IList<ResponseTool>? tools = null,
|
||||
float? temperature = null,
|
||||
float? topP = null,
|
||||
RaiConfig? raiConfig = null,
|
||||
ResponseReasoningOptions? reasoningOptions = null,
|
||||
ResponseTextOptions? textOptions = null,
|
||||
IDictionary<string, StructuredInputDefinition>? structuredInputs = null,
|
||||
IReadOnlyDictionary<string, string>? metadata = null,
|
||||
Func<IChatClient, IChatClient>? clientFactory = null,
|
||||
OpenAIClientOptions? openAIClientOptions = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
Throw.IfNull(agentsClient);
|
||||
Throw.IfNullOrWhitespace(model);
|
||||
|
||||
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);
|
||||
|
||||
if (clientFactory is not null)
|
||||
{
|
||||
chatClient = clientFactory(chatClient);
|
||||
}
|
||||
|
||||
chatClientAgentOptions.Id = GetAgentId(agentVersion);
|
||||
|
||||
return new ChatClientAgent(chatClient, chatClientAgentOptions);
|
||||
}
|
||||
|
||||
#region Private
|
||||
|
||||
private static (PromptAgentDefinition, AgentVersionCreationOptions?, ChatClientAgentOptions) CreatePromptAgentDefinitionAndOptions(
|
||||
string name,
|
||||
string model,
|
||||
string? instructions,
|
||||
string? description,
|
||||
float? temperature = null,
|
||||
float? topP = null,
|
||||
RaiConfig? raiConfig = null,
|
||||
ResponseReasoningOptions? reasoningOptions = null,
|
||||
ResponseTextOptions? textOptions = null,
|
||||
IList<ResponseTool>? tools = null,
|
||||
IDictionary<string, StructuredInputDefinition>? structuredInputs = null,
|
||||
IReadOnlyDictionary<string, string>? metadata = null)
|
||||
{
|
||||
PromptAgentDefinition promptAgentDefinition = new(model)
|
||||
{
|
||||
Model = model,
|
||||
Instructions = instructions,
|
||||
Temperature = temperature,
|
||||
TopP = topP,
|
||||
RaiConfig = raiConfig,
|
||||
ReasoningOptions = reasoningOptions,
|
||||
TextOptions = textOptions,
|
||||
};
|
||||
|
||||
var chatOptions = new ChatOptions()
|
||||
{
|
||||
TopP = topP,
|
||||
Temperature = temperature,
|
||||
Instructions = instructions,
|
||||
};
|
||||
|
||||
AgentVersionCreationOptions? versionCreationOptions = null;
|
||||
if (metadata is not null)
|
||||
{
|
||||
versionCreationOptions ??= new();
|
||||
foreach (var kvp in metadata)
|
||||
{
|
||||
versionCreationOptions.Metadata.Add(kvp.Key, kvp.Value);
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(description))
|
||||
{
|
||||
(versionCreationOptions ??= new()).Description = description;
|
||||
}
|
||||
|
||||
if (tools is { Count: > 0 })
|
||||
{
|
||||
chatOptions.Tools ??= [];
|
||||
|
||||
foreach (var tool in tools)
|
||||
{
|
||||
chatOptions.Tools.Add(tool);
|
||||
promptAgentDefinition.Tools.Add(tool);
|
||||
}
|
||||
}
|
||||
|
||||
if (structuredInputs is not null)
|
||||
{
|
||||
foreach (var kvp in structuredInputs)
|
||||
{
|
||||
promptAgentDefinition.StructuredInputs.Add(kvp.Key, kvp.Value);
|
||||
}
|
||||
}
|
||||
|
||||
var chatClientAgentOptions = new ChatClientAgentOptions()
|
||||
{
|
||||
Name = name,
|
||||
Instructions = instructions,
|
||||
Description = description,
|
||||
ChatOptions = chatOptions
|
||||
};
|
||||
|
||||
return (promptAgentDefinition, versionCreationOptions, chatClientAgentOptions);
|
||||
}
|
||||
|
||||
private static string GetAgentId(AgentVersion agentVersion)
|
||||
=> $"{agentVersion.Name}:{agentVersion.Id}";
|
||||
|
||||
#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
|
||||
|
||||
/// <summary>Key into AdditionalProperties used to store a strict option.</summary>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>Gets whether the properties specify that strict schema handling is desired.</summary>
|
||||
private static bool? HasStrict(IReadOnlyDictionary<string, object?>? additionalProperties) =>
|
||||
additionalProperties?.TryGetValue(StrictKey, out object? strictObj) is true &&
|
||||
strictObj is bool strictValue ?
|
||||
strictValue : null;
|
||||
|
||||
/// <summary>Extracts from an <see cref="AIFunctionDeclaration"/> the parameters and strictness setting for use with OpenAI's APIs.</summary>
|
||||
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));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the JSON schema transformer cache conforming to OpenAI <b>strict</b> / structured output restrictions per
|
||||
/// https://platform.openai.com/docs/guides/structured-outputs?api-mode=responses#supported-schemas.
|
||||
/// </summary>
|
||||
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<string> 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<string>()}{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<HostedVectorStoreContent>().Select(c => c.VectorStoreId) ?? [],
|
||||
fileSearchTool.MaximumResultCount);
|
||||
|
||||
case HostedCodeInterpreterTool codeTool:
|
||||
return ResponseTool.CreateCodeInterpreterTool(
|
||||
new CodeInterpreterToolContainer(codeTool.Inputs?.OfType<HostedFileContent>().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<string> toolNames, McpToolFilter filter)
|
||||
{
|
||||
foreach (var toolName in toolNames)
|
||||
{
|
||||
filter.ToolNames.Add(toolName);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Used to create the JSON payload for an OpenAI tool description.</summary>
|
||||
internal sealed class ToolJson
|
||||
{
|
||||
[JsonPropertyName("type")]
|
||||
public string Type { get; set; } = "object";
|
||||
|
||||
[JsonPropertyName("required")]
|
||||
public HashSet<string> Required { get; set; } = [];
|
||||
|
||||
[JsonPropertyName("properties")]
|
||||
public Dictionary<string, JsonElement> Properties { get; set; } = [];
|
||||
|
||||
[JsonPropertyName("additionalProperties")]
|
||||
public bool AdditionalProperties { get; set; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
/// <summary>Source-generated JSON type information for use by all OpenAI implementations.</summary>
|
||||
[JsonSourceGenerationOptions(JsonSerializerDefaults.Web,
|
||||
UseStringEnumConverter = true,
|
||||
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
|
||||
WriteIndented = true)]
|
||||
[JsonSerializable(typeof(AgentsClientExtensions.ToolJson))]
|
||||
internal sealed partial class AgentsClientJsonContext : JsonSerializerContext;
|
||||
@@ -0,0 +1,110 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
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.
|
||||
|
||||
namespace Microsoft.Agents.AI.AzureAIAgents;
|
||||
|
||||
/// <summary>
|
||||
/// Provides a chat client implementation that integrates with Azure AI Agents, enabling chat interactions using
|
||||
/// Azure-specific agent capabilities.
|
||||
/// </summary>
|
||||
internal sealed class AzureAIAgentChatClient : DelegatingChatClient
|
||||
{
|
||||
private readonly ChatClientMetadata? _metadata;
|
||||
private readonly AgentsClient _agentsClient;
|
||||
private readonly AgentVersion _agentVersion;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AzureAIAgentChatClient"/> class.
|
||||
/// </summary>
|
||||
/// <param name="agentsClient">An instance of <see cref="AgentsClient"/> to interact with Azure AI Agents services.</param>
|
||||
/// <param name="agentRecord">An instance of <see cref="AgentRecord"/> representing the specific agent to use.</param>
|
||||
/// <param name="model">The AI model to use for the chat client.</param>
|
||||
/// <param name="openAIClientOptions">An optional <see cref="OpenAIClientOptions"/> for configuring the underlying OpenAI client.</param>
|
||||
/// <remarks>
|
||||
/// The <see cref="IChatClient"/> provided should be decorated with a <see cref="AzureAIAgentChatClient"/> for proper functionality.
|
||||
/// </remarks>
|
||||
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._agentVersion = Throw.IfNull(agentVersion);
|
||||
this._metadata = new ChatClientMetadata("azure.ai.agents");
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override async Task<ChatResponse> GetResponseAsync(IEnumerable<ChatMessage> messages, ChatOptions? options = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var conversation = await this.GetOrCreateConversationAsync(messages, options, cancellationToken).ConfigureAwait(false);
|
||||
var conversationOptions = this.GetConversationEnabledChatOptions(options, conversation);
|
||||
|
||||
return await base.GetResponseAsync(messages, conversationOptions, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async override IAsyncEnumerable<ChatResponseUpdate> GetStreamingResponseAsync(IEnumerable<ChatMessage> messages, ChatOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default)
|
||||
{
|
||||
var conversation = await this.GetOrCreateConversationAsync(messages, options, cancellationToken).ConfigureAwait(false);
|
||||
var conversationOptions = this.GetConversationEnabledChatOptions(options, conversation);
|
||||
|
||||
await foreach (var chunk in base.GetStreamingResponseAsync(messages, conversationOptions, cancellationToken).ConfigureAwait(false))
|
||||
{
|
||||
yield return chunk;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<AgentConversation> GetOrCreateConversationAsync(IEnumerable<ChatMessage> messages, ChatOptions? options, CancellationToken cancellationToken)
|
||||
=> string.IsNullOrWhiteSpace(options?.ConversationId)
|
||||
? 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 (originalFactory?.Invoke(this) is not ResponseCreationOptions responseCreationOptions)
|
||||
{
|
||||
responseCreationOptions = new ResponseCreationOptions();
|
||||
}
|
||||
|
||||
responseCreationOptions.SetAgentReference(this._agentVersion.Name);
|
||||
responseCreationOptions.SetConversationReference(agentConversation);
|
||||
|
||||
return responseCreationOptions;
|
||||
};
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>$(ProjectsTargetFrameworks)</TargetFrameworks>
|
||||
<TargetFrameworks Condition="'$(Configuration)' == 'Debug'">$(ProjectsDebugTargetFrameworks)</TargetFrameworks>
|
||||
<VersionSuffix>alpha</VersionSuffix>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<InjectSharedThrow>true</InjectSharedThrow>
|
||||
</PropertyGroup>
|
||||
|
||||
<Import Project="$(RepoRoot)/dotnet/nuget/nuget-package.props" />
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Azure.AI.Agents" />
|
||||
<PackageReference Include="Microsoft.Extensions.AI" />
|
||||
<PackageReference Include="Microsoft.Extensions.AI.OpenAI" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Microsoft.Agents.AI\Microsoft.Agents.AI.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<!-- NuGet Package Settings -->
|
||||
<Title>Microsoft Agent Framework Azure AI Agents</Title>
|
||||
<Description>Provides Microsoft Agent Framework support for Azure AI Agents.</Description>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
@@ -68,7 +68,7 @@ public class OpenAIResponseFixture(bool store) : IChatClientAgentFixture
|
||||
string name = "HelpfulAssistant",
|
||||
string instructions = "You are a helpful assistant.",
|
||||
IList<AITool>? aiTools = null) =>
|
||||
new ChatClientAgent(
|
||||
new(
|
||||
this._openAIResponseClient.AsIChatClient(),
|
||||
options: new()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user