This commit is contained in:
Roger Barreto
2025-10-21 16:02:43 +01:00
Unverified
parent 33f84f9ed2
commit 2c5cf6c67b
5 changed files with 671 additions and 0 deletions
+1
View File
@@ -17,6 +17,7 @@
<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.OpenAI" Version="2.5.0-beta.1" />
<PackageVersion Include="Azure.Identity" Version="1.17.0" />
+2
View File
@@ -277,6 +277,7 @@
<Project Path="src/Microsoft.Agents.AI.A2A/Microsoft.Agents.AI.A2A.csproj" />
<Project Path="src/Microsoft.Agents.AI.Abstractions/Microsoft.Agents.AI.Abstractions.csproj" />
<Project Path="src/Microsoft.Agents.AI.AGUI/Microsoft.Agents.AI.AGUI.csproj" />
<Project Path="src/Microsoft.Agents.AI.AzureAI/Microsoft.Agents.AI.AzureAI.csproj" />
<Project Path="src/Microsoft.Agents.AI.AzureAI.Persistent/Microsoft.Agents.AI.AzureAI.Persistent.csproj" />
<Project Path="src/Microsoft.Agents.AI.CopilotStudio/Microsoft.Agents.AI.CopilotStudio.csproj" />
<Project Path="src/Microsoft.Agents.AI.Hosting.A2A.AspNetCore/Microsoft.Agents.AI.Hosting.A2A.AspNetCore.csproj" />
@@ -306,6 +307,7 @@
<Project Path="tests/Microsoft.Agents.AI.A2A.UnitTests/Microsoft.Agents.AI.A2A.UnitTests.csproj" />
<Project Path="tests/Microsoft.Agents.AI.AGUI.UnitTests/Microsoft.Agents.AI.AGUI.UnitTests.csproj" />
<Project Path="tests/Microsoft.Agents.AI.Abstractions.UnitTests/Microsoft.Agents.AI.Abstractions.UnitTests.csproj" />
<Project Path="tests/Microsoft.Agents.AI.AzureAI.UnitTests/Microsoft.Agents.AI.AzureAI.UnitTests.csproj" />
<Project Path="tests/Microsoft.Agents.AI.AzureAI.Persistent.UnitTests/Microsoft.Agents.AI.AzureAI.Persistent.UnitTests.csproj" />
<Project Path="tests/Microsoft.Agents.AI.Hosting.A2A.Tests/Microsoft.Agents.AI.Hosting.A2A.Tests.csproj" Id="2a1c544d-237d-4436-8732-ba0c447ac06b" />
<Project Path="tests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.UnitTests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.UnitTests.csproj" />
+4
View File
@@ -3,10 +3,14 @@
<packageSources>
<clear />
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
<add key="local" value="C:\localpackages" />
</packageSources>
<packageSourceMapping>
<packageSource key="nuget.org">
<package pattern="*" />
</packageSource>
<packageSource key="local">
<package pattern="*" />
</packageSource>
</packageSourceMapping>
</configuration>
@@ -11,7 +11,9 @@
<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>
@@ -0,0 +1,662 @@
// 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="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 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);
}
}