From 9b60e9dfc3bdebc3b4c56c9e38ce102cb05f246a Mon Sep 17 00:00:00 2001 From: westey <164392973+westey-m@users.noreply.github.com> Date: Thu, 26 Feb 2026 17:19:38 +0000 Subject: [PATCH 1/4] Add response filter for store input for *Providers --- .../01-get-started/04_memory/Program.cs | 1 - .../Program.cs | 2 +- .../Program.cs | 2 +- .../Program.cs | 1 - .../Program.cs | 4 +- .../AIContextProvider.cs | 24 +++-- .../ChatHistoryProvider.cs | 18 ++-- .../InMemoryChatHistoryProvider.cs | 3 +- .../InMemoryChatHistoryProviderOptions.cs | 15 ++- .../MessageAIContextProvider.cs | 8 +- .../CosmosChatHistoryProvider.cs | 24 +++-- .../FoundryMemoryProvider.cs | 2 +- .../FoundryMemoryProviderOptions.cs | 12 ++- .../Microsoft.Agents.AI.Mem0/Mem0Provider.cs | 2 +- .../Mem0ProviderOptions.cs | 12 ++- .../WorkflowChatHistoryProvider.cs | 1 - .../Memory/ChatHistoryMemoryProvider.cs | 2 +- .../ChatHistoryMemoryProviderOptions.cs | 12 ++- .../Microsoft.Agents.AI/TextSearchProvider.cs | 2 +- .../TextSearchProviderOptions.cs | 12 ++- .../AIContextProviderTests.cs | 98 ++++++++++++++++++- .../ChatHistoryProviderTests.cs | 17 +++- .../InMemoryChatHistoryProviderTests.cs | 2 +- .../CosmosChatHistoryProviderTests.cs | 2 +- .../Mem0ProviderTests.cs | 2 +- .../ChatClient/ChatClientAgentOptionsTests.cs | 8 +- .../ChatClient/ChatClientAgentTests.cs | 22 ++--- ...hatClientAgent_BackgroundResponsesTests.cs | 16 +-- ...tClientAgent_ChatHistoryManagementTests.cs | 8 +- .../Data/TextSearchProviderTests.cs | 2 +- .../Memory/ChatHistoryMemoryProviderTests.cs | 2 +- 31 files changed, 255 insertions(+), 83 deletions(-) diff --git a/dotnet/samples/01-get-started/04_memory/Program.cs b/dotnet/samples/01-get-started/04_memory/Program.cs index fa6940f5fd..3705e64f3a 100644 --- a/dotnet/samples/01-get-started/04_memory/Program.cs +++ b/dotnet/samples/01-get-started/04_memory/Program.cs @@ -92,7 +92,6 @@ internal sealed class UserInfoMemory : AIContextProvider private readonly IChatClient _chatClient; public UserInfoMemory(IChatClient chatClient, Func? stateInitializer = null) - : base(null, null) { this._sessionState = new ProviderSessionState( stateInitializer ?? (_ => new UserInfo()), diff --git a/dotnet/samples/02-agents/AgentWithRAG/AgentWithRAG_Step01_BasicTextRAG/Program.cs b/dotnet/samples/02-agents/AgentWithRAG/AgentWithRAG_Step01_BasicTextRAG/Program.cs index c04601d940..e1db6d3f4f 100644 --- a/dotnet/samples/02-agents/AgentWithRAG/AgentWithRAG_Step01_BasicTextRAG/Program.cs +++ b/dotnet/samples/02-agents/AgentWithRAG/AgentWithRAG_Step01_BasicTextRAG/Program.cs @@ -73,7 +73,7 @@ // We also want to maintain that exclusion here. ChatHistoryProvider = new InMemoryChatHistoryProvider(new InMemoryChatHistoryProviderOptions { - StorageInputMessageFilter = messages => messages.Where(m => m.GetAgentRequestMessageSourceType() != AgentRequestMessageSourceType.AIContextProvider && m.GetAgentRequestMessageSourceType() != AgentRequestMessageSourceType.ChatHistory) + StorageInputRequestMessageFilter = messages => messages.Where(m => m.GetAgentRequestMessageSourceType() != AgentRequestMessageSourceType.AIContextProvider && m.GetAgentRequestMessageSourceType() != AgentRequestMessageSourceType.ChatHistory) }), }); diff --git a/dotnet/samples/02-agents/AgentWithRAG/AgentWithRAG_Step02_CustomVectorStoreRAG/Program.cs b/dotnet/samples/02-agents/AgentWithRAG/AgentWithRAG_Step02_CustomVectorStoreRAG/Program.cs index 0c299a1445..0f65121c04 100644 --- a/dotnet/samples/02-agents/AgentWithRAG/AgentWithRAG_Step02_CustomVectorStoreRAG/Program.cs +++ b/dotnet/samples/02-agents/AgentWithRAG/AgentWithRAG_Step02_CustomVectorStoreRAG/Program.cs @@ -80,7 +80,7 @@ // You may choose to persist the TextSearchProvider messages, if you want the search output to be provided to the model in future interactions as well. ChatHistoryProvider = new InMemoryChatHistoryProvider(new InMemoryChatHistoryProviderOptions() { - StorageInputMessageFilter = msgs => msgs.Where(m => m.GetAgentRequestMessageSourceType() != AgentRequestMessageSourceType.ChatHistory && m.GetAgentRequestMessageSourceType() != AgentRequestMessageSourceType.AIContextProvider) + StorageInputRequestMessageFilter = msgs => msgs.Where(m => m.GetAgentRequestMessageSourceType() != AgentRequestMessageSourceType.ChatHistory && m.GetAgentRequestMessageSourceType() != AgentRequestMessageSourceType.AIContextProvider) }) }); diff --git a/dotnet/samples/02-agents/Agents/Agent_Step04_3rdPartyChatHistoryStorage/Program.cs b/dotnet/samples/02-agents/Agents/Agent_Step04_3rdPartyChatHistoryStorage/Program.cs index cbcf14157e..63fa5c0751 100644 --- a/dotnet/samples/02-agents/Agents/Agent_Step04_3rdPartyChatHistoryStorage/Program.cs +++ b/dotnet/samples/02-agents/Agents/Agent_Step04_3rdPartyChatHistoryStorage/Program.cs @@ -85,7 +85,6 @@ public VectorChatHistoryProvider( VectorStore vectorStore, Func? stateInitializer = null, string? stateKey = null) - : base(provideOutputMessageFilter: null, storeInputMessageFilter: null) { this._sessionState = new ProviderSessionState( stateInitializer ?? (_ => new State(Guid.NewGuid().ToString("N"))), diff --git a/dotnet/samples/02-agents/Agents/Agent_Step17_AdditionalAIContext/Program.cs b/dotnet/samples/02-agents/Agents/Agent_Step17_AdditionalAIContext/Program.cs index a341abe8cd..e3913c9f0e 100644 --- a/dotnet/samples/02-agents/Agents/Agent_Step17_AdditionalAIContext/Program.cs +++ b/dotnet/samples/02-agents/Agents/Agent_Step17_AdditionalAIContext/Program.cs @@ -49,11 +49,11 @@ You remind users of upcoming calendar events when the user interacts with you. """ }, ChatHistoryProvider = new InMemoryChatHistoryProvider(new InMemoryChatHistoryProviderOptions { - // Use StorageInputMessageFilter to provide a custom filter for messages stored in chat history. + // Use StorageInputRequestMessageFilter to provide a custom filter for request messages stored in chat history. // By default the chat history provider will store all messages, except for those that came from chat history in the first place. // In this case, we want to also exclude messages that came from AI context providers. // You may want to store these messages, depending on their content and your requirements. - StorageInputMessageFilter = messages => messages.Where(m => m.GetAgentRequestMessageSourceType() != AgentRequestMessageSourceType.AIContextProvider && m.GetAgentRequestMessageSourceType() != AgentRequestMessageSourceType.ChatHistory) + StorageInputRequestMessageFilter = messages => messages.Where(m => m.GetAgentRequestMessageSourceType() != AgentRequestMessageSourceType.AIContextProvider && m.GetAgentRequestMessageSourceType() != AgentRequestMessageSourceType.ChatHistory) }), // Add multiple AI context providers: one that maintains a todo list and one that provides upcoming calendar entries. // The agent will call each provider in sequence, accumulating context from each. diff --git a/dotnet/src/Microsoft.Agents.AI.Abstractions/AIContextProvider.cs b/dotnet/src/Microsoft.Agents.AI.Abstractions/AIContextProvider.cs index 7ac4eed18c..d068686245 100644 --- a/dotnet/src/Microsoft.Agents.AI.Abstractions/AIContextProvider.cs +++ b/dotnet/src/Microsoft.Agents.AI.Abstractions/AIContextProvider.cs @@ -33,18 +33,23 @@ public abstract class AIContextProvider { private static IEnumerable DefaultExternalOnlyFilter(IEnumerable messages) => messages.Where(m => m.GetAgentRequestMessageSourceType() == AgentRequestMessageSourceType.External); + private static IEnumerable DefaultNoopFilter(IEnumerable messages) + => messages; /// /// Initializes a new instance of the class. /// /// An optional filter function to apply to input messages before providing context via . If not set, defaults to including only messages. - /// An optional filter function to apply to request messages before storing context via . If not set, defaults to including only messages. + /// An optional filter function to apply to request messages before storing context via . If not set, defaults to including only messages. + /// An optional filter function to apply to response messages before storing context via . If not set, defaults to including only messages. protected AIContextProvider( Func, IEnumerable>? provideInputMessageFilter = null, - Func, IEnumerable>? storeInputMessageFilter = null) + Func, IEnumerable>? storeInputRequestMessageFilter = null, + Func, IEnumerable>? storeInputResponseMessageFilter = null) { this.ProvideInputMessageFilter = provideInputMessageFilter ?? DefaultExternalOnlyFilter; - this.StoreInputMessageFilter = storeInputMessageFilter ?? DefaultExternalOnlyFilter; + this.StoreInputRequestMessageFilter = storeInputRequestMessageFilter ?? DefaultExternalOnlyFilter; + this.StoreInputResponseMessageFilter = storeInputResponseMessageFilter ?? DefaultNoopFilter; } /// @@ -55,7 +60,12 @@ protected AIContextProvider( /// /// Gets the filter function to apply to request messages before storing context via . /// - protected Func, IEnumerable> StoreInputMessageFilter { get; } + protected Func, IEnumerable> StoreInputRequestMessageFilter { get; } + + /// + /// Gets the filter function to apply to response messages before storing context via . + /// + protected Func, IEnumerable> StoreInputResponseMessageFilter { get; } /// /// Gets the key used to store the provider state in the . @@ -245,7 +255,9 @@ public ValueTask InvokedAsync(InvokedContext context, CancellationToken cancella /// /// /// The default implementation of this method skips execution for any invocation failures, - /// filters the request messages using the configured store-input message filter + /// filters the request messages using the configured store-input request message filter + /// (which defaults to including only messages), + /// filters the response messages using the configured store-input response message filter /// (which defaults to including only messages), /// and calls to process the invocation results. /// For most scenarios, overriding is sufficient to process invocation results, @@ -261,7 +273,7 @@ protected virtual ValueTask InvokedCoreAsync(InvokedContext context, Cancellatio return default; } - var subContext = new InvokedContext(context.Agent, context.Session, this.StoreInputMessageFilter(context.RequestMessages), context.ResponseMessages!); + var subContext = new InvokedContext(context.Agent, context.Session, this.StoreInputRequestMessageFilter(context.RequestMessages), this.StoreInputResponseMessageFilter(context.ResponseMessages!)); return this.StoreAIContextAsync(subContext, cancellationToken); } diff --git a/dotnet/src/Microsoft.Agents.AI.Abstractions/ChatHistoryProvider.cs b/dotnet/src/Microsoft.Agents.AI.Abstractions/ChatHistoryProvider.cs index ad3f3aacfb..1d629c4cd6 100644 --- a/dotnet/src/Microsoft.Agents.AI.Abstractions/ChatHistoryProvider.cs +++ b/dotnet/src/Microsoft.Agents.AI.Abstractions/ChatHistoryProvider.cs @@ -42,21 +42,27 @@ public abstract class ChatHistoryProvider { private static IEnumerable DefaultExcludeChatHistoryFilter(IEnumerable messages) => messages.Where(m => m.GetAgentRequestMessageSourceType() != AgentRequestMessageSourceType.ChatHistory); + private static IEnumerable DefaultNoopFilter(IEnumerable messages) + => messages; private readonly Func, IEnumerable>? _provideOutputMessageFilter; - private readonly Func, IEnumerable> _storeInputMessageFilter; + private readonly Func, IEnumerable> _storeInputRequestMessageFilter; + private readonly Func, IEnumerable> _storeInputResponseMessageFilter; /// /// Initializes a new instance of the class. /// /// An optional filter function to apply to messages when retrieving them from the chat history. - /// An optional filter function to apply to messages before storing them in the chat history. If not set, defaults to excluding messages with source type . + /// An optional filter function to apply to request messages before storing them in the chat history. If not set, defaults to excluding messages with source type . + /// An optional filter function to apply to response messages before storing them in the chat history. If not set, defaults to excluding messages with source type . protected ChatHistoryProvider( Func, IEnumerable>? provideOutputMessageFilter = null, - Func, IEnumerable>? storeInputMessageFilter = null) + Func, IEnumerable>? storeInputRequestMessageFilter = null, + Func, IEnumerable>? storeInputResponseMessageFilter = null) { this._provideOutputMessageFilter = provideOutputMessageFilter; - this._storeInputMessageFilter = storeInputMessageFilter ?? DefaultExcludeChatHistoryFilter; + this._storeInputRequestMessageFilter = storeInputRequestMessageFilter ?? DefaultExcludeChatHistoryFilter; + this._storeInputResponseMessageFilter = storeInputResponseMessageFilter ?? DefaultNoopFilter; } /// @@ -216,7 +222,7 @@ public ValueTask InvokedAsync(InvokedContext context, CancellationToken cancella /// To check if the invocation was successful, inspect the property. /// /// - /// The default implementation of this method, skips execution for any invocation failures, filters messages using the optional storage input message filter + /// The default implementation of this method, skips execution for any invocation failures, filters messages using the optional storage input request and response message filters /// and calls to store new chat history messages. /// For most scenarios, overriding is sufficient to store chat history messages, while still benefiting from the default error handling and filtering behavior. /// However, for scenarios that require more control over error handling or message filtering, overriding this method allows you to directly control the messages that are stored for the invocation. @@ -229,7 +235,7 @@ protected virtual ValueTask InvokedCoreAsync(InvokedContext context, Cancellatio return default; } - var subContext = new InvokedContext(context.Agent, context.Session, this._storeInputMessageFilter(context.RequestMessages), context.ResponseMessages!); + var subContext = new InvokedContext(context.Agent, context.Session, this._storeInputRequestMessageFilter(context.RequestMessages), this._storeInputResponseMessageFilter(context.ResponseMessages!)); return this.StoreChatHistoryAsync(subContext, cancellationToken); } diff --git a/dotnet/src/Microsoft.Agents.AI.Abstractions/InMemoryChatHistoryProvider.cs b/dotnet/src/Microsoft.Agents.AI.Abstractions/InMemoryChatHistoryProvider.cs index 12e935b23e..e09dd6b0a0 100644 --- a/dotnet/src/Microsoft.Agents.AI.Abstractions/InMemoryChatHistoryProvider.cs +++ b/dotnet/src/Microsoft.Agents.AI.Abstractions/InMemoryChatHistoryProvider.cs @@ -38,7 +38,8 @@ public sealed class InMemoryChatHistoryProvider : ChatHistoryProvider public InMemoryChatHistoryProvider(InMemoryChatHistoryProviderOptions? options = null) : base( options?.ProvideOutputMessageFilter, - options?.StorageInputMessageFilter) + options?.StorageInputRequestMessageFilter, + options?.StorageInputResponseMessageFilter) { this._sessionState = new ProviderSessionState( options?.StateInitializer ?? (_ => new State()), diff --git a/dotnet/src/Microsoft.Agents.AI.Abstractions/InMemoryChatHistoryProviderOptions.cs b/dotnet/src/Microsoft.Agents.AI.Abstractions/InMemoryChatHistoryProviderOptions.cs index ba24f55ded..3a096fca88 100644 --- a/dotnet/src/Microsoft.Agents.AI.Abstractions/InMemoryChatHistoryProviderOptions.cs +++ b/dotnet/src/Microsoft.Agents.AI.Abstractions/InMemoryChatHistoryProviderOptions.cs @@ -59,7 +59,20 @@ public sealed class InMemoryChatHistoryProviderOptions /// Depending on your requirements, you could provide a different filter, that also excludes /// messages from e.g. AI context providers. /// - public Func, IEnumerable>? StorageInputMessageFilter { get; set; } + public Func, IEnumerable>? StorageInputRequestMessageFilter { get; set; } + + /// + /// Gets or sets an optional filter function applied to response messages before they are added to storage + /// during . + /// + /// + /// When , the provider defaults to excluding messages with + /// source type to avoid + /// storing messages that came from chat history in the first place. + /// Depending on your requirements, you could provide a different filter, that also excludes + /// messages from e.g. AI context providers. + /// + public Func, IEnumerable>? StorageInputResponseMessageFilter { get; set; } /// /// Gets or sets an optional filter function applied to messages produced by this provider diff --git a/dotnet/src/Microsoft.Agents.AI.Abstractions/MessageAIContextProvider.cs b/dotnet/src/Microsoft.Agents.AI.Abstractions/MessageAIContextProvider.cs index 24264e0e47..de623dd292 100644 --- a/dotnet/src/Microsoft.Agents.AI.Abstractions/MessageAIContextProvider.cs +++ b/dotnet/src/Microsoft.Agents.AI.Abstractions/MessageAIContextProvider.cs @@ -34,11 +34,13 @@ public abstract class MessageAIContextProvider : AIContextProvider /// Initializes a new instance of the class. /// /// An optional filter function to apply to input messages before providing messages via . If not set, defaults to including only messages. - /// An optional filter function to apply to request messages before storing messages via . If not set, defaults to including only messages. + /// An optional filter function to apply to request messages before storing messages via . If not set, defaults to including only messages. + /// An optional filter function to apply to response messages before storing messages via . If not set, defaults to including only messages. protected MessageAIContextProvider( Func, IEnumerable>? provideInputMessageFilter = null, - Func, IEnumerable>? storeInputMessageFilter = null) - : base(provideInputMessageFilter, storeInputMessageFilter) + Func, IEnumerable>? storeInputRequestMessageFilter = null, + Func, IEnumerable>? storeInputResponseMessageFilter = null) + : base(provideInputMessageFilter, storeInputRequestMessageFilter, storeInputResponseMessageFilter) { } diff --git a/dotnet/src/Microsoft.Agents.AI.CosmosNoSql/CosmosChatHistoryProvider.cs b/dotnet/src/Microsoft.Agents.AI.CosmosNoSql/CosmosChatHistoryProvider.cs index f1670fbb84..ca8bb95f3c 100644 --- a/dotnet/src/Microsoft.Agents.AI.CosmosNoSql/CosmosChatHistoryProvider.cs +++ b/dotnet/src/Microsoft.Agents.AI.CosmosNoSql/CosmosChatHistoryProvider.cs @@ -87,7 +87,8 @@ private static JsonSerializerOptions CreateDefaultJsonOptions() /// Whether this instance owns the CosmosClient and should dispose it. /// An optional key to use for storing the state in the . /// An optional filter function to apply to messages when retrieving them from the chat history. - /// An optional filter function to apply to messages before storing them in the chat history. If not set, defaults to excluding messages with source type . + /// An optional filter function to apply to request messages before storing them in the chat history. If not set, defaults to excluding messages with source type . + /// An optional filter function to apply to response messages before storing them in the chat history. If not set, defaults to excluding messages with source type . /// Thrown when or is . /// Thrown when any string parameter is null or whitespace. public CosmosChatHistoryProvider( @@ -98,8 +99,9 @@ public CosmosChatHistoryProvider( bool ownsClient = false, string? stateKey = null, Func, IEnumerable>? provideOutputMessageFilter = null, - Func, IEnumerable>? storeInputMessageFilter = null) - : base(provideOutputMessageFilter, storeInputMessageFilter) + Func, IEnumerable>? storeInputRequestMessageFilter = null, + Func, IEnumerable>? storeInputResponseMessageFilter = null) + : base(provideOutputMessageFilter, storeInputRequestMessageFilter, storeInputResponseMessageFilter) { this._sessionState = new ProviderSessionState( Throw.IfNull(stateInitializer), @@ -123,7 +125,8 @@ public CosmosChatHistoryProvider( /// A delegate that initializes the provider state on the first invocation. /// An optional key to use for storing the state in the . /// An optional filter function to apply to messages when retrieving them from the chat history. - /// An optional filter function to apply to messages before storing them in the chat history. If not set, defaults to excluding messages with source type . + /// An optional filter function to apply to request messages before storing them in the chat history. If not set, defaults to excluding messages with source type . + /// An optional filter function to apply to response messages before storing them in the chat history. If not set, defaults to excluding messages with source type . /// Thrown when any required parameter is null. /// Thrown when any string parameter is null or whitespace. public CosmosChatHistoryProvider( @@ -133,8 +136,9 @@ public CosmosChatHistoryProvider( Func stateInitializer, string? stateKey = null, Func, IEnumerable>? provideOutputMessageFilter = null, - Func, IEnumerable>? storeInputMessageFilter = null) - : this(new CosmosClient(Throw.IfNullOrWhitespace(connectionString)), databaseId, containerId, stateInitializer, ownsClient: true, stateKey, provideOutputMessageFilter, storeInputMessageFilter) + Func, IEnumerable>? storeInputRequestMessageFilter = null, + Func, IEnumerable>? storeInputResponseMessageFilter = null) + : this(new CosmosClient(Throw.IfNullOrWhitespace(connectionString)), databaseId, containerId, stateInitializer, ownsClient: true, stateKey, provideOutputMessageFilter, storeInputRequestMessageFilter, storeInputResponseMessageFilter) { } @@ -148,7 +152,8 @@ public CosmosChatHistoryProvider( /// A delegate that initializes the provider state on the first invocation. /// An optional key to use for storing the state in the . /// An optional filter function to apply to messages when retrieving them from the chat history. - /// An optional filter function to apply to messages before storing them in the chat history. If not set, defaults to excluding messages with source type . + /// An optional filter function to apply to request messages before storing them in the chat history. If not set, defaults to excluding messages with source type . + /// An optional filter function to apply to response messages before storing them in the chat history. If not set, defaults to excluding messages with source type . /// Thrown when any required parameter is null. /// Thrown when any string parameter is null or whitespace. public CosmosChatHistoryProvider( @@ -159,8 +164,9 @@ public CosmosChatHistoryProvider( Func stateInitializer, string? stateKey = null, Func, IEnumerable>? provideOutputMessageFilter = null, - Func, IEnumerable>? storeInputMessageFilter = null) - : this(new CosmosClient(Throw.IfNullOrWhitespace(accountEndpoint), Throw.IfNull(tokenCredential)), databaseId, containerId, stateInitializer, ownsClient: true, stateKey, provideOutputMessageFilter, storeInputMessageFilter) + Func, IEnumerable>? storeInputRequestMessageFilter = null, + Func, IEnumerable>? storeInputResponseMessageFilter = null) + : this(new CosmosClient(Throw.IfNullOrWhitespace(accountEndpoint), Throw.IfNull(tokenCredential)), databaseId, containerId, stateInitializer, ownsClient: true, stateKey, provideOutputMessageFilter, storeInputRequestMessageFilter, storeInputResponseMessageFilter) { } diff --git a/dotnet/src/Microsoft.Agents.AI.FoundryMemory/FoundryMemoryProvider.cs b/dotnet/src/Microsoft.Agents.AI.FoundryMemory/FoundryMemoryProvider.cs index 9ffeda3fb5..0f7041e834 100644 --- a/dotnet/src/Microsoft.Agents.AI.FoundryMemory/FoundryMemoryProvider.cs +++ b/dotnet/src/Microsoft.Agents.AI.FoundryMemory/FoundryMemoryProvider.cs @@ -59,7 +59,7 @@ public FoundryMemoryProvider( Func stateInitializer, FoundryMemoryProviderOptions? options = null, ILoggerFactory? loggerFactory = null) - : base(options?.SearchInputMessageFilter, options?.StorageInputMessageFilter) + : base(options?.SearchInputMessageFilter, options?.StorageInputRequestMessageFilter, options?.StorageInputResponseMessageFilter) { Throw.IfNull(client); Throw.IfNullOrWhitespace(memoryStoreName); diff --git a/dotnet/src/Microsoft.Agents.AI.FoundryMemory/FoundryMemoryProviderOptions.cs b/dotnet/src/Microsoft.Agents.AI.FoundryMemory/FoundryMemoryProviderOptions.cs index 482e14db82..6387c9d61e 100644 --- a/dotnet/src/Microsoft.Agents.AI.FoundryMemory/FoundryMemoryProviderOptions.cs +++ b/dotnet/src/Microsoft.Agents.AI.FoundryMemory/FoundryMemoryProviderOptions.cs @@ -63,5 +63,15 @@ public sealed class FoundryMemoryProviderOptions /// When , the provider defaults to including only /// messages. /// - public Func, IEnumerable>? StorageInputMessageFilter { get; set; } + public Func, IEnumerable>? StorageInputRequestMessageFilter { get; set; } + + /// + /// Gets or sets an optional filter function applied to response messages when determining which messages to + /// extract memories from during . + /// + /// + /// When , the provider defaults to including only + /// messages. + /// + public Func, IEnumerable>? StorageInputResponseMessageFilter { get; set; } } diff --git a/dotnet/src/Microsoft.Agents.AI.Mem0/Mem0Provider.cs b/dotnet/src/Microsoft.Agents.AI.Mem0/Mem0Provider.cs index 1924bc0da2..1e325b5683 100644 --- a/dotnet/src/Microsoft.Agents.AI.Mem0/Mem0Provider.cs +++ b/dotnet/src/Microsoft.Agents.AI.Mem0/Mem0Provider.cs @@ -52,7 +52,7 @@ public sealed class Mem0Provider : MessageAIContextProvider /// /// public Mem0Provider(HttpClient httpClient, Func stateInitializer, Mem0ProviderOptions? options = null, ILoggerFactory? loggerFactory = null) - : base(options?.SearchInputMessageFilter, options?.StorageInputMessageFilter) + : base(options?.SearchInputMessageFilter, options?.StorageInputRequestMessageFilter, options?.StorageInputResponseMessageFilter) { this._sessionState = new ProviderSessionState( ValidateStateInitializer(Throw.IfNull(stateInitializer)), diff --git a/dotnet/src/Microsoft.Agents.AI.Mem0/Mem0ProviderOptions.cs b/dotnet/src/Microsoft.Agents.AI.Mem0/Mem0ProviderOptions.cs index f7d14028d9..d53fa093d1 100644 --- a/dotnet/src/Microsoft.Agents.AI.Mem0/Mem0ProviderOptions.cs +++ b/dotnet/src/Microsoft.Agents.AI.Mem0/Mem0ProviderOptions.cs @@ -47,5 +47,15 @@ public sealed class Mem0ProviderOptions /// When , the provider defaults to including only /// messages. /// - public Func, IEnumerable>? StorageInputMessageFilter { get; set; } + public Func, IEnumerable>? StorageInputRequestMessageFilter { get; set; } + + /// + /// Gets or sets an optional filter function applied to response messages when determining which messages to + /// extract memories from during . + /// + /// + /// When , the provider defaults to including only + /// messages. + /// + public Func, IEnumerable>? StorageInputResponseMessageFilter { get; set; } } diff --git a/dotnet/src/Microsoft.Agents.AI.Workflows/WorkflowChatHistoryProvider.cs b/dotnet/src/Microsoft.Agents.AI.Workflows/WorkflowChatHistoryProvider.cs index b9d5f3ae49..1fd42f923e 100644 --- a/dotnet/src/Microsoft.Agents.AI.Workflows/WorkflowChatHistoryProvider.cs +++ b/dotnet/src/Microsoft.Agents.AI.Workflows/WorkflowChatHistoryProvider.cs @@ -22,7 +22,6 @@ internal sealed class WorkflowChatHistoryProvider : ChatHistoryProvider /// and source generated serializers are required, or Native AOT / Trimming is required. /// public WorkflowChatHistoryProvider(JsonSerializerOptions? jsonSerializerOptions = null) - : base(provideOutputMessageFilter: null, storeInputMessageFilter: null) { this._sessionState = new ProviderSessionState( _ => new StoreState(), diff --git a/dotnet/src/Microsoft.Agents.AI/Memory/ChatHistoryMemoryProvider.cs b/dotnet/src/Microsoft.Agents.AI/Memory/ChatHistoryMemoryProvider.cs index 7905db74b8..cd59d1aaa3 100644 --- a/dotnet/src/Microsoft.Agents.AI/Memory/ChatHistoryMemoryProvider.cs +++ b/dotnet/src/Microsoft.Agents.AI/Memory/ChatHistoryMemoryProvider.cs @@ -88,7 +88,7 @@ public ChatHistoryMemoryProvider( Func stateInitializer, ChatHistoryMemoryProviderOptions? options = null, ILoggerFactory? loggerFactory = null) - : base(options?.SearchInputMessageFilter, options?.StorageInputMessageFilter) + : base(options?.SearchInputMessageFilter, options?.StorageInputRequestMessageFilter, options?.StorageInputResponseMessageFilter) { this._sessionState = new ProviderSessionState( Throw.IfNull(stateInitializer), diff --git a/dotnet/src/Microsoft.Agents.AI/Memory/ChatHistoryMemoryProviderOptions.cs b/dotnet/src/Microsoft.Agents.AI/Memory/ChatHistoryMemoryProviderOptions.cs index 6c92a426f3..15c3393092 100644 --- a/dotnet/src/Microsoft.Agents.AI/Memory/ChatHistoryMemoryProviderOptions.cs +++ b/dotnet/src/Microsoft.Agents.AI/Memory/ChatHistoryMemoryProviderOptions.cs @@ -75,7 +75,17 @@ public sealed class ChatHistoryMemoryProviderOptions /// When , the provider defaults to including only /// messages. /// - public Func, IEnumerable>? StorageInputMessageFilter { get; set; } + public Func, IEnumerable>? StorageInputRequestMessageFilter { get; set; } + + /// + /// Gets or sets an optional filter function applied to response messages when storing recent chat history + /// during . + /// + /// + /// When , the provider defaults to including only + /// messages. + /// + public Func, IEnumerable>? StorageInputResponseMessageFilter { get; set; } /// /// Behavior choices for the provider. diff --git a/dotnet/src/Microsoft.Agents.AI/TextSearchProvider.cs b/dotnet/src/Microsoft.Agents.AI/TextSearchProvider.cs index dd62b0eb9b..df53729fce 100644 --- a/dotnet/src/Microsoft.Agents.AI/TextSearchProvider.cs +++ b/dotnet/src/Microsoft.Agents.AI/TextSearchProvider.cs @@ -61,7 +61,7 @@ public TextSearchProvider( Func>> searchAsync, TextSearchProviderOptions? options = null, ILoggerFactory? loggerFactory = null) - : base(options?.SearchInputMessageFilter, options?.StorageInputMessageFilter) + : base(options?.SearchInputMessageFilter, options?.StorageInputRequestMessageFilter, options?.StorageInputResponseMessageFilter) { this._sessionState = new ProviderSessionState( _ => new TextSearchProviderState(), diff --git a/dotnet/src/Microsoft.Agents.AI/TextSearchProviderOptions.cs b/dotnet/src/Microsoft.Agents.AI/TextSearchProviderOptions.cs index 837470b776..1ef7e16a49 100644 --- a/dotnet/src/Microsoft.Agents.AI/TextSearchProviderOptions.cs +++ b/dotnet/src/Microsoft.Agents.AI/TextSearchProviderOptions.cs @@ -86,7 +86,17 @@ public sealed class TextSearchProviderOptions /// When , the provider defaults to including only /// messages. /// - public Func, IEnumerable>? StorageInputMessageFilter { get; set; } + public Func, IEnumerable>? StorageInputRequestMessageFilter { get; set; } + + /// + /// Gets or sets an optional filter function applied to response messages when updating the recent message + /// memory during . + /// + /// + /// When , the provider defaults to including only + /// messages. + /// + public Func, IEnumerable>? StorageInputResponseMessageFilter { get; set; } /// /// Gets or sets the list of types to filter recent messages to diff --git a/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AIContextProviderTests.cs b/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AIContextProviderTests.cs index 811f9a3216..0e664d1ac9 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AIContextProviderTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AIContextProviderTests.cs @@ -543,7 +543,9 @@ public async Task InvokedCoreAsync_CallsStoreAIContextWithFilteredMessagesAsync( var storedRequest = provider.LastStoredContext!.RequestMessages.ToList(); Assert.Single(storedRequest); Assert.Equal("External", storedRequest[0].Text); - Assert.Same(responseMessages, provider.LastStoredContext.ResponseMessages); + var storedResponse = provider.LastStoredContext.ResponseMessages!.ToList(); + Assert.Single(storedResponse); + Assert.Equal("Response", storedResponse[0].Text); } [Fact] @@ -565,13 +567,14 @@ public async Task InvokedCoreAsync_UsesCustomStoreInputFilterAsync() { // Arrange - filter that only keeps System messages var provider = new TestAIContextProvider( - storeInputMessageFilter: msgs => msgs.Where(m => m.Role == ChatRole.System)); + storeInputRequestMessageFilter: msgs => msgs.Where(m => m.Role == ChatRole.System), + storeInputResponseMessageFilter: msgs => msgs.Where(m => m.Role == ChatRole.Assistant)); var messages = new[] { new ChatMessage(ChatRole.User, "User msg"), new ChatMessage(ChatRole.System, "System msg") }; - var context = new AIContextProvider.InvokedContext(s_mockAgent, s_mockSession, messages, [new ChatMessage(ChatRole.Assistant, "Response")]); + var context = new AIContextProvider.InvokedContext(s_mockAgent, s_mockSession, messages, [new ChatMessage(ChatRole.Assistant, "Response"), new ChatMessage(ChatRole.Tool, "Response")]); // Act await provider.InvokedAsync(context); @@ -581,6 +584,9 @@ public async Task InvokedCoreAsync_UsesCustomStoreInputFilterAsync() var storedRequest = provider.LastStoredContext!.RequestMessages.ToList(); Assert.Single(storedRequest); Assert.Equal("System msg", storedRequest[0].Text); + var storedResponse = provider.LastStoredContext.ResponseMessages!.ToList(); + Assert.Single(storedResponse); + Assert.Equal("Response", storedResponse[0].Text); } [Fact] @@ -605,6 +611,87 @@ public async Task InvokedCoreAsync_DefaultFilterExcludesNonExternalMessagesAsync Assert.Equal("External", storedRequest[0].Text); } + [Fact] + public async Task InvokedCoreAsync_DefaultResponseFilterPassesAllResponseMessagesAsync() + { + // Arrange + var provider = new TestAIContextProvider(); + var requestMessages = new[] { new ChatMessage(ChatRole.User, "Request") }; + var externalResponse = new ChatMessage(ChatRole.Assistant, "ExternalResp"); + var historyResponse = new ChatMessage(ChatRole.Assistant, "HistoryResp") + .WithAgentRequestMessageSource(AgentRequestMessageSourceType.ChatHistory, "src"); + var contextResponse = new ChatMessage(ChatRole.Assistant, "ContextResp") + .WithAgentRequestMessageSource(AgentRequestMessageSourceType.AIContextProvider, "src"); + var context = new AIContextProvider.InvokedContext(s_mockAgent, s_mockSession, requestMessages, [externalResponse, historyResponse, contextResponse]); + + // Act + await provider.InvokedAsync(context); + + // Assert - default response filter is a noop, so all response messages are kept + Assert.NotNull(provider.LastStoredContext); + var storedResponse = provider.LastStoredContext!.ResponseMessages!.ToList(); + Assert.Equal(3, storedResponse.Count); + Assert.Equal("ExternalResp", storedResponse[0].Text); + Assert.Equal("HistoryResp", storedResponse[1].Text); + Assert.Equal("ContextResp", storedResponse[2].Text); + } + + [Fact] + public async Task InvokedCoreAsync_UsesCustomResponseFilterAsync() + { + // Arrange - response filter that only keeps Assistant messages with specific text + var provider = new TestAIContextProvider( + storeInputResponseMessageFilter: msgs => msgs.Where(m => m.Text == "Keep")); + var requestMessages = new[] { new ChatMessage(ChatRole.User, "Request") }; + var responseMessages = new[] + { + new ChatMessage(ChatRole.Assistant, "Keep"), + new ChatMessage(ChatRole.Assistant, "Drop") + }; + var context = new AIContextProvider.InvokedContext(s_mockAgent, s_mockSession, requestMessages, responseMessages); + + // Act + await provider.InvokedAsync(context); + + // Assert + Assert.NotNull(provider.LastStoredContext); + var storedResponse = provider.LastStoredContext!.ResponseMessages!.ToList(); + Assert.Single(storedResponse); + Assert.Equal("Keep", storedResponse[0].Text); + } + + [Fact] + public async Task InvokedCoreAsync_RequestAndResponseFiltersOperateIndependentlyAsync() + { + // Arrange - different filters for request and response + var provider = new TestAIContextProvider( + storeInputRequestMessageFilter: msgs => msgs.Where(m => m.Role == ChatRole.System), + storeInputResponseMessageFilter: msgs => msgs.Where(m => m.Text == "Resp1")); + var requestMessages = new[] + { + new ChatMessage(ChatRole.User, "User"), + new ChatMessage(ChatRole.System, "System") + }; + var responseMessages = new[] + { + new ChatMessage(ChatRole.Assistant, "Resp1"), + new ChatMessage(ChatRole.Assistant, "Resp2") + }; + var context = new AIContextProvider.InvokedContext(s_mockAgent, s_mockSession, requestMessages, responseMessages); + + // Act + await provider.InvokedAsync(context); + + // Assert - request filter kept only System, response filter kept only Resp1 + Assert.NotNull(provider.LastStoredContext); + var storedRequest = provider.LastStoredContext!.RequestMessages.ToList(); + Assert.Single(storedRequest); + Assert.Equal("System", storedRequest[0].Text); + var storedResponse = provider.LastStoredContext!.ResponseMessages!.ToList(); + Assert.Single(storedResponse); + Assert.Equal("Resp1", storedResponse[0].Text); + } + #endregion private sealed class TestAIContextProvider : AIContextProvider @@ -620,8 +707,9 @@ public TestAIContextProvider( AIContext? provideContext = null, bool captureFilteredContext = false, Func, IEnumerable>? provideInputMessageFilter = null, - Func, IEnumerable>? storeInputMessageFilter = null) - : base(provideInputMessageFilter, storeInputMessageFilter) + Func, IEnumerable>? storeInputRequestMessageFilter = null, + Func, IEnumerable>? storeInputResponseMessageFilter = null) + : base(provideInputMessageFilter, storeInputRequestMessageFilter, storeInputResponseMessageFilter) { this._provideContext = provideContext; this._captureFilteredContext = captureFilteredContext; diff --git a/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/ChatHistoryProviderTests.cs b/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/ChatHistoryProviderTests.cs index 5df661f009..ed4e4823b3 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/ChatHistoryProviderTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/ChatHistoryProviderTests.cs @@ -439,7 +439,9 @@ public async Task InvokedCoreAsync_CallsStoreChatHistoryWithFilteredMessagesAsyn var storedRequest = provider.LastStoredContext!.RequestMessages.ToList(); Assert.Single(storedRequest); Assert.Equal("External", storedRequest[0].Text); - Assert.Same(responseMessages, provider.LastStoredContext.ResponseMessages); + var storedResponse = provider.LastStoredContext.ResponseMessages!.ToList(); + Assert.Single(storedResponse); + Assert.Equal("Response", storedResponse[0].Text); } [Fact] @@ -461,13 +463,14 @@ public async Task InvokedCoreAsync_UsesCustomStoreInputFilterAsync() { // Arrange - filter that only keeps System messages var provider = new TestChatHistoryProvider( - storeInputMessageFilter: msgs => msgs.Where(m => m.Role == ChatRole.System)); + storeInputRequestMessageFilter: msgs => msgs.Where(m => m.Role == ChatRole.System), + storeInputResponseMessageFilter: msgs => msgs.Where(m => m.Role == ChatRole.Assistant)); var messages = new[] { new ChatMessage(ChatRole.User, "User msg"), new ChatMessage(ChatRole.System, "System msg") }; - var context = new ChatHistoryProvider.InvokedContext(s_mockAgent, s_mockSession, messages, [new ChatMessage(ChatRole.Assistant, "Response")]); + var context = new ChatHistoryProvider.InvokedContext(s_mockAgent, s_mockSession, messages, [new ChatMessage(ChatRole.Assistant, "Response"), new ChatMessage(ChatRole.Tool, "Response")]); // Act await provider.InvokedAsync(context); @@ -477,6 +480,9 @@ public async Task InvokedCoreAsync_UsesCustomStoreInputFilterAsync() var storedRequest = provider.LastStoredContext!.RequestMessages.ToList(); Assert.Single(storedRequest); Assert.Equal("System msg", storedRequest[0].Text); + var storedResponse = provider.LastStoredContext.ResponseMessages!.ToList(); + Assert.Single(storedResponse); + Assert.Equal("Response", storedResponse[0].Text); } [Fact] @@ -529,8 +535,9 @@ private sealed class TestChatHistoryProvider : ChatHistoryProvider public TestChatHistoryProvider( IEnumerable? provideMessages = null, Func, IEnumerable>? provideOutputMessageFilter = null, - Func, IEnumerable>? storeInputMessageFilter = null) - : base(provideOutputMessageFilter, storeInputMessageFilter) + Func, IEnumerable>? storeInputRequestMessageFilter = null, + Func, IEnumerable>? storeInputResponseMessageFilter = null) + : base(provideOutputMessageFilter, storeInputRequestMessageFilter, storeInputResponseMessageFilter) { this._provideMessages = provideMessages; } diff --git a/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/InMemoryChatHistoryProviderTests.cs b/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/InMemoryChatHistoryProviderTests.cs index ebe1131ab7..147ceaf195 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/InMemoryChatHistoryProviderTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/InMemoryChatHistoryProviderTests.cs @@ -418,7 +418,7 @@ public async Task InvokedAsync_CustomFilter_OverridesDefaultAsync() var session = CreateMockSession(); var provider = new InMemoryChatHistoryProvider(new InMemoryChatHistoryProviderOptions { - StorageInputMessageFilter = messages => messages.Where(m => m.GetAgentRequestMessageSourceType() == AgentRequestMessageSourceType.External) + StorageInputRequestMessageFilter = messages => messages.Where(m => m.GetAgentRequestMessageSourceType() == AgentRequestMessageSourceType.External) }); var requestMessages = new List { diff --git a/dotnet/tests/Microsoft.Agents.AI.CosmosNoSql.UnitTests/CosmosChatHistoryProviderTests.cs b/dotnet/tests/Microsoft.Agents.AI.CosmosNoSql.UnitTests/CosmosChatHistoryProviderTests.cs index a790b19cdd..736bf7f026 100644 --- a/dotnet/tests/Microsoft.Agents.AI.CosmosNoSql.UnitTests/CosmosChatHistoryProviderTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.CosmosNoSql.UnitTests/CosmosChatHistoryProviderTests.cs @@ -1004,7 +1004,7 @@ public async Task InvokedAsync_CustomStorageInputFilter_OverridesDefaultAsync() s_testDatabaseId, TestContainerId, _ => new CosmosChatHistoryProvider.State(conversationId), - storeInputMessageFilter: messages => messages.Where(m => m.GetAgentRequestMessageSourceType() == AgentRequestMessageSourceType.External)); + storeInputRequestMessageFilter: messages => messages.Where(m => m.GetAgentRequestMessageSourceType() == AgentRequestMessageSourceType.External)); var requestMessages = new[] { diff --git a/dotnet/tests/Microsoft.Agents.AI.Mem0.UnitTests/Mem0ProviderTests.cs b/dotnet/tests/Microsoft.Agents.AI.Mem0.UnitTests/Mem0ProviderTests.cs index 02e18f324e..9f9de9127b 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Mem0.UnitTests/Mem0ProviderTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Mem0.UnitTests/Mem0ProviderTests.cs @@ -530,7 +530,7 @@ public async Task InvokedAsync_CustomStorageInputFilter_OverridesDefaultAsync() var mockSession = new TestAgentSession(); var sut = new Mem0Provider(this._httpClient, _ => new Mem0Provider.State(storageScope), options: new Mem0ProviderOptions { - StorageInputMessageFilter = messages => messages // No filtering - store everything + StorageInputRequestMessageFilter = messages => messages // No filtering - store everything }); var requestMessages = new List diff --git a/dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgentOptionsTests.cs b/dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgentOptionsTests.cs index b8e3c57af6..48b0ed1ee4 100644 --- a/dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgentOptionsTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgentOptionsTests.cs @@ -115,8 +115,8 @@ public void Clone_CreatesDeepCopyWithSameValues() const string Description = "Test description"; var tools = new List { AIFunctionFactory.Create(() => "test") }; - var mockChatHistoryProvider = new Mock(null, null).Object; - var mockAIContextProvider = new Mock(null, null).Object; + var mockChatHistoryProvider = new Mock(null, null, null).Object; + var mockAIContextProvider = new Mock(null, null, null).Object; var original = new ChatClientAgentOptions() { @@ -149,8 +149,8 @@ public void Clone_CreatesDeepCopyWithSameValues() public void Clone_WithoutProvidingChatOptions_ClonesCorrectly() { // Arrange - var mockChatHistoryProvider = new Mock(null, null).Object; - var mockAIContextProvider = new Mock(null, null).Object; + var mockChatHistoryProvider = new Mock(null, null, null).Object; + var mockAIContextProvider = new Mock(null, null, null).Object; var original = new ChatClientAgentOptions { diff --git a/dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgentTests.cs b/dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgentTests.cs index 12446c89c0..9713a91c2c 100644 --- a/dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgentTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgentTests.cs @@ -488,7 +488,7 @@ public async Task RunAsyncInvokesAIContextProviderAndUsesResultAsync() }) .ReturnsAsync(new ChatResponse(responseMessages)); - var mockProvider = new Mock(null, null); + var mockProvider = new Mock(null, null, null); mockProvider .Protected() .Setup>("InvokingCoreAsync", ItExpr.IsAny(), ItExpr.IsAny()) @@ -559,7 +559,7 @@ public async Task RunAsyncInvokesAIContextProviderWhenGetResponseFailsAsync() It.IsAny())) .Throws(new InvalidOperationException("downstream failure")); - var mockProvider = new Mock(null, null); + var mockProvider = new Mock(null, null, null); mockProvider .Protected() .Setup>("InvokingCoreAsync", ItExpr.IsAny(), ItExpr.IsAny()) @@ -617,7 +617,7 @@ public async Task RunAsyncInvokesAIContextProviderAndSucceedsWithEmptyAIContextA }) .ReturnsAsync(new ChatResponse([new(ChatRole.Assistant, "response")])); - var mockProvider = new Mock(null, null); + var mockProvider = new Mock(null, null, null); mockProvider .Protected() .Setup>("InvokingCoreAsync", ItExpr.IsAny(), ItExpr.IsAny()) @@ -677,7 +677,7 @@ public async Task RunAsyncInvokesMultipleAIContextProvidersInOrderAsync() .ReturnsAsync(new ChatResponse(responseMessages)); // Provider 1: adds a system message and a tool - var mockProvider1 = new Mock(null, null); + var mockProvider1 = new Mock(null, null, null); mockProvider1.SetupGet(p => p.StateKey).Returns("Provider1"); mockProvider1 .Protected() @@ -696,7 +696,7 @@ public async Task RunAsyncInvokesMultipleAIContextProvidersInOrderAsync() // Provider 2: adds another system message and verifies it receives accumulated context from provider 1 AIContext? provider2ReceivedContext = null; - var mockProvider2 = new Mock(null, null); + var mockProvider2 = new Mock(null, null, null); mockProvider2.SetupGet(p => p.StateKey).Returns("Provider2"); mockProvider2 .Protected() @@ -784,7 +784,7 @@ public async Task RunAsyncInvokesMultipleAIContextProvidersOnFailureAsync() It.IsAny())) .ThrowsAsync(new InvalidOperationException("downstream failure")); - var mockProvider1 = new Mock(null, null); + var mockProvider1 = new Mock(null, null, null); mockProvider1.SetupGet(p => p.StateKey).Returns("Provider1"); mockProvider1 .Protected() @@ -801,7 +801,7 @@ public async Task RunAsyncInvokesMultipleAIContextProvidersOnFailureAsync() .Setup("InvokedCoreAsync", ItExpr.IsAny(), ItExpr.IsAny()) .Returns(new ValueTask()); - var mockProvider2 = new Mock(null, null); + var mockProvider2 = new Mock(null, null, null); mockProvider2.SetupGet(p => p.StateKey).Returns("Provider2"); mockProvider2 .Protected() @@ -869,7 +869,7 @@ public async Task RunStreamingAsyncInvokesMultipleAIContextProvidersAsync() }) .Returns(ToAsyncEnumerableAsync(responseUpdates)); - var mockProvider1 = new Mock(null, null); + var mockProvider1 = new Mock(null, null, null); mockProvider1.SetupGet(p => p.StateKey).Returns("Provider1"); mockProvider1 .Protected() @@ -886,7 +886,7 @@ public async Task RunStreamingAsyncInvokesMultipleAIContextProvidersAsync() .Setup("InvokedCoreAsync", ItExpr.IsAny(), ItExpr.IsAny()) .Returns(new ValueTask()); - var mockProvider2 = new Mock(null, null); + var mockProvider2 = new Mock(null, null, null); mockProvider2.SetupGet(p => p.StateKey).Returns("Provider2"); mockProvider2 .Protected() @@ -1828,7 +1828,7 @@ public async Task RunStreamingAsyncInvokesAIContextProviderAndUsesResultAsync() }) .Returns(ToAsyncEnumerableAsync(responseUpdates)); - var mockProvider = new Mock(null, null); + var mockProvider = new Mock(null, null, null); mockProvider .Protected() .Setup>("InvokingCoreAsync", ItExpr.IsAny(), ItExpr.IsAny()) @@ -1907,7 +1907,7 @@ public async Task RunStreamingAsyncInvokesAIContextProviderWhenGetResponseFailsA It.IsAny())) .Throws(new InvalidOperationException("downstream failure")); - var mockProvider = new Mock(null, null); + var mockProvider = new Mock(null, null, null); mockProvider .Protected() .Setup>("InvokingCoreAsync", ItExpr.IsAny(), ItExpr.IsAny()) diff --git a/dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgent_BackgroundResponsesTests.cs b/dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgent_BackgroundResponsesTests.cs index 64835f2b2f..ebb1791dfd 100644 --- a/dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgent_BackgroundResponsesTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgent_BackgroundResponsesTests.cs @@ -338,7 +338,7 @@ public async Task RunAsync_WhenContinuationTokenProvided_SkipsSessionMessagePopu List capturedMessages = []; // Create a mock chat history provider that would normally provide messages - var mockChatHistoryProvider = new Mock(null, null); + var mockChatHistoryProvider = new Mock(null, null, null); mockChatHistoryProvider.SetupGet(p => p.StateKey).Returns("ChatHistoryProvider"); mockChatHistoryProvider .Protected() @@ -346,7 +346,7 @@ public async Task RunAsync_WhenContinuationTokenProvided_SkipsSessionMessagePopu .ReturnsAsync([new(ChatRole.User, "Message from chat history provider")]); // Create a mock AI context provider that would normally provide context - var mockContextProvider = new Mock(null, null); + var mockContextProvider = new Mock(null, null, null); mockContextProvider.SetupGet(p => p.StateKey).Returns("Provider1"); mockContextProvider .Protected() @@ -407,7 +407,7 @@ public async Task RunStreamingAsync_WhenContinuationTokenProvided_SkipsSessionMe List capturedMessages = []; // Create a mock chat history provider that would normally provide messages - var mockChatHistoryProvider = new Mock(null, null); + var mockChatHistoryProvider = new Mock(null, null, null); mockChatHistoryProvider.SetupGet(p => p.StateKey).Returns("ChatHistoryProvider"); mockChatHistoryProvider .Protected() @@ -415,7 +415,7 @@ public async Task RunStreamingAsync_WhenContinuationTokenProvided_SkipsSessionMe .ReturnsAsync([new(ChatRole.User, "Message from chat history provider")]); // Create a mock AI context provider that would normally provide context - var mockContextProvider = new Mock(null, null); + var mockContextProvider = new Mock(null, null, null); mockContextProvider.SetupGet(p => p.StateKey).Returns("Provider1"); mockContextProvider .Protected() @@ -638,7 +638,7 @@ public async Task RunStreamingAsync_WhenResumingStreaming_UsesUpdatesFromInitial .Returns(ToAsyncEnumerableAsync(returnUpdates)); List capturedMessagesAddedToProvider = []; - var mockChatHistoryProvider = new Mock(null, null); + var mockChatHistoryProvider = new Mock(null, null, null); mockChatHistoryProvider.SetupGet(p => p.StateKey).Returns("ChatHistoryProvider"); mockChatHistoryProvider .Protected() @@ -647,7 +647,7 @@ public async Task RunStreamingAsync_WhenResumingStreaming_UsesUpdatesFromInitial .Returns(new ValueTask()); AIContextProvider.InvokedContext? capturedInvokedContext = null; - var mockContextProvider = new Mock(null, null); + var mockContextProvider = new Mock(null, null, null); mockContextProvider.SetupGet(p => p.StateKey).Returns("Provider1"); mockContextProvider .Protected() @@ -702,7 +702,7 @@ public async Task RunStreamingAsync_WhenResumingStreaming_UsesInputMessagesFromI .Returns(ToAsyncEnumerableAsync(Array.Empty())); List capturedMessagesAddedToProvider = []; - var mockChatHistoryProvider = new Mock(null, null); + var mockChatHistoryProvider = new Mock(null, null, null); mockChatHistoryProvider.SetupGet(p => p.StateKey).Returns("ChatHistoryProvider"); mockChatHistoryProvider .Protected() @@ -711,7 +711,7 @@ public async Task RunStreamingAsync_WhenResumingStreaming_UsesInputMessagesFromI .Returns(new ValueTask()); AIContextProvider.InvokedContext? capturedInvokedContext = null; - var mockContextProvider = new Mock(null, null); + var mockContextProvider = new Mock(null, null, null); mockContextProvider.SetupGet(p => p.StateKey).Returns("Provider1"); mockContextProvider .Protected() diff --git a/dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgent_ChatHistoryManagementTests.cs b/dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgent_ChatHistoryManagementTests.cs index 423c867abd..8bf38ebc98 100644 --- a/dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgent_ChatHistoryManagementTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgent_ChatHistoryManagementTests.cs @@ -185,7 +185,7 @@ public async Task RunAsync_UsesChatHistoryProvider_WhenProvidedAndNoConversation It.IsAny(), It.IsAny())).ReturnsAsync(new ChatResponse([new(ChatRole.Assistant, "response")])); - Mock mockChatHistoryProvider = new(null, null); + Mock mockChatHistoryProvider = new(null, null, null); mockChatHistoryProvider .Protected() .Setup>>("InvokingCoreAsync", ItExpr.IsAny(), ItExpr.IsAny()) @@ -240,7 +240,7 @@ public async Task RunAsync_NotifiesChatHistoryProvider_OnFailureAsync() It.IsAny(), It.IsAny())).Throws(new InvalidOperationException("Test Error")); - Mock mockChatHistoryProvider = new(null, null); + Mock mockChatHistoryProvider = new(null, null, null); mockChatHistoryProvider .Protected() .Setup>>("InvokingCoreAsync", ItExpr.IsAny(), ItExpr.IsAny()) @@ -311,7 +311,7 @@ public async Task RunAsync_UsesOverrideChatHistoryProvider_WhenProvidedViaAdditi It.IsAny())).ReturnsAsync(new ChatResponse([new(ChatRole.Assistant, "response")])); // Arrange a chat history provider to override the factory provided one. - Mock mockOverrideChatHistoryProvider = new(null, null); + Mock mockOverrideChatHistoryProvider = new(null, null, null); mockOverrideChatHistoryProvider .Protected() .Setup>>("InvokingCoreAsync", ItExpr.IsAny(), ItExpr.IsAny()) @@ -324,7 +324,7 @@ public async Task RunAsync_UsesOverrideChatHistoryProvider_WhenProvidedViaAdditi // Arrange a chat history provider to provide to the agent at construction time. // This one shouldn't be used since it is being overridden. - Mock mockAgentOptionsChatHistoryProvider = new(null, null); + Mock mockAgentOptionsChatHistoryProvider = new(null, null, null); mockAgentOptionsChatHistoryProvider .Protected() .Setup>>("InvokingCoreAsync", ItExpr.IsAny(), ItExpr.IsAny()) diff --git a/dotnet/tests/Microsoft.Agents.AI.UnitTests/Data/TextSearchProviderTests.cs b/dotnet/tests/Microsoft.Agents.AI.UnitTests/Data/TextSearchProviderTests.cs index 46c56fc483..a0d6bbb35f 100644 --- a/dotnet/tests/Microsoft.Agents.AI.UnitTests/Data/TextSearchProviderTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.UnitTests/Data/TextSearchProviderTests.cs @@ -467,7 +467,7 @@ public async Task InvokedAsync_CustomStorageInputFilter_OverridesDefaultAsync() { RecentMessageMemoryLimit = 10, RecentMessageRolesIncluded = [ChatRole.User, ChatRole.System], - StorageInputMessageFilter = messages => messages // No filtering - store everything + StorageInputRequestMessageFilter = messages => messages // No filtering - store everything }; string? capturedInput = null; Task> SearchDelegateAsync(string input, CancellationToken ct) diff --git a/dotnet/tests/Microsoft.Agents.AI.UnitTests/Memory/ChatHistoryMemoryProviderTests.cs b/dotnet/tests/Microsoft.Agents.AI.UnitTests/Memory/ChatHistoryMemoryProviderTests.cs index ff5d709202..a0657d5a47 100644 --- a/dotnet/tests/Microsoft.Agents.AI.UnitTests/Memory/ChatHistoryMemoryProviderTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.UnitTests/Memory/ChatHistoryMemoryProviderTests.cs @@ -687,7 +687,7 @@ public async Task InvokedAsync_CustomStorageInputFilter_OverridesDefaultAsync() _ => new ChatHistoryMemoryProvider.State(new ChatHistoryMemoryProviderScope { UserId = "UID" }), options: new ChatHistoryMemoryProviderOptions { - StorageInputMessageFilter = messages => messages // No filtering - store everything + StorageInputRequestMessageFilter = messages => messages // No filtering - store everything }); var requestMessages = new List From ce3f1d6e3b35e4a0c5f89a8487ca3846855e2422 Mon Sep 17 00:00:00 2001 From: westey <164392973+westey-m@users.noreply.github.com> Date: Thu, 26 Feb 2026 19:44:22 +0000 Subject: [PATCH 2/4] Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../AIContextProvider.cs | 4 ++-- .../InMemoryChatHistoryProviderOptions.cs | 9 ++++----- .../MessageAIContextProvider.cs | 2 +- .../FoundryMemoryProviderOptions.cs | 3 +-- .../src/Microsoft.Agents.AI.Mem0/Mem0ProviderOptions.cs | 3 +-- .../Memory/ChatHistoryMemoryProviderOptions.cs | 4 +--- 6 files changed, 10 insertions(+), 15 deletions(-) diff --git a/dotnet/src/Microsoft.Agents.AI.Abstractions/AIContextProvider.cs b/dotnet/src/Microsoft.Agents.AI.Abstractions/AIContextProvider.cs index d068686245..82e5f2c360 100644 --- a/dotnet/src/Microsoft.Agents.AI.Abstractions/AIContextProvider.cs +++ b/dotnet/src/Microsoft.Agents.AI.Abstractions/AIContextProvider.cs @@ -41,7 +41,7 @@ private static IEnumerable DefaultNoopFilter(IEnumerable /// An optional filter function to apply to input messages before providing context via . If not set, defaults to including only messages. /// An optional filter function to apply to request messages before storing context via . If not set, defaults to including only messages. - /// An optional filter function to apply to response messages before storing context via . If not set, defaults to including only messages. + /// An optional filter function to apply to response messages before storing context via . If not set, defaults to a no-op filter that includes all response messages. protected AIContextProvider( Func, IEnumerable>? provideInputMessageFilter = null, Func, IEnumerable>? storeInputRequestMessageFilter = null, @@ -258,7 +258,7 @@ public ValueTask InvokedAsync(InvokedContext context, CancellationToken cancella /// filters the request messages using the configured store-input request message filter /// (which defaults to including only messages), /// filters the response messages using the configured store-input response message filter - /// (which defaults to including only messages), + /// (which defaults to a no-op, so all response messages are processed), /// and calls to process the invocation results. /// For most scenarios, overriding is sufficient to process invocation results, /// while still benefiting from the default error handling and filtering behavior. diff --git a/dotnet/src/Microsoft.Agents.AI.Abstractions/InMemoryChatHistoryProviderOptions.cs b/dotnet/src/Microsoft.Agents.AI.Abstractions/InMemoryChatHistoryProviderOptions.cs index 3a096fca88..873619d484 100644 --- a/dotnet/src/Microsoft.Agents.AI.Abstractions/InMemoryChatHistoryProviderOptions.cs +++ b/dotnet/src/Microsoft.Agents.AI.Abstractions/InMemoryChatHistoryProviderOptions.cs @@ -66,11 +66,10 @@ public sealed class InMemoryChatHistoryProviderOptions /// during . /// /// - /// When , the provider defaults to excluding messages with - /// source type to avoid - /// storing messages that came from chat history in the first place. - /// Depending on your requirements, you could provide a different filter, that also excludes - /// messages from e.g. AI context providers. + /// When , no filtering is applied to response messages before they are stored. + /// If you want to avoid persisting certain messages (for example, those with + /// source type or produced by AI context providers), + /// provide a filter that returns only the messages you want to keep. /// public Func, IEnumerable>? StorageInputResponseMessageFilter { get; set; } diff --git a/dotnet/src/Microsoft.Agents.AI.Abstractions/MessageAIContextProvider.cs b/dotnet/src/Microsoft.Agents.AI.Abstractions/MessageAIContextProvider.cs index de623dd292..c5f367443c 100644 --- a/dotnet/src/Microsoft.Agents.AI.Abstractions/MessageAIContextProvider.cs +++ b/dotnet/src/Microsoft.Agents.AI.Abstractions/MessageAIContextProvider.cs @@ -35,7 +35,7 @@ public abstract class MessageAIContextProvider : AIContextProvider /// /// An optional filter function to apply to input messages before providing messages via . If not set, defaults to including only messages. /// An optional filter function to apply to request messages before storing messages via . If not set, defaults to including only messages. - /// An optional filter function to apply to response messages before storing messages via . If not set, defaults to including only messages. + /// An optional filter function to apply to response messages before storing messages via . If not set, defaults to including all response messages (no filtering). protected MessageAIContextProvider( Func, IEnumerable>? provideInputMessageFilter = null, Func, IEnumerable>? storeInputRequestMessageFilter = null, diff --git a/dotnet/src/Microsoft.Agents.AI.FoundryMemory/FoundryMemoryProviderOptions.cs b/dotnet/src/Microsoft.Agents.AI.FoundryMemory/FoundryMemoryProviderOptions.cs index 6387c9d61e..870fe1d271 100644 --- a/dotnet/src/Microsoft.Agents.AI.FoundryMemory/FoundryMemoryProviderOptions.cs +++ b/dotnet/src/Microsoft.Agents.AI.FoundryMemory/FoundryMemoryProviderOptions.cs @@ -70,8 +70,7 @@ public sealed class FoundryMemoryProviderOptions /// extract memories from during . /// /// - /// When , the provider defaults to including only - /// messages. + /// When , the provider does not filter response messages and includes all messages. /// public Func, IEnumerable>? StorageInputResponseMessageFilter { get; set; } } diff --git a/dotnet/src/Microsoft.Agents.AI.Mem0/Mem0ProviderOptions.cs b/dotnet/src/Microsoft.Agents.AI.Mem0/Mem0ProviderOptions.cs index d53fa093d1..4a3a16712f 100644 --- a/dotnet/src/Microsoft.Agents.AI.Mem0/Mem0ProviderOptions.cs +++ b/dotnet/src/Microsoft.Agents.AI.Mem0/Mem0ProviderOptions.cs @@ -54,8 +54,7 @@ public sealed class Mem0ProviderOptions /// extract memories from during . /// /// - /// When , the provider defaults to including only - /// messages. + /// When , the provider applies no filtering and includes all response messages. /// public Func, IEnumerable>? StorageInputResponseMessageFilter { get; set; } } diff --git a/dotnet/src/Microsoft.Agents.AI/Memory/ChatHistoryMemoryProviderOptions.cs b/dotnet/src/Microsoft.Agents.AI/Memory/ChatHistoryMemoryProviderOptions.cs index 15c3393092..a9c5b93928 100644 --- a/dotnet/src/Microsoft.Agents.AI/Memory/ChatHistoryMemoryProviderOptions.cs +++ b/dotnet/src/Microsoft.Agents.AI/Memory/ChatHistoryMemoryProviderOptions.cs @@ -82,11 +82,9 @@ public sealed class ChatHistoryMemoryProviderOptions /// during . /// /// - /// When , the provider defaults to including only - /// messages. + /// When , the provider does not apply any filtering and includes all response messages. /// public Func, IEnumerable>? StorageInputResponseMessageFilter { get; set; } - /// /// Behavior choices for the provider. /// From 372c12e734746cd66225899804e0cdb39ad34376 Mon Sep 17 00:00:00 2001 From: westey <164392973+westey-m@users.noreply.github.com> Date: Thu, 26 Feb 2026 19:47:47 +0000 Subject: [PATCH 3/4] Address feedback --- .../CosmosChatHistoryProvider.cs | 6 +++--- dotnet/src/Microsoft.Agents.AI/TextSearchProviderOptions.cs | 3 +-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/dotnet/src/Microsoft.Agents.AI.CosmosNoSql/CosmosChatHistoryProvider.cs b/dotnet/src/Microsoft.Agents.AI.CosmosNoSql/CosmosChatHistoryProvider.cs index ca8bb95f3c..a48283e36d 100644 --- a/dotnet/src/Microsoft.Agents.AI.CosmosNoSql/CosmosChatHistoryProvider.cs +++ b/dotnet/src/Microsoft.Agents.AI.CosmosNoSql/CosmosChatHistoryProvider.cs @@ -88,7 +88,7 @@ private static JsonSerializerOptions CreateDefaultJsonOptions() /// An optional key to use for storing the state in the . /// An optional filter function to apply to messages when retrieving them from the chat history. /// An optional filter function to apply to request messages before storing them in the chat history. If not set, defaults to excluding messages with source type . - /// An optional filter function to apply to response messages before storing them in the chat history. If not set, defaults to excluding messages with source type . + /// An optional filter function to apply to response messages before storing them in the chat history. If not set, defaults storing all response messages. /// Thrown when or is . /// Thrown when any string parameter is null or whitespace. public CosmosChatHistoryProvider( @@ -126,7 +126,7 @@ public CosmosChatHistoryProvider( /// An optional key to use for storing the state in the . /// An optional filter function to apply to messages when retrieving them from the chat history. /// An optional filter function to apply to request messages before storing them in the chat history. If not set, defaults to excluding messages with source type . - /// An optional filter function to apply to response messages before storing them in the chat history. If not set, defaults to excluding messages with source type . + /// An optional filter function to apply to response messages before storing them in the chat history. If not set, defaults storing all response messages. /// Thrown when any required parameter is null. /// Thrown when any string parameter is null or whitespace. public CosmosChatHistoryProvider( @@ -153,7 +153,7 @@ public CosmosChatHistoryProvider( /// An optional key to use for storing the state in the . /// An optional filter function to apply to messages when retrieving them from the chat history. /// An optional filter function to apply to request messages before storing them in the chat history. If not set, defaults to excluding messages with source type . - /// An optional filter function to apply to response messages before storing them in the chat history. If not set, defaults to excluding messages with source type . + /// An optional filter function to apply to response messages before storing them in the chat history. If not set, defaults storing all response messages. /// Thrown when any required parameter is null. /// Thrown when any string parameter is null or whitespace. public CosmosChatHistoryProvider( diff --git a/dotnet/src/Microsoft.Agents.AI/TextSearchProviderOptions.cs b/dotnet/src/Microsoft.Agents.AI/TextSearchProviderOptions.cs index 1ef7e16a49..879e34121d 100644 --- a/dotnet/src/Microsoft.Agents.AI/TextSearchProviderOptions.cs +++ b/dotnet/src/Microsoft.Agents.AI/TextSearchProviderOptions.cs @@ -93,8 +93,7 @@ public sealed class TextSearchProviderOptions /// memory during . /// /// - /// When , the provider defaults to including only - /// messages. + /// When , the provider defaults to including all messages. /// public Func, IEnumerable>? StorageInputResponseMessageFilter { get; set; } From 1a76aabe3e485fbeaef61c89353f0ea42a67bdb4 Mon Sep 17 00:00:00 2001 From: westey <164392973+westey-m@users.noreply.github.com> Date: Fri, 27 Feb 2026 18:51:33 +0000 Subject: [PATCH 4/4] Apply suggestions from code review Co-authored-by: SergeyMenshykh <68852919+SergeyMenshykh@users.noreply.github.com> --- .../Microsoft.Agents.AI.Abstractions/ChatHistoryProvider.cs | 2 +- .../CosmosChatHistoryProvider.cs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/dotnet/src/Microsoft.Agents.AI.Abstractions/ChatHistoryProvider.cs b/dotnet/src/Microsoft.Agents.AI.Abstractions/ChatHistoryProvider.cs index 1d629c4cd6..df9ff0069e 100644 --- a/dotnet/src/Microsoft.Agents.AI.Abstractions/ChatHistoryProvider.cs +++ b/dotnet/src/Microsoft.Agents.AI.Abstractions/ChatHistoryProvider.cs @@ -54,7 +54,7 @@ private static IEnumerable DefaultNoopFilter(IEnumerable /// An optional filter function to apply to messages when retrieving them from the chat history. /// An optional filter function to apply to request messages before storing them in the chat history. If not set, defaults to excluding messages with source type . - /// An optional filter function to apply to response messages before storing them in the chat history. If not set, defaults to excluding messages with source type . + /// An optional filter function to apply to response messages before storing them in the chat history. If not set, defaults to a no-op filter that includes all response messages. protected ChatHistoryProvider( Func, IEnumerable>? provideOutputMessageFilter = null, Func, IEnumerable>? storeInputRequestMessageFilter = null, diff --git a/dotnet/src/Microsoft.Agents.AI.CosmosNoSql/CosmosChatHistoryProvider.cs b/dotnet/src/Microsoft.Agents.AI.CosmosNoSql/CosmosChatHistoryProvider.cs index a48283e36d..afaa59ee53 100644 --- a/dotnet/src/Microsoft.Agents.AI.CosmosNoSql/CosmosChatHistoryProvider.cs +++ b/dotnet/src/Microsoft.Agents.AI.CosmosNoSql/CosmosChatHistoryProvider.cs @@ -88,7 +88,7 @@ private static JsonSerializerOptions CreateDefaultJsonOptions() /// An optional key to use for storing the state in the . /// An optional filter function to apply to messages when retrieving them from the chat history. /// An optional filter function to apply to request messages before storing them in the chat history. If not set, defaults to excluding messages with source type . - /// An optional filter function to apply to response messages before storing them in the chat history. If not set, defaults storing all response messages. + /// An optional filter function to apply to response messages before storing them in the chat history. If not set, defaults to storing all response messages. /// Thrown when or is . /// Thrown when any string parameter is null or whitespace. public CosmosChatHistoryProvider( @@ -126,7 +126,7 @@ public CosmosChatHistoryProvider( /// An optional key to use for storing the state in the . /// An optional filter function to apply to messages when retrieving them from the chat history. /// An optional filter function to apply to request messages before storing them in the chat history. If not set, defaults to excluding messages with source type . - /// An optional filter function to apply to response messages before storing them in the chat history. If not set, defaults storing all response messages. + /// An optional filter function to apply to response messages before storing them in the chat history. If not set, defaults to storing all response messages. /// Thrown when any required parameter is null. /// Thrown when any string parameter is null or whitespace. public CosmosChatHistoryProvider( @@ -153,7 +153,7 @@ public CosmosChatHistoryProvider( /// An optional key to use for storing the state in the . /// An optional filter function to apply to messages when retrieving them from the chat history. /// An optional filter function to apply to request messages before storing them in the chat history. If not set, defaults to excluding messages with source type . - /// An optional filter function to apply to response messages before storing them in the chat history. If not set, defaults storing all response messages. + /// An optional filter function to apply to response messages before storing them in the chat history. If not set, defaults to storing all response messages. /// Thrown when any required parameter is null. /// Thrown when any string parameter is null or whitespace. public CosmosChatHistoryProvider(