mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
8015e00f56
* Surface downstream experimental flags and remove unecessary suppressions * Fix format error * Fix file encoding. * Address PR comments.
161 lines
7.8 KiB
C#
161 lines
7.8 KiB
C#
// Copyright (c) Microsoft. All rights reserved.
|
|
|
|
using System.Diagnostics.CodeAnalysis;
|
|
using System.Runtime.CompilerServices;
|
|
using Azure.AI.Projects;
|
|
using Azure.AI.Projects.OpenAI;
|
|
using Microsoft.Extensions.AI;
|
|
using Microsoft.Shared.DiagnosticIds;
|
|
using Microsoft.Shared.Diagnostics;
|
|
using OpenAI.Responses;
|
|
|
|
namespace Microsoft.Agents.AI.AzureAI;
|
|
|
|
/// <summary>
|
|
/// Provides a chat client implementation that integrates with Azure AI Agents, enabling chat interactions using
|
|
/// Azure-specific agent capabilities.
|
|
/// </summary>
|
|
[Experimental(DiagnosticIds.Experiments.AIOpenAIResponses)]
|
|
internal sealed class AzureAIProjectChatClient : DelegatingChatClient
|
|
{
|
|
private readonly ChatClientMetadata? _metadata;
|
|
private readonly AIProjectClient _agentClient;
|
|
private readonly AgentVersion? _agentVersion;
|
|
private readonly AgentRecord? _agentRecord;
|
|
private readonly ChatOptions? _chatOptions;
|
|
private readonly AgentReference _agentReference;
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="AzureAIProjectChatClient"/> class.
|
|
/// </summary>
|
|
/// <param name="aiProjectClient">An instance of <see cref="AIProjectClient"/> to interact with Azure AI Agents services.</param>
|
|
/// <param name="agentReference">An instance of <see cref="AgentReference"/> representing the specific agent to use.</param>
|
|
/// <param name="defaultModelId">The default model to use for the agent, if applicable.</param>
|
|
/// <param name="chatOptions">An instance of <see cref="ChatOptions"/> representing the options on how the agent was predefined.</param>
|
|
/// <remarks>
|
|
/// The <see cref="IChatClient"/> provided should be decorated with a <see cref="AzureAIProjectChatClient"/> for proper functionality.
|
|
/// </remarks>
|
|
internal AzureAIProjectChatClient(AIProjectClient aiProjectClient, AgentReference agentReference, string? defaultModelId, ChatOptions? chatOptions)
|
|
: base(Throw.IfNull(aiProjectClient)
|
|
.GetProjectOpenAIClient()
|
|
.GetProjectResponsesClientForAgent(agentReference)
|
|
.AsIChatClient())
|
|
{
|
|
this._agentClient = aiProjectClient;
|
|
this._agentReference = Throw.IfNull(agentReference);
|
|
this._metadata = new ChatClientMetadata("azure.ai.agents", defaultModelId: defaultModelId);
|
|
this._chatOptions = chatOptions;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="AzureAIProjectChatClient"/> class.
|
|
/// </summary>
|
|
/// <param name="aiProjectClient">An instance of <see cref="AIProjectClient"/> 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="chatOptions">An instance of <see cref="ChatOptions"/> representing the options on how the agent was predefined.</param>
|
|
/// <remarks>
|
|
/// The <see cref="IChatClient"/> provided should be decorated with a <see cref="AzureAIProjectChatClient"/> for proper functionality.
|
|
/// </remarks>
|
|
internal AzureAIProjectChatClient(AIProjectClient aiProjectClient, AgentRecord agentRecord, ChatOptions? chatOptions)
|
|
: this(aiProjectClient, Throw.IfNull(agentRecord).Versions.Latest, chatOptions)
|
|
{
|
|
this._agentRecord = agentRecord;
|
|
}
|
|
|
|
internal AzureAIProjectChatClient(AIProjectClient aiProjectClient, AgentVersion agentVersion, ChatOptions? chatOptions)
|
|
: this(
|
|
aiProjectClient,
|
|
CreateAgentReference(Throw.IfNull(agentVersion)),
|
|
(agentVersion.Definition as PromptAgentDefinition)?.Model,
|
|
chatOptions)
|
|
{
|
|
this._agentVersion = agentVersion;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates an <see cref="AgentReference"/> from an <see cref="AgentVersion"/>.
|
|
/// Uses the agent version's version if available, otherwise defaults to "latest".
|
|
/// </summary>
|
|
/// <param name="agentVersion">The agent version to create a reference from.</param>
|
|
/// <returns>An <see cref="AgentReference"/> for the specified agent version.</returns>
|
|
private static AgentReference CreateAgentReference(AgentVersion agentVersion)
|
|
{
|
|
// If the version is null, empty, or whitespace, use "latest" as the default.
|
|
// This handles cases where hosted agents (like MCP agents) may not have a version assigned.
|
|
var version = string.IsNullOrWhiteSpace(agentVersion.Version) ? "latest" : agentVersion.Version;
|
|
return new AgentReference(agentVersion.Name, version);
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public override object? GetService(Type serviceType, object? serviceKey = null)
|
|
{
|
|
return (serviceKey is null && serviceType == typeof(ChatClientMetadata))
|
|
? this._metadata
|
|
: (serviceKey is null && serviceType == typeof(AIProjectClient))
|
|
? this._agentClient
|
|
: (serviceKey is null && serviceType == typeof(AgentVersion))
|
|
? this._agentVersion
|
|
: (serviceKey is null && serviceType == typeof(AgentRecord))
|
|
? this._agentRecord
|
|
: (serviceKey is null && serviceType == typeof(AgentReference))
|
|
? this._agentReference
|
|
: base.GetService(serviceType, serviceKey);
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public override async Task<ChatResponse> GetResponseAsync(IEnumerable<ChatMessage> messages, ChatOptions? options = null, CancellationToken cancellationToken = default)
|
|
{
|
|
var agentOptions = this.GetAgentEnabledChatOptions(options);
|
|
|
|
return await base.GetResponseAsync(messages, agentOptions, cancellationToken).ConfigureAwait(false);
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public override async IAsyncEnumerable<ChatResponseUpdate> GetStreamingResponseAsync(IEnumerable<ChatMessage> messages, ChatOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default)
|
|
{
|
|
var agentOptions = this.GetAgentEnabledChatOptions(options);
|
|
|
|
await foreach (var chunk in base.GetStreamingResponseAsync(messages, agentOptions, cancellationToken).ConfigureAwait(false))
|
|
{
|
|
yield return chunk;
|
|
}
|
|
}
|
|
|
|
private ChatOptions GetAgentEnabledChatOptions(ChatOptions? options)
|
|
{
|
|
// Start with a clone of the base chat options defined for the agent, if any.
|
|
ChatOptions agentEnabledChatOptions = this._chatOptions?.Clone() ?? new();
|
|
|
|
// Ignore per-request all options that can't be overridden.
|
|
agentEnabledChatOptions.Instructions = null;
|
|
agentEnabledChatOptions.Tools = null;
|
|
agentEnabledChatOptions.Temperature = null;
|
|
agentEnabledChatOptions.TopP = null;
|
|
agentEnabledChatOptions.PresencePenalty = null;
|
|
agentEnabledChatOptions.ResponseFormat = null;
|
|
|
|
// Use the conversation from the request, or the one defined at the client level.
|
|
agentEnabledChatOptions.ConversationId = options?.ConversationId ?? this._chatOptions?.ConversationId;
|
|
|
|
// Preserve the original RawRepresentationFactory
|
|
var originalFactory = options?.RawRepresentationFactory;
|
|
|
|
agentEnabledChatOptions.RawRepresentationFactory = (client) =>
|
|
{
|
|
if (originalFactory?.Invoke(this) is not CreateResponseOptions responseCreationOptions)
|
|
{
|
|
responseCreationOptions = new CreateResponseOptions();
|
|
}
|
|
|
|
responseCreationOptions.Agent = this._agentReference;
|
|
#pragma warning disable SCME0001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
|
|
responseCreationOptions.Patch.Remove("$.model"u8);
|
|
#pragma warning restore SCME0001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
|
|
|
|
return responseCreationOptions;
|
|
};
|
|
|
|
return agentEnabledChatOptions;
|
|
}
|
|
}
|