// Copyright (c) Microsoft. All rights reserved.
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Text.Json.Serialization;
using Microsoft.Extensions.AI;
using Microsoft.Shared.DiagnosticIds;
namespace Microsoft.Agents.AI.Compaction;
///
/// Represents a logical group of instances that must be kept or removed together during compaction.
///
///
///
/// Message groups ensure atomic preservation of related messages. For example, an assistant message
/// containing tool calls and its corresponding tool result messages form a
/// group — removing one without the other would cause LLM API errors.
///
///
/// Groups also support exclusion semantics: a group can be marked as excluded (with an optional reason)
/// to indicate it should not be included in the messages sent to the model, while still being preserved
/// for diagnostics, storage, or later re-inclusion.
///
///
/// Each group tracks its , , and
/// so that can efficiently aggregate totals across all or only included groups.
///
///
[Experimental(DiagnosticIds.Experiments.AgentsAIExperiments)]
public sealed class CompactionMessageGroup
{
///
/// The key used to identify a message as a compaction summary.
///
///
/// When this key is present with a value of , the message is classified as
/// by .
///
public static readonly string SummaryPropertyKey = "_is_summary";
///
/// Initializes a new instance of the class.
///
/// The kind of message group.
/// The messages in this group. The list is captured as a read-only snapshot.
/// The total UTF-8 byte count of the text content in the messages.
/// The token count for the messages, computed by a tokenizer or estimated.
///
/// The user turn this group belongs to, or for .
///
[JsonConstructor]
internal CompactionMessageGroup(CompactionGroupKind kind, IReadOnlyList messages, int byteCount, int tokenCount, int? turnIndex = null)
{
this.Kind = kind;
this.Messages = messages;
this.MessageCount = messages.Count;
this.ByteCount = byteCount;
this.TokenCount = tokenCount;
this.TurnIndex = turnIndex;
}
///
/// Gets the kind of this message group.
///
public CompactionGroupKind Kind { get; }
///
/// Gets the messages in this group.
///
public IReadOnlyList Messages { get; }
///
/// Gets the number of messages in this group.
///
public int MessageCount { get; }
///
/// Gets the total UTF-8 byte count of the text content in this group's messages.
///
public int ByteCount { get; }
///
/// Gets the estimated or actual token count for this group's messages.
///
public int TokenCount { get; }
///
/// Gets user turn index this group belongs to, or for groups
/// that precede the first user message (e.g., system messages). A turn index of 0
/// corresponds with any non-system message that precedes the first user message,
/// turn index 1 corresponds with the first user message and its subsequent non-user
/// messages, and so on...
///
///
/// A turn starts with a group and includes all subsequent
/// non-user, non-system groups until the next user group or end of conversation. System messages
/// () are always assigned a turn index
/// since they never belong to a user turn.
///
public int? TurnIndex { get; }
///
/// Gets or sets a value indicating whether this group is excluded from the projected message list.
///
///
/// Excluded groups are preserved in the collection for diagnostics or storage purposes
/// but are not included when calling .
///
public bool IsExcluded { get; set; }
///
/// Gets or sets an optional reason explaining why this group was excluded.
///
public string? ExcludeReason { get; set; }
}