Skip to content

Commit

Permalink
.Net: Moved IChatHistoryReducer from Agents to SK packages (#10285)
Browse files Browse the repository at this point in the history
### Motivation and Context

<!-- Thank you for your contribution to the semantic-kernel repo!
Please help reviewers and future users, providing the following
information:
  1. Why is this change required?
  2. What problem does it solve?
  3. What scenario does it contribute to?
  4. If it fixes an open issue, please link to the issue here.
-->

Resolves: #10203

This PR:
- Moves `IChatHistoryReducer` from `Agents.Core` to
`Microsoft.SemanticKernel.Abstractions` package.
- Moves implementations `ChatHistorySummarizationReducer` and
`ChatHistoryTruncationReducer` from `Agents.Core` to
`Microsoft.SemanticKernel.Core` package.

The functionality is marked as experimental.

### Contribution Checklist

<!-- Before submitting this PR, please make sure: -->

- [x] The code builds clean without any errors or warnings
- [x] The PR follows the [SK Contribution
Guidelines](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md)
and the [pre-submission formatting
script](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md#development-scripts)
raises no violations
- [x] All unit tests pass, and I have added new tests where possible
- [x] I didn't break anyone 😄
  • Loading branch information
dmytrostruk authored Jan 27, 2025
1 parent 5098ba3 commit b592a4a
Show file tree
Hide file tree
Showing 26 changed files with 240 additions and 491 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright (c) Microsoft. All rights reserved.

using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Agents;
using Microsoft.SemanticKernel.Agents.History;
using Microsoft.SemanticKernel.ChatCompletion;

namespace Agents;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ internal static class ChatHistoryExtensions
/// <remarks>
/// For simplicity only a single system message is supported in these examples.
/// </remarks>
internal static ChatMessageContent? GetSystemMessage(this ChatHistory chatHistory)
internal static ChatMessageContent? GetSystemMessage(this IReadOnlyList<ChatMessageContent> chatHistory)
{
return chatHistory.FirstOrDefault(m => m.Role == AuthorRole.System);
}
Expand All @@ -34,7 +34,9 @@ internal static class ChatHistoryExtensions
/// <param name="summaryMessage">An optional summary messageContent to include</param>
/// <param name="messageFilter">An optional message filter</param>
public static IEnumerable<ChatMessageContent> Extract(
this ChatHistory chatHistory, int startIndex, int? endIndex = null,
this IReadOnlyList<ChatMessageContent> chatHistory,
int startIndex,
int? endIndex = null,
ChatMessageContent? systemMessage = null,
ChatMessageContent? summaryMessage = null,
Func<ChatMessageContent, bool>? messageFilter = null)
Expand Down Expand Up @@ -71,11 +73,11 @@ public static IEnumerable<ChatMessageContent> Extract(
/// <summary>
/// Compute the index truncation where truncation should begin using the current truncation threshold.
/// </summary>
/// <param name="chatHistory">ChatHistory instance to be truncated</param>
/// <param name="truncatedSize"></param>
/// <param name="truncationThreshold"></param>
/// <param name="chatHistory">The source history.</param>
/// <param name="truncatedSize">Truncated size.</param>
/// <param name="truncationThreshold">Truncation threshold.</param>
/// <param name="hasSystemMessage">Flag indicating whether or not the chat history contains a system messageContent</param>
public static int ComputeTruncationIndex(this ChatHistory chatHistory, int truncatedSize, int truncationThreshold, bool hasSystemMessage)
public static int ComputeTruncationIndex(this IReadOnlyList<ChatMessageContent> chatHistory, int truncatedSize, int truncationThreshold, bool hasSystemMessage)
{
if (chatHistory.Count <= truncationThreshold)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@ namespace ChatCompletion;
/// <remarks>
/// This reducer requires that the ChatMessageContent.MetaData contains a TokenCount property.
/// </remarks>
public sealed class MaxTokensChatHistoryReducer : IChatHistoryReducer
public sealed class ChatHistoryMaxTokensReducer : IChatHistoryReducer
{
private readonly int _maxTokenCount;

/// <summary>
/// Creates a new instance of <see cref="MaxTokensChatHistoryReducer"/>.
/// Creates a new instance of <see cref="ChatHistoryMaxTokensReducer"/>.
/// </summary>
/// <param name="maxTokenCount">Max token count to send to the model.</param>
public MaxTokensChatHistoryReducer(int maxTokenCount)
public ChatHistoryMaxTokensReducer(int maxTokenCount)
{
if (maxTokenCount <= 0)
{
Expand All @@ -30,7 +30,7 @@ public MaxTokensChatHistoryReducer(int maxTokenCount)
}

/// <inheritdoc/>
public Task<IEnumerable<ChatMessageContent>?> ReduceAsync(ChatHistory chatHistory, CancellationToken cancellationToken = default)
public Task<IEnumerable<ChatMessageContent>?> ReduceAsync(IReadOnlyList<ChatMessageContent> chatHistory, CancellationToken cancellationToken = default)
{
var systemMessage = chatHistory.GetSystemMessage();

Expand All @@ -47,12 +47,13 @@ public MaxTokensChatHistoryReducer(int maxTokenCount)
}

#region private

/// <summary>
/// Compute the index truncation where truncation should begin using the current truncation threshold.
/// </summary>
/// <param name="chatHistory">ChatHistory instance to be truncated</param>
/// <param name="chatHistory">Chat history to be truncated.</param>
/// <param name="systemMessage">The system message</param>
private int ComputeTruncationIndex(ChatHistory chatHistory, ChatMessageContent? systemMessage)
private int ComputeTruncationIndex(IReadOnlyList<ChatMessageContent> chatHistory, ChatMessageContent? systemMessage)
{
var truncationIndex = -1;

Expand Down Expand Up @@ -83,5 +84,6 @@ private int ComputeTruncationIndex(ChatHistory chatHistory, ChatMessageContent?

return truncationIndex;
}

#endregion
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,31 +11,6 @@ namespace ChatCompletion;
/// </summary>
public class ChatHistoryReducerTests(ITestOutputHelper output) : BaseTest(output)
{
[Theory]
[InlineData(3, null, null, 5, 0)]
[InlineData(2, null, null, 1, 1)]
[InlineData(2, "SystemMessage", null, 2, 2)]
[InlineData(10, null, null, 3, 3)]
[InlineData(10, "SystemMessage", null, 3, 3)]
[InlineData(9, null, null, 5, 5)]
[InlineData(11, null, null, 5, 5)]
[InlineData(8, "SystemMessage", null, 5, 5)]
[InlineData(10, "SystemMessage", null, 5, 5)]
[InlineData(3, null, new int[] { 0 }, 3, 2)]
[InlineData(3, "SystemMessage", new int[] { 0 }, 4, 3)]
public async Task VerifyTruncatingChatHistoryReducerAsync(int messageCount, string? systemMessage, int[]? functionCallIndexes, int truncatedSize, int expectedSize)
{
// Arrange
var chatHistory = CreateHistoryWithUserInput(messageCount, systemMessage, functionCallIndexes);
var reducer = new TruncatingChatHistoryReducer(truncatedSize);

// Act
var reducedHistory = await reducer.ReduceAsync(chatHistory);

// Assert
VerifyReducedHistory(reducedHistory, ComputeExpectedMessages(chatHistory, expectedSize));
}

[Theory]
[InlineData(3, null, null, 100, 0)]
[InlineData(3, "SystemMessage", null, 100, 0)]
Expand All @@ -47,30 +22,7 @@ public async Task VerifyMaxTokensChatHistoryReducerAsync(int messageCount, strin
{
// Arrange
var chatHistory = CreateHistoryWithUserInput(messageCount, systemMessage, functionCallIndexes, true);
var reducer = new MaxTokensChatHistoryReducer(maxTokens);

// Act
var reducedHistory = await reducer.ReduceAsync(chatHistory);

// Assert
VerifyReducedHistory(reducedHistory, ComputeExpectedMessages(chatHistory, expectedSize));
}

[Theory]
[InlineData(3, null, null, 5, 10, 0)]
[InlineData(10, null, null, 5, 10, 6)]
[InlineData(10, "SystemMessage", null, 5, 10, 6)]
[InlineData(10, null, new int[] { 1 }, 5, 10, 6)]
[InlineData(10, "SystemMessage", new int[] { 2 }, 5, 10, 6)]
public async Task VerifySummarizingChatHistoryReducerAsync(int messageCount, string? systemMessage, int[]? functionCallIndexes, int truncatedSize, int truncationThreshold, int expectedSize)
{
// Arrange
Assert.NotNull(TestConfiguration.OpenAI.ChatModelId);
Assert.NotNull(TestConfiguration.OpenAI.ApiKey);
IChatCompletionService chatClient = new FakeChatCompletionService("The dialog consists of repetitive interaction where both the user and assistant exchange identical phrases in Latin.");

var chatHistory = CreateHistoryWithUserInput(messageCount, systemMessage, functionCallIndexes, true);
var reducer = new SummarizingChatHistoryReducer(chatClient, truncatedSize, truncationThreshold);
var reducer = new ChatHistoryMaxTokensReducer(maxTokens);

// Act
var reducedHistory = await reducer.ReduceAsync(chatHistory);
Expand Down

This file was deleted.

This file was deleted.

Loading

0 comments on commit b592a4a

Please sign in to comment.