File: ChatCompletion\ChatResponse.cs
Web Access
Project: src\src\Libraries\Microsoft.Extensions.AI.Abstractions\Microsoft.Extensions.AI.Abstractions.csproj (Microsoft.Extensions.AI.Abstractions)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Text.Json.Serialization;
using Microsoft.Shared.Diagnostics;
 
namespace Microsoft.Extensions.AI;
 
/// <summary>Represents the response to a chat request.</summary>
/// <remarks>
/// <see cref="ChatResponse"/> provides one or more response messages and metadata about the response.
/// A typical response will contain a single message, however a response may contain multiple messages
/// in a variety of scenarios. For example, if automatic function calling is employed, such that a single
/// request to a <see cref="IChatClient"/> may actually generate multiple roundtrips to an inner <see cref="IChatClient"/>
/// it uses, all of the involved messages may be surfaced as part of the final <see cref="ChatResponse"/>.
/// </remarks>
public class ChatResponse
{
    /// <summary>The response messages.</summary>
    private IList<ChatMessage>? _messages;
 
    /// <summary>Initializes a new instance of the <see cref="ChatResponse"/> class.</summary>
    public ChatResponse()
    {
    }
 
    /// <summary>Initializes a new instance of the <see cref="ChatResponse"/> class.</summary>
    /// <param name="message">The response message.</param>
    /// <exception cref="ArgumentNullException"><paramref name="message"/> is <see langword="null"/>.</exception>
    public ChatResponse(ChatMessage message)
    {
        _ = Throw.IfNull(message);
 
        Messages.Add(message);
    }
 
    /// <summary>Initializes a new instance of the <see cref="ChatResponse"/> class.</summary>
    /// <param name="messages">The response messages.</param>
    public ChatResponse(IList<ChatMessage>? messages)
    {
        _messages = messages;
    }
 
    /// <summary>Gets or sets the chat response messages.</summary>
    [AllowNull]
    public IList<ChatMessage> Messages
    {
        get => _messages ??= new List<ChatMessage>(1);
        set => _messages = value;
    }
 
    /// <summary>Gets the text of the response.</summary>
    /// <remarks>
    /// This property concatenates the <see cref="ChatMessage.Text"/> of all <see cref="ChatMessage"/>
    /// instances in <see cref="Messages"/>.
    /// </remarks>
    [JsonIgnore]
    public string Text => _messages?.ConcatText() ?? string.Empty;
 
    /// <summary>Gets or sets the ID of the chat response.</summary>
    public string? ResponseId { get; set; }
 
    /// <summary>Gets or sets an identifier for the state of the conversation.</summary>
    /// <remarks>
    /// Some <see cref="IChatClient"/> implementations are capable of storing the state for a conversation, such that
    /// the input messages supplied to <see cref="IChatClient.GetResponseAsync"/> need only be the additional messages beyond
    /// what's already stored. If this property is non-<see langword="null"/>, it represents an identifier for that state,
    /// and it should be used in a subsequent <see cref="ChatOptions.ConversationId"/> instead of supplying the same messages
    /// (and this <see cref="ChatResponse"/>'s message) as part of the <c>messages</c> parameter. Note that the value may
    /// or may not differ on every response, depending on whether the underlying provider uses a fixed ID for each conversation
    /// or updates it for each message.
    /// </remarks>
    /// <remarks>This method is obsolete. Use <see cref="ConversationId"/> instead.</remarks>
    [Obsolete("Use ConversationId instead.")]
    [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
    public string? ChatThreadId
    {
        get => ConversationId;
        set => ConversationId = value;
    }
 
    /// <summary>Gets or sets an identifier for the state of the conversation.</summary>
    /// <remarks>
    /// Some <see cref="IChatClient"/> implementations are capable of storing the state for a conversation, such that
    /// the input messages supplied to <see cref="IChatClient.GetResponseAsync"/> need only be the additional messages beyond
    /// what's already stored. If this property is non-<see langword="null"/>, it represents an identifier for that state,
    /// and it should be used in a subsequent <see cref="ChatOptions.ConversationId"/> instead of supplying the same messages
    /// (and this <see cref="ChatResponse"/>'s message) as part of the <c>messages</c> parameter. Note that the value may
    /// or may not differ on every response, depending on whether the underlying provider uses a fixed ID for each conversation
    /// or updates it for each message.
    /// </remarks>
    public string? ConversationId { get; set; }
 
    /// <summary>Gets or sets the model ID used in the creation of the chat response.</summary>
    public string? ModelId { get; set; }
 
    /// <summary>Gets or sets a timestamp for the chat response.</summary>
    public DateTimeOffset? CreatedAt { get; set; }
 
    /// <summary>Gets or sets the reason for the chat response.</summary>
    public ChatFinishReason? FinishReason { get; set; }
 
    /// <summary>Gets or sets usage details for the chat response.</summary>
    public UsageDetails? Usage { get; set; }
 
    /// <summary>Gets or sets the raw representation of the chat response from an underlying implementation.</summary>
    /// <remarks>
    /// If a <see cref="ChatResponse"/> is created to represent some underlying object from another object
    /// model, this property can be used to store that original object. This can be useful for debugging or
    /// for enabling a consumer to access the underlying object model if needed.
    /// </remarks>
    [JsonIgnore]
    public object? RawRepresentation { get; set; }
 
    /// <summary>Gets or sets any additional properties associated with the chat response.</summary>
    public AdditionalPropertiesDictionary? AdditionalProperties { get; set; }
 
    /// <inheritdoc />
    public override string ToString() => Text;
 
    /// <summary>Creates an array of <see cref="ChatResponseUpdate" /> instances that represent this <see cref="ChatResponse" />.</summary>
    /// <returns>An array of <see cref="ChatResponseUpdate" /> instances that may be used to represent this <see cref="ChatResponse" />.</returns>
    public ChatResponseUpdate[] ToChatResponseUpdates()
    {
        ChatResponseUpdate? extra = null;
        if (AdditionalProperties is not null || Usage is not null)
        {
            extra = new ChatResponseUpdate
            {
                AdditionalProperties = AdditionalProperties
            };
 
            if (Usage is { } usage)
            {
                extra.Contents.Add(new UsageContent(usage));
            }
        }
 
        int messageCount = _messages?.Count ?? 0;
        var updates = new ChatResponseUpdate[messageCount + (extra is not null ? 1 : 0)];
 
        int i;
        for (i = 0; i < messageCount; i++)
        {
            ChatMessage message = _messages![i];
            updates[i] = new ChatResponseUpdate
            {
                ConversationId = ConversationId,
 
                AdditionalProperties = message.AdditionalProperties,
                AuthorName = message.AuthorName,
                Contents = message.Contents,
                RawRepresentation = message.RawRepresentation,
                Role = message.Role,
 
                ResponseId = ResponseId,
                MessageId = message.MessageId,
                CreatedAt = CreatedAt,
                FinishReason = FinishReason,
                ModelId = ModelId
            };
        }
 
        if (extra is not null)
        {
            updates[i] = extra;
        }
 
        return updates;
    }
}