.NET: [BREAKING] Add consistent message filtering to all providers. (#3851)

* Add consistent message filtering to all providers.

* Remove old chat history filtering classes

* Fix merge issues

* Fix unit test

* Enforce non-nullable property

* Fix merging bug and make troubleshooting source info easier by adding tostring implementation
This commit is contained in:
westey
2026-02-12 10:50:13 +00:00
committed by GitHub
Unverified
parent c99df98547
commit de82ffd40a
26 changed files with 918 additions and 502 deletions
@@ -27,9 +27,14 @@ namespace Microsoft.Agents.AI;
/// </remarks>
public sealed class InMemoryChatHistoryProvider : ChatHistoryProvider
{
private static IEnumerable<ChatMessage> DefaultExcludeChatHistoryFilter(IEnumerable<ChatMessage> messages)
=> messages.Where(m => m.GetAgentRequestMessageSourceType() != AgentRequestMessageSourceType.ChatHistory);
private readonly string _stateKey;
private readonly Func<AgentSession?, State> _stateInitializer;
private readonly JsonSerializerOptions _jsonSerializerOptions;
private readonly Func<IEnumerable<ChatMessage>, IEnumerable<ChatMessage>> _storageInputMessageFilter;
private readonly Func<IEnumerable<ChatMessage>, IEnumerable<ChatMessage>>? _retrievalOutputMessageFilter;
/// <summary>
/// Initializes a new instance of the <see cref="InMemoryChatHistoryProvider"/> class.
@@ -45,6 +50,8 @@ public sealed class InMemoryChatHistoryProvider : ChatHistoryProvider
this.ReducerTriggerEvent = options?.ReducerTriggerEvent ?? InMemoryChatHistoryProviderOptions.ChatReducerTriggerEvent.BeforeMessagesRetrieval;
this._stateKey = options?.StateKey ?? base.StateKey;
this._jsonSerializerOptions = options?.JsonSerializerOptions ?? AgentAbstractionsJsonUtilities.DefaultOptions;
this._storageInputMessageFilter = options?.StorageInputMessageFilter ?? DefaultExcludeChatHistoryFilter;
this._retrievalOutputMessageFilter = options?.RetrievalOutputMessageFilter;
}
/// <inheritdoc />
@@ -115,7 +122,12 @@ public sealed class InMemoryChatHistoryProvider : ChatHistoryProvider
state.Messages = (await this.ChatReducer.ReduceAsync(state.Messages, cancellationToken).ConfigureAwait(false)).ToList();
}
return state.Messages
IEnumerable<ChatMessage> output = state.Messages;
if (this._retrievalOutputMessageFilter is not null)
{
output = this._retrievalOutputMessageFilter(output);
}
return output
.Select(message => message.WithAgentRequestMessageSource(AgentRequestMessageSourceType.ChatHistory, this.GetType().FullName!))
.Concat(context.RequestMessages);
}
@@ -133,7 +145,7 @@ public sealed class InMemoryChatHistoryProvider : ChatHistoryProvider
var state = this.GetOrInitializeState(context.Session);
// Add request and response messages to the provider
var allNewMessages = context.RequestMessages.Concat(context.ResponseMessages ?? []);
var allNewMessages = this._storageInputMessageFilter(context.RequestMessages).Concat(context.ResponseMessages ?? []);
state.Messages.AddRange(allNewMessages);
if (this.ReducerTriggerEvent is InMemoryChatHistoryProviderOptions.ChatReducerTriggerEvent.AfterMessageAdded && this.ChatReducer is not null)