diff --git a/dotnet/src/Microsoft.Agents.AI.Abstractions/AgentRunResponseUpdateExtensions.cs b/dotnet/src/Microsoft.Agents.AI.Abstractions/AgentRunResponseExtensions.cs similarity index 71% rename from dotnet/src/Microsoft.Agents.AI.Abstractions/AgentRunResponseUpdateExtensions.cs rename to dotnet/src/Microsoft.Agents.AI.Abstractions/AgentRunResponseExtensions.cs index 848bd5fd24..adad80d890 100644 --- a/dotnet/src/Microsoft.Agents.AI.Abstractions/AgentRunResponseUpdateExtensions.cs +++ b/dotnet/src/Microsoft.Agents.AI.Abstractions/AgentRunResponseExtensions.cs @@ -14,10 +14,91 @@ using Microsoft.Shared.Diagnostics; namespace Microsoft.Agents.AI; /// -/// Provides extension methods for working with instances. +/// Provides extension methods for working with and instances. /// -public static class AgentRunResponseUpdateExtensions +public static class AgentRunResponseExtensions { + /// + /// Creates a from an . + /// + /// The . + /// A built from . + /// + /// If the 's is a + /// instance, that instance is returned directly. Otherwise, a new + /// is created and populated with the data from the . + /// The instance is a shallow copy; any reference-type members (e.g. ) + /// will be shared between the two instances. + /// + public static ChatResponse AsChatResponse(this AgentRunResponse response) + { + Throw.IfNull(response); + + return + response.RawRepresentation as ChatResponse ?? + new() + { + AdditionalProperties = response.AdditionalProperties, + CreatedAt = response.CreatedAt, + Messages = response.Messages, + RawRepresentation = response, + ResponseId = response.ResponseId, + Usage = response.Usage, + }; + } + + /// + /// Creates a from an . + /// + /// The . + /// A built from . + /// + /// If the 's is a + /// instance, that instance is returned directly. Otherwise, a new + /// is created and populated with the data from the . + /// The instance is a shallow copy; any reference-type members (e.g. ) + /// will be shared between the two instances. + /// + public static ChatResponseUpdate AsChatResponseUpdate(this AgentRunResponseUpdate responseUpdate) + { + Throw.IfNull(responseUpdate); + + return + responseUpdate.RawRepresentation as ChatResponseUpdate ?? + new() + { + AdditionalProperties = responseUpdate.AdditionalProperties, + AuthorName = responseUpdate.AuthorName, + Contents = responseUpdate.Contents, + CreatedAt = responseUpdate.CreatedAt, + MessageId = responseUpdate.MessageId, + RawRepresentation = responseUpdate, + ResponseId = responseUpdate.ResponseId, + Role = responseUpdate.Role, + }; + } + + /// + /// Creates an asynchronous enumerable of instances from an asynchronous + /// enumerable of instances. + /// + /// The sequence . + /// A sequence of instances built from . + /// + /// Each is converted to a using + /// . + /// + public static async IAsyncEnumerable AsChatResponseUpdatesAsync( + this IAsyncEnumerable responseUpdates) + { + Throw.IfNull(responseUpdates); + + await foreach (var responseUpdate in responseUpdates.ConfigureAwait(false)) + { + yield return responseUpdate.AsChatResponseUpdate(); + } + } + /// Combines instances into a single . /// The updates to be combined. /// The combined . diff --git a/dotnet/src/Microsoft.Agents.AI.Abstractions/AgentRunResponseUpdate.cs b/dotnet/src/Microsoft.Agents.AI.Abstractions/AgentRunResponseUpdate.cs index 3087c78b92..f9f7b674dd 100644 --- a/dotnet/src/Microsoft.Agents.AI.Abstractions/AgentRunResponseUpdate.cs +++ b/dotnet/src/Microsoft.Agents.AI.Abstractions/AgentRunResponseUpdate.cs @@ -22,7 +22,7 @@ namespace Microsoft.Agents.AI; /// /// /// The relationship between and is -/// codified in the and +/// codified in the and /// , which enable bidirectional conversions /// between the two. Note, however, that the provided conversions may be lossy, for example if multiple /// updates all have different objects whereas there's only one slot for @@ -135,7 +135,7 @@ public class AgentRunResponseUpdate /// Some providers may consider streaming responses to be a single message, and in that case /// the value of this property may be the same as the response ID. /// - /// This value is used when + /// This value is used when /// groups instances into instances. /// The value must be unique to each call to the underlying provider, and must be shared by /// all updates that are part of the same logical message within a streaming response. diff --git a/dotnet/src/Microsoft.Agents.AI.OpenAI/ChatClient/AsyncStreamingUpdateCollectionResult.cs b/dotnet/src/Microsoft.Agents.AI.OpenAI/ChatClient/AsyncStreamingUpdateCollectionResult.cs index 2c273b98ce..127f45ef6e 100644 --- a/dotnet/src/Microsoft.Agents.AI.OpenAI/ChatClient/AsyncStreamingUpdateCollectionResult.cs +++ b/dotnet/src/Microsoft.Agents.AI.OpenAI/ChatClient/AsyncStreamingUpdateCollectionResult.cs @@ -3,7 +3,7 @@ using System.ClientModel; using OpenAI.Chat; -namespace Microsoft.Agents.AI.OpenAI.ChatClient; +namespace Microsoft.Agents.AI.OpenAI; internal sealed class AsyncStreamingUpdateCollectionResult : AsyncCollectionResult { @@ -19,13 +19,10 @@ internal sealed class AsyncStreamingUpdateCollectionResult : AsyncCollectionResu public override IAsyncEnumerable GetRawPagesAsync() => AsyncEnumerable.Repeat(ClientResult.FromValue(this._updates, new StreamingUpdatePipelineResponse(this._updates)), 1); - protected override async IAsyncEnumerable GetValuesFromPageAsync(ClientResult page) + protected override IAsyncEnumerable GetValuesFromPageAsync(ClientResult page) { var updates = ((ClientResult>)page).Value; - await foreach (var update in updates.ConfigureAwait(false)) - { - yield return update.AsStreamingChatCompletionUpdate(); - } + return updates.AsChatResponseUpdatesAsync().AsOpenAIStreamingChatCompletionUpdatesAsync(); } } diff --git a/dotnet/src/Microsoft.Agents.AI.OpenAI/ChatClient/StreamingUpdatePipelineResponse.cs b/dotnet/src/Microsoft.Agents.AI.OpenAI/ChatClient/StreamingUpdatePipelineResponse.cs index 1768d100ad..e999ad04e7 100644 --- a/dotnet/src/Microsoft.Agents.AI.OpenAI/ChatClient/StreamingUpdatePipelineResponse.cs +++ b/dotnet/src/Microsoft.Agents.AI.OpenAI/ChatClient/StreamingUpdatePipelineResponse.cs @@ -2,7 +2,7 @@ using System.ClientModel.Primitives; -namespace Microsoft.Agents.AI.OpenAI.ChatClient; +namespace Microsoft.Agents.AI.OpenAI; internal sealed class StreamingUpdatePipelineResponse : PipelineResponse { diff --git a/dotnet/src/Microsoft.Agents.AI.OpenAI/Extensions/AIAgentWithOpenAIExtensions.cs b/dotnet/src/Microsoft.Agents.AI.OpenAI/Extensions/AIAgentWithOpenAIExtensions.cs index c51698c973..32bb50080d 100644 --- a/dotnet/src/Microsoft.Agents.AI.OpenAI/Extensions/AIAgentWithOpenAIExtensions.cs +++ b/dotnet/src/Microsoft.Agents.AI.OpenAI/Extensions/AIAgentWithOpenAIExtensions.cs @@ -2,7 +2,7 @@ using System.ClientModel; using Microsoft.Agents.AI; -using Microsoft.Agents.AI.OpenAI.ChatClient; +using Microsoft.Agents.AI.OpenAI; using Microsoft.Shared.Diagnostics; using OpenAI.Chat; @@ -34,7 +34,7 @@ public static class AIAgentWithOpenAIExtensions /// Thrown when any message in has a type that is not supported by the message conversion method. /// /// This method converts the OpenAI chat messages 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 from the response using . + /// runs the agent with the converted message collection, and then extracts the native OpenAI from the response using . /// public static async Task RunAsync(this AIAgent agent, IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { @@ -43,7 +43,7 @@ public static class AIAgentWithOpenAIExtensions var response = await agent.RunAsync([.. messages.AsChatMessages()], thread, options, cancellationToken).ConfigureAwait(false); - return response.AsChatCompletion(); + return response.AsOpenAIChatCompletion(); } /// @@ -60,7 +60,7 @@ public static class AIAgentWithOpenAIExtensions /// Thrown when the type is not supported by the message conversion method. /// /// This method converts the OpenAI chat messages to the Microsoft Extensions AI format using the appropriate conversion method, - /// runs the agent, and then extracts the native OpenAI from the response using . + /// runs the agent, and then extracts the native OpenAI from the response using . /// public static AsyncCollectionResult RunStreamingAsync(this AIAgent agent, IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { diff --git a/dotnet/src/Microsoft.Agents.AI.OpenAI/Extensions/AgentRunResponseExtensions.cs b/dotnet/src/Microsoft.Agents.AI.OpenAI/Extensions/AgentRunResponseExtensions.cs index 4c727788e0..1660192fad 100644 --- a/dotnet/src/Microsoft.Agents.AI.OpenAI/Extensions/AgentRunResponseExtensions.cs +++ b/dotnet/src/Microsoft.Agents.AI.OpenAI/Extensions/AgentRunResponseExtensions.cs @@ -1,52 +1,45 @@ // Copyright (c) Microsoft. All rights reserved. using Microsoft.Agents.AI; -using Microsoft.Extensions.AI; using Microsoft.Shared.Diagnostics; using OpenAI.Chat; +using OpenAI.Responses; namespace OpenAI; /// -/// Provides extension methods for to extract native OpenAI response objects -/// from the Microsoft Agent Framework responses. +/// Provides extension methods for and instances to +/// create or extract native OpenAI response objects from the Microsoft Agent Framework responses. /// -/// -/// These extensions enable developers to access the underlying OpenAI SDK objects when working with -/// AI agents that are backed by OpenAI services. The methods extract strongly-typed OpenAI responses -/// from the property, providing a bridge between -/// the Microsoft Extensions AI framework and the native OpenAI SDK types. -/// public static class AgentRunResponseExtensions { /// - /// Extracts a native OpenAI object from an . + /// Creates or extracts a native OpenAI object from an . /// - /// The agent response containing the raw OpenAI representation. - /// The native OpenAI object. - /// Thrown when is . - /// - /// Thrown when the is not a object. - /// This typically occurs when the agent response was not generated by an OpenAI chat completion service - /// or when the underlying representation has been modified or corrupted. - /// - /// - /// - /// This method provides access to the native OpenAI object that was used - /// to generate the agent response. This is useful when you need to access OpenAI-specific properties - /// or metadata that are not exposed through the Microsoft Extensions AI abstractions. - /// - /// - public static ChatCompletion AsChatCompletion(this AgentRunResponse agentResponse) + /// The agent response. + /// The OpenAI object. + /// is . + public static ChatCompletion AsOpenAIChatCompletion(this AgentRunResponse response) { - Throw.IfNull(agentResponse); + Throw.IfNull(response); - if (agentResponse.RawRepresentation is ChatResponse chatResponse) - { - return chatResponse.RawRepresentation is ChatCompletion chatCompletion - ? chatCompletion - : throw new ArgumentException("ChatResponse.RawRepresentation must be a ChatCompletion"); - } - throw new ArgumentException("AgentRunResponse.RawRepresentation must be a ChatResponse"); + return + response.RawRepresentation as ChatCompletion ?? + response.AsChatResponse().AsOpenAIChatCompletion(); + } + + /// + /// Creates or extracts a native OpenAI object from an . + /// + /// The agent response. + /// The OpenAI object. + /// is . + public static OpenAIResponse AsOpenAIResponse(this AgentRunResponse response) + { + Throw.IfNull(response); + + return + response.RawRepresentation as OpenAIResponse ?? + response.AsChatResponse().AsOpenAIResponse(); } } diff --git a/dotnet/src/Microsoft.Agents.AI.OpenAI/Extensions/AgentRunResponseUpdateExtensions.cs b/dotnet/src/Microsoft.Agents.AI.OpenAI/Extensions/AgentRunResponseUpdateExtensions.cs deleted file mode 100644 index 83d800aaba..0000000000 --- a/dotnet/src/Microsoft.Agents.AI.OpenAI/Extensions/AgentRunResponseUpdateExtensions.cs +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. - -using Microsoft.Extensions.AI; -using Microsoft.Shared.Diagnostics; -using OpenAI.Chat; - -namespace Microsoft.Agents.AI; - -/// -/// Provides extension methods for to extract native OpenAI response objects -/// from the Microsoft Agent Framework responses. -/// -/// -/// These extensions enable developers to access the underlying OpenAI SDK objects when working with -/// AI agents that are backed by OpenAI services. The methods extract strongly-typed OpenAI responses -/// from the property, providing a bridge between -/// the Microsoft Extensions AI framework and the native OpenAI SDK types. -/// -public static class AgentRunResponseUpdateExtensions -{ - /// - /// Extracts a native OpenAI object from an . - /// - /// The agent response containing the raw OpenAI representation. - /// The native OpenAI object. - /// Thrown when is . - /// - /// Thrown when the is not a object, - /// or when the nested is not a object. - /// This typically occurs when the agent response was not generated by an OpenAI streaming chat completion service - /// or when the underlying representation has been modified or corrupted. - /// - /// - /// - /// This method provides access to the native OpenAI object that was used - /// to generate the agent response. This is useful when you need to access OpenAI-specific properties - /// or metadata that are not exposed through the Microsoft Extensions AI abstractions. - /// - /// - public static StreamingChatCompletionUpdate AsStreamingChatCompletionUpdate(this AgentRunResponseUpdate agentResponseUpdate) - { - Throw.IfNull(agentResponseUpdate); - - if (agentResponseUpdate.RawRepresentation is ChatResponseUpdate chatResponseUpdate) - { - return chatResponseUpdate.RawRepresentation is StreamingChatCompletionUpdate streamingChatCompletionUpdate - ? streamingChatCompletionUpdate - : throw new ArgumentException("ChatResponseUpdate.RawRepresentation must be a StreamingChatCompletionUpdate"); - } - throw new ArgumentException("AgentRunResponseUpdate.RawRepresentation must be a ChatResponseUpdate"); - } -} diff --git a/dotnet/src/Microsoft.Agents.AI.OpenAI/OpenAIChatClientAgent.cs b/dotnet/src/Microsoft.Agents.AI.OpenAI/OpenAIChatClientAgent.cs index fbef8d10fa..b529e1151b 100644 --- a/dotnet/src/Microsoft.Agents.AI.OpenAI/OpenAIChatClientAgent.cs +++ b/dotnet/src/Microsoft.Agents.AI.OpenAI/OpenAIChatClientAgent.cs @@ -1,6 +1,5 @@ // Copyright (c) Microsoft. All rights reserved. -using System.Text.Json; using Microsoft.Agents.AI; using Microsoft.Extensions.AI; using Microsoft.Extensions.Logging; @@ -11,12 +10,10 @@ using ChatMessage = OpenAI.Chat.ChatMessage; namespace OpenAI; /// -/// OpenAI chat completion based implementation of . +/// Provides an backed by an OpenAI chat completion implementation. /// -public class OpenAIChatClientAgent : AIAgent +public class OpenAIChatClientAgent : DelegatingAIAgent { - private readonly ChatClientAgent _chatClientAgent; - /// /// Initialize an instance of /// @@ -30,20 +27,14 @@ public class OpenAIChatClientAgent : AIAgent string? instructions = null, string? name = null, string? description = null, - ILoggerFactory? loggerFactory = null) + ILoggerFactory? loggerFactory = null) : + this(client, new() + { + Name = name, + Description = description, + Instructions = instructions, + }, loggerFactory) { - Throw.IfNull(client); - - var chatClient = client.AsIChatClient(); - this._chatClientAgent = new( - chatClient, - new ChatClientAgentOptions() - { - Name = name, - Description = description, - Instructions = instructions, - }, - loggerFactory); } /// @@ -52,12 +43,10 @@ public class OpenAIChatClientAgent : AIAgent /// Instance of /// Options to create the agent. /// Optional instance of - public OpenAIChatClientAgent(ChatClient client, ChatClientAgentOptions options, ILoggerFactory? loggerFactory = null) + public OpenAIChatClientAgent( + ChatClient client, ChatClientAgentOptions options, ILoggerFactory? loggerFactory = null) : + base(new ChatClientAgent(Throw.IfNull(client).AsIChatClient(), options, loggerFactory)) { - Throw.IfNull(client); - - var chatClient = client.AsIChatClient(); - this._chatClientAgent = new(chatClient, options, loggerFactory); } /// @@ -74,37 +63,35 @@ public class OpenAIChatClientAgent : AIAgent AgentRunOptions? options = null, CancellationToken cancellationToken = default) { - var response = await this.RunAsync([.. messages.AsChatMessages()], thread, options, cancellationToken).ConfigureAwait(false); + var response = await this.RunAsync(messages.AsChatMessages(), thread, options, cancellationToken).ConfigureAwait(false); - return response.AsChatCompletion(); + return response.AsOpenAIChatCompletion(); + } + + /// + /// Run the agent streaming with the provided message and arguments. + /// + /// The messages to pass to the agent. + /// 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. + /// Optional parameters for agent invocation. + /// The to monitor for cancellation requests. The default is . + /// A containing the list of items. + public virtual IAsyncEnumerable RunStreamingAsync( + IEnumerable messages, + AgentThread? thread = null, + AgentRunOptions? options = null, + CancellationToken cancellationToken = default) + { + var response = this.RunStreamingAsync(messages.AsChatMessages(), thread, options, cancellationToken); + + return response.AsChatResponseUpdatesAsync().AsOpenAIStreamingChatCompletionUpdatesAsync(cancellationToken); } /// - public sealed override AgentThread GetNewThread() - => this._chatClientAgent.GetNewThread(); + public sealed override Task RunAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) => + base.RunAsync(messages, thread, options, cancellationToken); /// - public sealed override AgentThread DeserializeThread(JsonElement serializedThread, JsonSerializerOptions? jsonSerializerOptions = null) - => this._chatClientAgent.DeserializeThread(serializedThread, jsonSerializerOptions); - - /// - public sealed override Task RunAsync( - IEnumerable messages, - AgentThread? thread = null, - AgentRunOptions? options = null, - CancellationToken cancellationToken = default) - => this._chatClientAgent.RunAsync(messages, thread, options, cancellationToken); - - /// - public sealed override IAsyncEnumerable RunStreamingAsync( - IEnumerable messages, - AgentThread? thread = null, - AgentRunOptions? options = null, - CancellationToken cancellationToken = default) - => this._chatClientAgent.RunStreamingAsync(messages, thread, options, cancellationToken); - - /// - public override object? GetService(Type serviceType, object? serviceKey = null) - => base.GetService(serviceType, serviceKey) - ?? this._chatClientAgent.GetService(serviceType, serviceKey); + public override IAsyncEnumerable RunStreamingAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) => + base.RunStreamingAsync(messages, thread, options, cancellationToken); } diff --git a/dotnet/src/Microsoft.Agents.AI.OpenAI/OpenAIResponseClientAgent.cs b/dotnet/src/Microsoft.Agents.AI.OpenAI/OpenAIResponseClientAgent.cs new file mode 100644 index 0000000000..8c5603fb05 --- /dev/null +++ b/dotnet/src/Microsoft.Agents.AI.OpenAI/OpenAIResponseClientAgent.cs @@ -0,0 +1,115 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System.Runtime.CompilerServices; +using Microsoft.Agents.AI; +using Microsoft.Extensions.AI; +using Microsoft.Extensions.Logging; +using Microsoft.Shared.Diagnostics; +using OpenAI.Responses; + +namespace OpenAI; + +/// +/// Provides an backed by an OpenAI Responses implementation. +/// +public class OpenAIResponseClientAgent : DelegatingAIAgent +{ + /// + /// Initialize an instance of . + /// + /// Instance of + /// Optional instructions for the agent. + /// Optional name for the agent. + /// Optional description for the agent. + /// Optional instance of + public OpenAIResponseClientAgent( + OpenAIResponseClient client, + string? instructions = null, + string? name = null, + string? description = null, + ILoggerFactory? loggerFactory = null) : + this(client, new() + { + Name = name, + Description = description, + Instructions = instructions, + }, loggerFactory) + { + } + + /// + /// Initialize an instance of . + /// + /// Instance of + /// Options to create the agent. + /// Optional instance of + public OpenAIResponseClientAgent( + OpenAIResponseClient client, ChatClientAgentOptions options, ILoggerFactory? loggerFactory = null) : + base(new ChatClientAgent(Throw.IfNull(client).AsIChatClient(), options, loggerFactory)) + { + } + + /// + /// Run the agent with the provided message and arguments. + /// + /// The messages to pass to the agent. + /// 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. + /// Optional parameters for agent invocation. + /// The to monitor for cancellation requests. The default is . + /// A containing the list of items. + public virtual async Task RunAsync( + IEnumerable messages, + AgentThread? thread = null, + AgentRunOptions? options = null, + CancellationToken cancellationToken = default) + { + var response = await this.RunAsync(messages.AsChatMessages(), thread, options, cancellationToken).ConfigureAwait(false); + + return response.AsOpenAIResponse(); + } + + /// + /// Run the agent streaming with the provided message and arguments. + /// + /// The messages to pass to the agent. + /// 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. + /// Optional parameters for agent invocation. + /// The to monitor for cancellation requests. The default is . + /// A containing the list of items. + public virtual async IAsyncEnumerable RunStreamingAsync( + IEnumerable messages, + AgentThread? thread = null, + AgentRunOptions? options = null, + [EnumeratorCancellation] CancellationToken cancellationToken = default) + { + var response = this.RunStreamingAsync(messages.AsChatMessages(), thread, options, cancellationToken); + + await foreach (var update in response.ConfigureAwait(false)) + { + switch (update.RawRepresentation) + { + case StreamingResponseUpdate rawUpdate: + yield return rawUpdate; + break; + + case ChatResponseUpdate { RawRepresentation: StreamingResponseUpdate rawUpdate }: + yield return rawUpdate; + break; + + default: + // TODO: The OpenAI library does not currently expose model factory methods for creating + // StreamingResponseUpdates. We are thus unable to manufacture such instances when there isn't + // already one in the update and instead skip them. + break; + } + } + } + + /// + public sealed override Task RunAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) => + base.RunAsync(messages, thread, options, cancellationToken); + + /// + public sealed override IAsyncEnumerable RunStreamingAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) => + base.RunStreamingAsync(messages, thread, options, cancellationToken); +} diff --git a/dotnet/src/Microsoft.Agents.AI.Workflows/MessageMerger.cs b/dotnet/src/Microsoft.Agents.AI.Workflows/MessageMerger.cs index 8489d43f86..4560074dd2 100644 --- a/dotnet/src/Microsoft.Agents.AI.Workflows/MessageMerger.cs +++ b/dotnet/src/Microsoft.Agents.AI.Workflows/MessageMerger.cs @@ -72,19 +72,7 @@ internal sealed class MessageMerger throw new InvalidOperationException($"No updates found for message ID '{messageId}' in response '{this.ResponseId}'."); } - return updates.Select(oldUpdate => - oldUpdate.RawRepresentation as ChatResponseUpdate ?? - new() - { - AdditionalProperties = oldUpdate.AdditionalProperties, - AuthorName = oldUpdate.AuthorName, - Contents = oldUpdate.Contents, - CreatedAt = oldUpdate.CreatedAt, - MessageId = oldUpdate.MessageId, - RawRepresentation = oldUpdate.RawRepresentation, - Role = oldUpdate.Role, - ResponseId = oldUpdate.ResponseId, - }).ToChatResponse().Messages; + return updates.Select(oldUpdate => oldUpdate.AsChatResponseUpdate()).ToChatResponse().Messages; } } } diff --git a/dotnet/src/Microsoft.Agents.AI/OpenTelemetryAgent.cs b/dotnet/src/Microsoft.Agents.AI/OpenTelemetryAgent.cs index ba8e35a46c..fafc224e77 100644 --- a/dotnet/src/Microsoft.Agents.AI/OpenTelemetryAgent.cs +++ b/dotnet/src/Microsoft.Agents.AI/OpenTelemetryAgent.cs @@ -182,15 +182,7 @@ public sealed class OpenTelemetryAgent : DelegatingAIAgent, IDisposable var response = await parentAgent.InnerAgent.RunAsync(messages, fo?.Thread, fo?.Options, cancellationToken).ConfigureAwait(false); // Wrap the response in a ChatResponse so we can pass it back through OpenTelemetryChatClient. - return new ChatResponse - { - AdditionalProperties = response.AdditionalProperties, - CreatedAt = response.CreatedAt, - Messages = response.Messages, - RawRepresentation = response, - ResponseId = response.ResponseId, - Usage = response.Usage, - }; + return response.AsChatResponse(); } public async IAsyncEnumerable GetStreamingResponseAsync( @@ -205,17 +197,7 @@ public sealed class OpenTelemetryAgent : DelegatingAIAgent, IDisposable await foreach (var update in parentAgent.InnerAgent.RunStreamingAsync(messages, fo?.Thread, fo?.Options, cancellationToken).ConfigureAwait(false)) { // Wrap the response updates in ChatResponseUpdates so we can pass them back through OpenTelemetryChatClient. - yield return new ChatResponseUpdate - { - AdditionalProperties = update.AdditionalProperties, - AuthorName = update.AuthorName, - Contents = update.Contents, - CreatedAt = update.CreatedAt, - MessageId = update.MessageId, - RawRepresentation = update, - ResponseId = update.ResponseId, - Role = update.Role, - }; + yield return update.AsChatResponseUpdate(); } }