// 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;
}
}