mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
.NET: Update to latest Azure.AI.*, OpenAI, and M.E.AI* (#2850)
* Update to latest Azure.AI.*, OpenAI, and M.E.AI* Absorb breaking changes in Responses surface area * Update dotnet/samples/AgentWebChat/AgentWebChat.AgentHost/Utilities/ChatClientExtensions.cs * Update dotnet/samples/AgentWebChat/AgentWebChat.AgentHost/Utilities/ChatClientExtensions.cs * Update dotnet/samples/AgentWebChat/AgentWebChat.AgentHost/Utilities/ChatClientExtensions.cs * Update dotnet/samples/GettingStarted/AgentWithOpenAI/Agent_OpenAI_Step04_CreateFromOpenAIResponseClient/Program.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Using patch to remove the model is necessary, updated the response client to actually use the the ForAgent --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Roger Barreto <19890735+rogerbarreto@users.noreply.github.com>
This commit is contained in:
co-authored by
Copilot
Roger Barreto
parent
958a488f96
commit
3c322c91e7
@@ -23,11 +23,6 @@ internal sealed class AzureAIProjectChatClient : DelegatingChatClient
|
||||
private readonly AgentRecord? _agentRecord;
|
||||
private readonly ChatOptions? _chatOptions;
|
||||
private readonly AgentReference _agentReference;
|
||||
/// <summary>
|
||||
/// The usage of a no-op model is a necessary change to avoid OpenAIClients to throw exceptions when
|
||||
/// used with Azure AI Agents as the model used is now defined at the agent creation time.
|
||||
/// </summary>
|
||||
private const string NoOpModel = "no-op";
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AzureAIProjectChatClient"/> class.
|
||||
@@ -42,7 +37,7 @@ internal sealed class AzureAIProjectChatClient : DelegatingChatClient
|
||||
internal AzureAIProjectChatClient(AIProjectClient aiProjectClient, AgentReference agentReference, string? defaultModelId, ChatOptions? chatOptions)
|
||||
: base(Throw.IfNull(aiProjectClient)
|
||||
.GetProjectOpenAIClient()
|
||||
.GetOpenAIResponseClient(defaultModelId ?? NoOpModel)
|
||||
.GetProjectResponsesClientForAgent(agentReference)
|
||||
.AsIChatClient())
|
||||
{
|
||||
this._agentClient = aiProjectClient;
|
||||
@@ -132,13 +127,15 @@ internal sealed class AzureAIProjectChatClient : DelegatingChatClient
|
||||
|
||||
agentEnabledChatOptions.RawRepresentationFactory = (client) =>
|
||||
{
|
||||
if (originalFactory?.Invoke(this) is not ResponseCreationOptions responseCreationOptions)
|
||||
if (originalFactory?.Invoke(this) is not CreateResponseOptions responseCreationOptions)
|
||||
{
|
||||
responseCreationOptions = new ResponseCreationOptions();
|
||||
responseCreationOptions = new CreateResponseOptions();
|
||||
}
|
||||
|
||||
ResponseCreationOptionsExtensions.set_Agent(responseCreationOptions, this._agentReference);
|
||||
ResponseCreationOptionsExtensions.set_Model(responseCreationOptions, null);
|
||||
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;
|
||||
};
|
||||
|
||||
@@ -400,7 +400,7 @@ public static partial class AzureAIProjectChatClientExtensions
|
||||
};
|
||||
|
||||
// Attempt to capture breaking glass options from the raw representation factory that match the agent definition.
|
||||
if (options.ChatOptions?.RawRepresentationFactory?.Invoke(new NoOpChatClient()) is ResponseCreationOptions respCreationOptions)
|
||||
if (options.ChatOptions?.RawRepresentationFactory?.Invoke(new NoOpChatClient()) is CreateResponseOptions respCreationOptions)
|
||||
{
|
||||
agentDefinition.ReasoningOptions = respCreationOptions.ReasoningOptions;
|
||||
}
|
||||
@@ -466,7 +466,7 @@ public static partial class AzureAIProjectChatClientExtensions
|
||||
};
|
||||
|
||||
// Attempt to capture breaking glass options from the raw representation factory that match the agent definition.
|
||||
if (options.ChatOptions?.RawRepresentationFactory?.Invoke(new NoOpChatClient()) is ResponseCreationOptions respCreationOptions)
|
||||
if (options.ChatOptions?.RawRepresentationFactory?.Invoke(new NoOpChatClient()) is CreateResponseOptions respCreationOptions)
|
||||
{
|
||||
agentDefinition.ReasoningOptions = respCreationOptions.ReasoningOptions;
|
||||
}
|
||||
|
||||
+11
-15
@@ -84,22 +84,18 @@ internal sealed class ConversationReferenceJsonConverter : JsonConverter<Convers
|
||||
return;
|
||||
}
|
||||
|
||||
// If only ID is present and no metadata, serialize as a simple string
|
||||
if (value.Metadata is null || value.Metadata.Count == 0)
|
||||
// Ideally if only ID is present and no metadata, we would serialize as a simple string.
|
||||
// However, while a request's "conversation" property can be either a string or an object
|
||||
// containing a string, a response's "conversation" property is always an object. Since
|
||||
// here we don't know which scenario we're in, we always serialize as an object, which works
|
||||
// in any scenario.
|
||||
writer.WriteStartObject();
|
||||
writer.WriteString("id", value.Id);
|
||||
if (value.Metadata is not null)
|
||||
{
|
||||
writer.WriteStringValue(value.Id);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Otherwise, serialize as an object
|
||||
writer.WriteStartObject();
|
||||
writer.WriteString("id", value.Id);
|
||||
if (value.Metadata is not null)
|
||||
{
|
||||
writer.WritePropertyName("metadata");
|
||||
JsonSerializer.Serialize(writer, value.Metadata, OpenAIHostingJsonContext.Default.DictionaryStringString);
|
||||
}
|
||||
writer.WriteEndObject();
|
||||
writer.WritePropertyName("metadata");
|
||||
JsonSerializer.Serialize(writer, value.Metadata, OpenAIHostingJsonContext.Default.DictionaryStringString);
|
||||
}
|
||||
writer.WriteEndObject();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,22 +73,22 @@ public static class AIAgentWithOpenAIExtensions
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs the AI agent with a collection of OpenAI response items and returns the response as a native OpenAI <see cref="OpenAIResponse"/>.
|
||||
/// Runs the AI agent with a collection of OpenAI response items and returns the response as a native OpenAI <see cref="ResponseResult"/>.
|
||||
/// </summary>
|
||||
/// <param name="agent">The AI agent to run.</param>
|
||||
/// <param name="messages">The collection of OpenAI response items to send to the agent.</param>
|
||||
/// <param name="thread">The conversation thread to continue with this invocation. If not provided, creates a new thread. The thread will be mutated with the provided messages and agent response.</param>
|
||||
/// <param name="options">Optional parameters for agent invocation.</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="Task{OpenAIResponse}"/> representing the asynchronous operation that returns a native OpenAI <see cref="OpenAIResponse"/> response.</returns>
|
||||
/// <returns>A <see cref="Task{ResponseResult}"/> representing the asynchronous operation that returns a native OpenAI <see cref="ResponseResult"/> response.</returns>
|
||||
/// <exception cref="ArgumentNullException">Thrown when <paramref name="agent"/> or <paramref name="messages"/> is <see langword="null"/>.</exception>
|
||||
/// <exception cref="ArgumentException">Thrown when the agent's response cannot be converted to an <see cref="OpenAIResponse"/>, typically when the underlying representation is not an OpenAI response.</exception>
|
||||
/// <exception cref="ArgumentException">Thrown when the agent's response cannot be converted to an <see cref="ResponseResult"/>, typically when the underlying representation is not an OpenAI response.</exception>
|
||||
/// <exception cref="NotSupportedException">Thrown when any message in <paramref name="messages"/> has a type that is not supported by the message conversion method.</exception>
|
||||
/// <remarks>
|
||||
/// This method converts the OpenAI response items to the Microsoft Extensions AI format using the appropriate conversion method,
|
||||
/// runs the agent with the converted message collection, and then extracts the native OpenAI <see cref="OpenAIResponse"/> from the response using <see cref="AgentRunResponseExtensions.AsOpenAIResponse"/>.
|
||||
/// runs the agent with the converted message collection, and then extracts the native OpenAI <see cref="ResponseResult"/> from the response using <see cref="AgentRunResponseExtensions.AsOpenAIResponse"/>.
|
||||
/// </remarks>
|
||||
public static async Task<OpenAIResponse> RunAsync(this AIAgent agent, IEnumerable<ResponseItem> messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default)
|
||||
public static async Task<ResponseResult> RunAsync(this AIAgent agent, IEnumerable<ResponseItem> messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
Throw.IfNull(agent);
|
||||
Throw.IfNull(messages);
|
||||
|
||||
@@ -29,17 +29,17 @@ public static class AgentRunResponseExtensions
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates or extracts a native OpenAI <see cref="OpenAIResponse"/> object from an <see cref="AgentRunResponse"/>.
|
||||
/// Creates or extracts a native OpenAI <see cref="ResponseResult"/> object from an <see cref="AgentRunResponse"/>.
|
||||
/// </summary>
|
||||
/// <param name="response">The agent response.</param>
|
||||
/// <returns>The OpenAI <see cref="OpenAIResponse"/> object.</returns>
|
||||
/// <returns>The OpenAI <see cref="ResponseResult"/> object.</returns>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="response"/> is <see langword="null"/>.</exception>
|
||||
public static OpenAIResponse AsOpenAIResponse(this AgentRunResponse response)
|
||||
public static ResponseResult AsOpenAIResponse(this AgentRunResponse response)
|
||||
{
|
||||
Throw.IfNull(response);
|
||||
|
||||
return
|
||||
response.RawRepresentation as OpenAIResponse ??
|
||||
response.AsChatResponse().AsOpenAIResponse();
|
||||
response.RawRepresentation as ResponseResult ??
|
||||
response.AsChatResponse().AsOpenAIResponseResult();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ using Microsoft.Shared.Diagnostics;
|
||||
namespace OpenAI.Responses;
|
||||
|
||||
/// <summary>
|
||||
/// Provides extension methods for <see cref="OpenAIResponseClient"/>
|
||||
/// Provides extension methods for <see cref="ResponsesClient"/>
|
||||
/// to simplify the creation of AI agents that work with OpenAI services.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
@@ -20,9 +20,9 @@ namespace OpenAI.Responses;
|
||||
public static class OpenAIResponseClientExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates an AI agent from an <see cref="OpenAIResponseClient"/> using the OpenAI Response API.
|
||||
/// Creates an AI agent from an <see cref="ResponsesClient"/> using the OpenAI Response API.
|
||||
/// </summary>
|
||||
/// <param name="client">The <see cref="OpenAIResponseClient" /> to use for the agent.</param>
|
||||
/// <param name="client">The <see cref="ResponsesClient" /> to use for the agent.</param>
|
||||
/// <param name="instructions">Optional system instructions that define the agent's behavior and personality.</param>
|
||||
/// <param name="name">Optional name for the agent for identification purposes.</param>
|
||||
/// <param name="description">Optional description of the agent's capabilities and purpose.</param>
|
||||
@@ -33,7 +33,7 @@ public static class OpenAIResponseClientExtensions
|
||||
/// <returns>An <see cref="ChatClientAgent"/> instance backed by the OpenAI Response service.</returns>
|
||||
/// <exception cref="ArgumentNullException">Thrown when <paramref name="client"/> is <see langword="null"/>.</exception>
|
||||
public static ChatClientAgent CreateAIAgent(
|
||||
this OpenAIResponseClient client,
|
||||
this ResponsesClient client,
|
||||
string? instructions = null,
|
||||
string? name = null,
|
||||
string? description = null,
|
||||
@@ -61,9 +61,9 @@ public static class OpenAIResponseClientExtensions
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an AI agent from an <see cref="OpenAIResponseClient"/> using the OpenAI Response API.
|
||||
/// Creates an AI agent from an <see cref="ResponsesClient"/> using the OpenAI Response API.
|
||||
/// </summary>
|
||||
/// <param name="client">The <see cref="OpenAIResponseClient" /> to use for the agent.</param>
|
||||
/// <param name="client">The <see cref="ResponsesClient" /> to use for 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="loggerFactory">Optional logger factory for enabling logging within the agent.</param>
|
||||
@@ -71,7 +71,7 @@ public static class OpenAIResponseClientExtensions
|
||||
/// <returns>An <see cref="ChatClientAgent"/> instance backed by the OpenAI Response service.</returns>
|
||||
/// <exception cref="ArgumentNullException">Thrown when <paramref name="client"/> or <paramref name="options"/> is <see langword="null"/>.</exception>
|
||||
public static ChatClientAgent CreateAIAgent(
|
||||
this OpenAIResponseClient client,
|
||||
this ResponsesClient client,
|
||||
ChatClientAgentOptions options,
|
||||
Func<IChatClient, IChatClient>? clientFactory = null,
|
||||
ILoggerFactory? loggerFactory = null,
|
||||
|
||||
@@ -111,7 +111,7 @@ public sealed class AzureAgentProvider(Uri projectEndpoint, TokenCredential proj
|
||||
if (inputArguments is not null)
|
||||
{
|
||||
JsonNode jsonNode = ConvertDictionaryToJson(inputArguments);
|
||||
ResponseCreationOptions responseCreationOptions = new();
|
||||
CreateResponseOptions responseCreationOptions = new();
|
||||
#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.Set("$.structured_inputs"u8, BinaryData.FromString(jsonNode.ToJsonString()));
|
||||
#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.
|
||||
@@ -206,7 +206,7 @@ public sealed class AzureAgentProvider(Uri projectEndpoint, TokenCredential proj
|
||||
public override async Task<ChatMessage> GetMessageAsync(string conversationId, string messageId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
AgentResponseItem responseItem = await this.GetConversationClient().GetProjectConversationItemAsync(conversationId, messageId, include: null, cancellationToken).ConfigureAwait(false);
|
||||
ResponseItem[] items = [responseItem.AsOpenAIResponseItem()];
|
||||
ResponseItem[] items = [responseItem.AsResponseResultItem()];
|
||||
return items.AsChatMessages().Single();
|
||||
}
|
||||
|
||||
@@ -223,7 +223,7 @@ public sealed class AzureAgentProvider(Uri projectEndpoint, TokenCredential proj
|
||||
|
||||
await foreach (AgentResponseItem responseItem in this.GetConversationClient().GetProjectConversationItemsAsync(conversationId, null, limit, order.ToString(), after, before, include: null, cancellationToken).ConfigureAwait(false))
|
||||
{
|
||||
ResponseItem[] items = [responseItem.AsOpenAIResponseItem()];
|
||||
ResponseItem[] items = [responseItem.AsResponseResultItem()];
|
||||
foreach (ChatMessage message in items.AsChatMessages())
|
||||
{
|
||||
yield return message;
|
||||
|
||||
Reference in New Issue
Block a user