// Copyright (c) Microsoft. All rights reserved. using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.AI; using Microsoft.Extensions.Logging; using Microsoft.Shared.DiagnosticIds; using Microsoft.Shared.Diagnostics; namespace Microsoft.Agents.AI.Compaction; /// /// A compaction strategy that delegates to an to reduce the conversation's /// included messages. /// /// /// /// This strategy bridges the abstraction from Microsoft.Extensions.AI /// into the compaction pipeline. It collects the currently included messages from the /// , passes them to the reducer, and rebuilds the index from the /// reduced message list when the reducer produces fewer messages. /// /// /// The controls when reduction is attempted. /// Use for common trigger conditions such as token or message thresholds. /// /// /// Use this strategy when you have an existing implementation /// (such as MessageCountingChatReducer) and want to apply it as part of a /// pipeline or as an in-run compaction strategy. /// /// [Experimental(DiagnosticIds.Experiments.AgentsAIExperiments)] public sealed class ChatReducerCompactionStrategy : CompactionStrategy { /// /// Initializes a new instance of the class. /// /// /// The that performs the message reduction. /// /// /// The that controls when compaction proceeds. /// public ChatReducerCompactionStrategy(IChatReducer chatReducer, CompactionTrigger trigger) : base(trigger) { this.ChatReducer = Throw.IfNull(chatReducer); } /// /// Gets the chat reducer used to reduce messages. /// public IChatReducer ChatReducer { get; } /// protected override async ValueTask CompactCoreAsync(CompactionMessageIndex index, ILogger logger, CancellationToken cancellationToken) { // No need to short-circuit on empty conversations, this is handled by . List includedMessages = [.. index.GetIncludedMessages()]; IEnumerable reduced = await this.ChatReducer.ReduceAsync(includedMessages, cancellationToken).ConfigureAwait(false); IList reducedMessages = reduced as IList ?? [.. reduced]; if (reducedMessages.Count >= includedMessages.Count) { return false; } // Rebuild the index from the reduced messages CompactionMessageIndex rebuilt = CompactionMessageIndex.Create(reducedMessages, index.Tokenizer); index.Groups.Clear(); foreach (CompactionMessageGroup group in rebuilt.Groups) { index.Groups.Add(group); } return true; } }