Add an AgentRunResponse ctor accepting a ChatResponse (#204)

* Add an AgentRunResponse ctor accepting a ChatResponse

* Update dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/AgentRunResponseUpdate.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Stephen Toub
2025-07-21 10:49:47 -04:00
committed by GitHub
Unverified
parent a8fadbc49c
commit d964b05ae7
6 changed files with 85 additions and 62 deletions
@@ -44,6 +44,21 @@ public class AgentRunResponse
this.Messages.Add(message);
}
/// <summary>Initializes a new instance of the <see cref="AgentRunResponse"/> class.</summary>
/// <param name="response">The <see cref="ChatResponse"/> from which to seed this <see cref="AgentRunResponse"/>.</param>
/// <exception cref="ArgumentNullException"><paramref name="response"/> is <see langword="null"/>.</exception>
public AgentRunResponse(ChatResponse response)
{
_ = Throw.IfNull(response);
this.AdditionalProperties = response.AdditionalProperties;
this.CreatedAt = response.CreatedAt;
this.Messages = response.Messages;
this.RawRepresentation = response.RawRepresentation;
this.ResponseId = response.ResponseId;
this.Usage = response.Usage;
}
/// <summary>Initializes a new instance of the <see cref="AgentRunResponse"/> class.</summary>
/// <param name="messages">The response messages.</param>
public AgentRunResponse(IList<ChatMessage>? messages)
@@ -5,6 +5,7 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Text.Json.Serialization;
using Microsoft.Shared.Diagnostics;
namespace Microsoft.Extensions.AI.Agents;
@@ -58,6 +59,22 @@ public class AgentRunResponseUpdate
this._contents = contents;
}
/// <summary>Initializes a new instance of the <see cref="AgentRunResponseUpdate"/> class.</summary>
/// <param name="chatResponseUpdate">The <see cref="ChatResponseUpdate"/> from which to seed this <see cref="AgentRunResponseUpdate"/>.</param>
public AgentRunResponseUpdate(ChatResponseUpdate chatResponseUpdate)
{
_ = Throw.IfNull(chatResponseUpdate);
this.AdditionalProperties = chatResponseUpdate.AdditionalProperties;
this.AuthorName = chatResponseUpdate.AuthorName;
this.Contents = chatResponseUpdate.Contents;
this.CreatedAt = chatResponseUpdate.CreatedAt;
this.MessageId = chatResponseUpdate.MessageId;
this.RawRepresentation = chatResponseUpdate.RawRepresentation;
this.ResponseId = chatResponseUpdate.ResponseId;
this.Role = chatResponseUpdate.Role;
}
/// <summary>Gets or sets the name of the author of the response update.</summary>
public string? AuthorName
{
@@ -130,7 +130,7 @@ public sealed class ChatClientAgent : AIAgent
await this.NotifyThreadOfNewMessagesAsync(chatClientThread, chatResponseMessages, cancellationToken).ConfigureAwait(false);
return chatResponse.ToAgentRunResponse(this.Id);
return new(chatResponse) { AgentId = this.Id };
}
/// <inheritdoc/>
@@ -167,7 +167,7 @@ public sealed class ChatClientAgent : AIAgent
{
responseUpdates.Add(update);
update.AuthorName ??= this.Name;
yield return update.ToAgentRunResponseUpdate(this.Id);
yield return new(update) { AgentId = this.Id };
}
hasUpdates = await responseUpdatesEnumerator.MoveNextAsync().ConfigureAwait(false);
@@ -1,60 +0,0 @@
// Copyright (c) Microsoft. All rights reserved.
using Microsoft.Shared.Diagnostics;
namespace Microsoft.Extensions.AI.Agents;
/// <summary>
/// Contains extension methods for <see cref="ChatResponse"/> and <see cref="ChatResponseUpdate"/>.
/// </summary>
internal static class ChatResponseExtensions
{
/// <summary>
/// Converts a <see cref="ChatResponse"/> instance to an <see cref="AgentRunResponse"/>.
/// </summary>
/// <param name="chatResponse">The <see cref="ChatResponse"/> to convert. Cannot be <see langword="null"/>.</param>
/// <param name="agentId">The ID of the agent that generated the response. Cannot be <see langword="null"/>.</param>
/// <returns>
/// An <see cref="AgentRunResponse"/> containing the messages, metadata, and additional properties from the
/// specified <see cref="ChatResponse"/>.
/// </returns>
public static AgentRunResponse ToAgentRunResponse(this ChatResponse chatResponse, string agentId)
{
_ = Throw.IfNull(chatResponse);
_ = Throw.IfNullOrWhitespace(agentId);
return new AgentRunResponse(chatResponse.Messages)
{
AgentId = agentId,
ResponseId = chatResponse.ResponseId,
CreatedAt = chatResponse.CreatedAt,
Usage = chatResponse.Usage,
RawRepresentation = chatResponse,
AdditionalProperties = chatResponse.AdditionalProperties
};
}
/// <summary>
/// Converts a <see cref="ChatResponseUpdate"/> instance to an <see cref="AgentRunResponseUpdate"/>.
/// </summary>
/// <param name="chatResponseUpdate">The <see cref="ChatResponseUpdate"/> to convert. Cannot be <see langword="null"/>.</param>
/// <param name="agentId">The ID of the agent that generated the response. Cannot be <see langword="null"/>.</param>
/// <returns>An <see cref="AgentRunResponseUpdate"/> containing the properties from the specified <see cref="ChatResponseUpdate"/>.</returns>
public static AgentRunResponseUpdate ToAgentRunResponseUpdate(this ChatResponseUpdate chatResponseUpdate, string agentId)
{
_ = Throw.IfNull(chatResponseUpdate);
return new()
{
AgentId = agentId,
Role = chatResponseUpdate.Role,
AuthorName = chatResponseUpdate.AuthorName,
Contents = chatResponseUpdate.Contents,
MessageId = chatResponseUpdate.MessageId,
ResponseId = chatResponseUpdate.ResponseId,
CreatedAt = chatResponseUpdate.CreatedAt,
RawRepresentation = chatResponseUpdate,
AdditionalProperties = chatResponseUpdate.AdditionalProperties
};
}
}
@@ -43,6 +43,28 @@ public class AgentRunResponseTests
Assert.Same(messages, response.Messages);
}
[Fact]
public void ConstructorWithChatResponseRoundtrips()
{
ChatResponse chatResponse = new()
{
AdditionalProperties = new(),
CreatedAt = new DateTimeOffset(2022, 1, 1, 0, 0, 0, TimeSpan.Zero),
Messages = [new(ChatRole.Assistant, "This is a test message.")],
RawRepresentation = new object(),
ResponseId = "responseId",
Usage = new UsageDetails(),
};
AgentRunResponse response = new(chatResponse);
Assert.Same(chatResponse.AdditionalProperties, response.AdditionalProperties);
Assert.Equal(chatResponse.CreatedAt, response.CreatedAt);
Assert.Same(chatResponse.Messages, response.Messages);
Assert.Equal(chatResponse.ResponseId, response.ResponseId);
Assert.Same(chatResponse.RawRepresentation, response.RawRepresentation);
Assert.Same(chatResponse.Usage, response.Usage);
}
[Fact]
public void PropertiesRoundtrip()
{
@@ -24,6 +24,35 @@ public class AgentRunResponseUpdateTests
Assert.Equal(string.Empty, update.ToString());
}
[Fact]
public void ConstructorWithChatResponseUpdateRoundtrips()
{
ChatResponseUpdate chatResponseUpdate = new()
{
AdditionalProperties = new(),
AuthorName = "author",
Contents = [new TextContent("hello")],
ConversationId = "conversationId",
CreatedAt = new DateTimeOffset(2022, 1, 1, 0, 0, 0, TimeSpan.Zero),
FinishReason = ChatFinishReason.Length,
MessageId = "messageId",
ModelId = "modelId",
RawRepresentation = new object(),
ResponseId = "responseId",
Role = ChatRole.Assistant,
};
AgentRunResponseUpdate response = new(chatResponseUpdate);
Assert.Same(chatResponseUpdate.AdditionalProperties, response.AdditionalProperties);
Assert.Equal(chatResponseUpdate.AuthorName, response.AuthorName);
Assert.Same(chatResponseUpdate.Contents, response.Contents);
Assert.Equal(chatResponseUpdate.CreatedAt, response.CreatedAt);
Assert.Equal(chatResponseUpdate.MessageId, response.MessageId);
Assert.Same(chatResponseUpdate.RawRepresentation, response.RawRepresentation);
Assert.Equal(chatResponseUpdate.ResponseId, response.ResponseId);
Assert.Equal(chatResponseUpdate.Role, response.Role);
}
[Fact]
public void PropertiesRoundtrip()
{