From 7dccf3a07bc5fd76004b85305f5121712e188b15 Mon Sep 17 00:00:00 2001 From: westey <164392973+westey-m@users.noreply.github.com> Date: Tue, 10 Feb 2026 19:21:18 +0000 Subject: [PATCH] .NET: [BREAKING] Update message source code to match python. (#3805) * Update message source code to match python. * Apply suggestion from @Copilot Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Address PR comment * Move setting of source information to extension method * Add underscore for attribution key to indicate internal usage * Stick to version 102 of the SDK since 103 is causing issues. * Revert global.json change * Fix unit test --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../AIContextProvider.cs | 36 +- .../AgentRequestMessageSource.cs | 16 - .../AgentRequestMessageSourceAttribution.cs | 102 ++++ .../AgentRequestMessageSourceType.cs | 35 +- .../ChatHistoryProvider.cs | 34 +- .../ChatHistoryProviderExtensions.cs | 2 +- .../ChatMessageExtensions.cs | 60 ++- .../Microsoft.Agents.AI.Mem0/Mem0Provider.cs | 4 +- .../Memory/ChatHistoryMemoryProvider.cs | 4 +- .../Microsoft.Agents.AI/TextSearchProvider.cs | 4 +- .../AIContextProviderTests.cs | 45 +- ...entRequestMessageSourceAttributionTests.cs | 466 ++++++++++++++++++ .../AgentRequestMessageSourceTypeTests.cs | 103 +--- .../ChatHistoryProviderExtensionsTests.cs | 6 +- .../ChatHistoryProviderMessageFilterTests.cs | 4 +- .../ChatHistoryProviderTests.cs | 45 +- .../ChatMessageExtensionsTests.cs | 390 +++++++++++++-- .../InMemoryChatHistoryProviderTests.cs | 2 +- .../CosmosChatHistoryProviderTests.cs | 2 +- 19 files changed, 1084 insertions(+), 276 deletions(-) delete mode 100644 dotnet/src/Microsoft.Agents.AI.Abstractions/AgentRequestMessageSource.cs create mode 100644 dotnet/src/Microsoft.Agents.AI.Abstractions/AgentRequestMessageSourceAttribution.cs create mode 100644 dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AgentRequestMessageSourceAttributionTests.cs diff --git a/dotnet/src/Microsoft.Agents.AI.Abstractions/AIContextProvider.cs b/dotnet/src/Microsoft.Agents.AI.Abstractions/AIContextProvider.cs index a4b606e6a1..76ff8752b9 100644 --- a/dotnet/src/Microsoft.Agents.AI.Abstractions/AIContextProvider.cs +++ b/dotnet/src/Microsoft.Agents.AI.Abstractions/AIContextProvider.cs @@ -32,23 +32,23 @@ namespace Microsoft.Agents.AI; /// public abstract class AIContextProvider { - private readonly string _sourceName; + private readonly string _sourceId; /// /// Initializes a new instance of the class. /// protected AIContextProvider() { - this._sourceName = this.GetType().FullName!; + this._sourceId = this.GetType().FullName!; } /// - /// Initializes a new instance of the class with the specified source name. + /// Initializes a new instance of the class with the specified source id. /// - /// The source name to stamp on for each messages produced by the . - protected AIContextProvider(string sourceName) + /// The source id to stamp on for each messages produced by the . + protected AIContextProvider(string sourceId) { - this._sourceName = sourceName; + this._sourceId = sourceId; } /// @@ -76,27 +76,9 @@ public abstract class AIContextProvider return aiContext; } - aiContext.Messages = aiContext.Messages.Select(message => - { - if (message.AdditionalProperties != null - // Check if the message was already tagged with this provider's source type - && message.AdditionalProperties.TryGetValue(AgentRequestMessageSourceType.AdditionalPropertiesKey, out var messageSourceType) - && messageSourceType is AgentRequestMessageSourceType typedMessageSourceType - && typedMessageSourceType == AgentRequestMessageSourceType.AIContextProvider - // Check if the message was already tagged with this provider's source - && message.AdditionalProperties.TryGetValue(AgentRequestMessageSource.AdditionalPropertiesKey, out var messageSource) - && messageSource is string typedMessageSource - && typedMessageSource == this._sourceName) - { - return message; - } - - message = message.Clone(); - message.AdditionalProperties ??= new(); - message.AdditionalProperties[AgentRequestMessageSourceType.AdditionalPropertiesKey] = AgentRequestMessageSourceType.AIContextProvider; - message.AdditionalProperties[AgentRequestMessageSource.AdditionalPropertiesKey] = this._sourceName; - return message; - }).ToList(); + aiContext.Messages = aiContext.Messages + .Select(message => message.AsAgentRequestMessageSourcedMessage(AgentRequestMessageSourceType.AIContextProvider, this._sourceId)) + .ToList(); return aiContext; } diff --git a/dotnet/src/Microsoft.Agents.AI.Abstractions/AgentRequestMessageSource.cs b/dotnet/src/Microsoft.Agents.AI.Abstractions/AgentRequestMessageSource.cs deleted file mode 100644 index 127f1c1b8d..0000000000 --- a/dotnet/src/Microsoft.Agents.AI.Abstractions/AgentRequestMessageSource.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. - -using Microsoft.Extensions.AI; - -namespace Microsoft.Agents.AI; - -/// -/// Provides a constant for the key used to store the source of the agent request message. -/// -public static class AgentRequestMessageSource -{ - /// - /// Provides the key used in to store the source of the agent request message. - /// - public static readonly string AdditionalPropertiesKey = "Agent.RequestMessageSource"; -} diff --git a/dotnet/src/Microsoft.Agents.AI.Abstractions/AgentRequestMessageSourceAttribution.cs b/dotnet/src/Microsoft.Agents.AI.Abstractions/AgentRequestMessageSourceAttribution.cs new file mode 100644 index 0000000000..2c606814ce --- /dev/null +++ b/dotnet/src/Microsoft.Agents.AI.Abstractions/AgentRequestMessageSourceAttribution.cs @@ -0,0 +1,102 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System; +using Microsoft.Extensions.AI; + +namespace Microsoft.Agents.AI; + +/// +/// Represents attribution information for the source of an agent request message for a specific run, including the component type and +/// identifier. +/// +/// +/// Use this struct to identify which component provided a message during an agent run. +/// This is useful to allow filtering of messages based on their source, such as distinguishing between user input, middleware-generated messages, and chat history. +/// +public readonly struct AgentRequestMessageSourceAttribution : IEquatable +{ + /// + /// Provides the key used in to store the + /// associated with the agent request message. + /// + public static readonly string AdditionalPropertiesKey = "_attribution"; + + /// + /// Initializes a new instance of the struct with the specified source type and identifier. + /// + /// The of the component that provided the message. + /// The unique identifier of the component that provided the message. + public AgentRequestMessageSourceAttribution(AgentRequestMessageSourceType sourceType, string? sourceId) + { + this.SourceType = sourceType; + this.SourceId = sourceId; + } + + /// + /// Gets the type of component that provided the message for the current agent run. + /// + public AgentRequestMessageSourceType SourceType { get; } + + /// + /// Gets the unique identifier of the component that provided the message for the current agent run. + /// + public string? SourceId { get; } + + /// + /// Determines whether the specified is equal to the current instance. + /// + /// The to compare with the current instance. + /// if the specified instance is equal to the current instance; otherwise, . + public bool Equals(AgentRequestMessageSourceAttribution other) + { + return this.SourceType == other.SourceType && + string.Equals(this.SourceId, other.SourceId, StringComparison.Ordinal); + } + + /// + /// Determines whether the specified object is equal to the current instance. + /// + /// The object to compare with the current instance. + /// if the specified object is equal to the current instance; otherwise, . + public override bool Equals(object? obj) + { + return obj is AgentRequestMessageSourceAttribution other && this.Equals(other); + } + + /// + /// Returns a hash code for the current instance. + /// + /// A hash code for the current instance. + public override int GetHashCode() + { + unchecked + { + int hash = 17; + hash = (hash * 31) + this.SourceType.GetHashCode(); + hash = (hash * 31) + (this.SourceId?.GetHashCode() ?? 0); + return hash; + } + } + + /// + /// Determines whether two instances are equal. + /// + /// The first instance to compare. + /// The second instance to compare. + /// if the instances are equal; otherwise, . + public static bool operator ==(AgentRequestMessageSourceAttribution left, AgentRequestMessageSourceAttribution right) + { + return left.Equals(right); + } + + /// + /// Determines whether two instances are not equal. + /// + /// The first instance to compare. + /// The second instance to compare. + /// if the instances are not equal; otherwise, . + public static bool operator !=(AgentRequestMessageSourceAttribution left, AgentRequestMessageSourceAttribution right) + { + return !left.Equals(right); + } +} diff --git a/dotnet/src/Microsoft.Agents.AI.Abstractions/AgentRequestMessageSourceType.cs b/dotnet/src/Microsoft.Agents.AI.Abstractions/AgentRequestMessageSourceType.cs index 1cca747906..14bbbe3388 100644 --- a/dotnet/src/Microsoft.Agents.AI.Abstractions/AgentRequestMessageSourceType.cs +++ b/dotnet/src/Microsoft.Agents.AI.Abstractions/AgentRequestMessageSourceType.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. using System; -using Microsoft.Extensions.AI; using Microsoft.Shared.Diagnostics; namespace Microsoft.Agents.AI; @@ -14,15 +13,10 @@ namespace Microsoft.Agents.AI; /// This type helps to identify whether a message came from outside the agent pipeline, /// whether it was produced by middleware, or came from chat history. /// -public sealed class AgentRequestMessageSourceType : IEquatable +public readonly struct AgentRequestMessageSourceType : IEquatable { /// - /// Provides the key used in to store the source type of the agent request message. - /// - public static readonly string AdditionalPropertiesKey = "Agent.RequestMessageSourceType"; - - /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the struct. /// /// The string value representing the source of the agent request message. public AgentRequestMessageSourceType(string value) => this.Value = Throw.IfNullOrWhitespace(value); @@ -30,7 +24,7 @@ public sealed class AgentRequestMessageSourceType : IEquatable /// Get the string value representing the source of the agent request message. /// - public string Value { get; } + public string Value { get { return field ?? External.Value; } } /// /// The message came from outside the agent pipeline (e.g., user input). @@ -52,18 +46,8 @@ public sealed class AgentRequestMessageSourceType : IEquatable /// The to compare to this instance. /// if the value of the parameter is the same as the value of this instance; otherwise, . - public bool Equals(AgentRequestMessageSourceType? other) + public bool Equals(AgentRequestMessageSourceType other) { - if (other is null) - { - return false; - } - - if (ReferenceEquals(this, other)) - { - return true; - } - return string.Equals(this.Value, other.Value, StringComparison.Ordinal); } @@ -72,7 +56,7 @@ public sealed class AgentRequestMessageSourceType : IEquatable /// The object to compare to this instance. /// if is a and its value is the same as this instance; otherwise, . - public override bool Equals(object? obj) => this.Equals(obj as AgentRequestMessageSourceType); + public override bool Equals(object? obj) => obj is AgentRequestMessageSourceType other && this.Equals(other); /// /// Returns the hash code for this instance. @@ -86,13 +70,8 @@ public sealed class AgentRequestMessageSourceType : IEquatableThe first to compare. /// The second to compare. /// if the value of is the same as the value of ; otherwise, . - public static bool operator ==(AgentRequestMessageSourceType? left, AgentRequestMessageSourceType? right) + public static bool operator ==(AgentRequestMessageSourceType left, AgentRequestMessageSourceType right) { - if (left is null) - { - return right is null; - } - return left.Equals(right); } @@ -102,5 +81,5 @@ public sealed class AgentRequestMessageSourceType : IEquatableThe first to compare. /// The second to compare. /// if the value of is different from the value of ; otherwise, . - public static bool operator !=(AgentRequestMessageSourceType? left, AgentRequestMessageSourceType? right) => !(left == right); + public static bool operator !=(AgentRequestMessageSourceType left, AgentRequestMessageSourceType right) => !(left == right); } diff --git a/dotnet/src/Microsoft.Agents.AI.Abstractions/ChatHistoryProvider.cs b/dotnet/src/Microsoft.Agents.AI.Abstractions/ChatHistoryProvider.cs index f49c5d46a7..086ad02061 100644 --- a/dotnet/src/Microsoft.Agents.AI.Abstractions/ChatHistoryProvider.cs +++ b/dotnet/src/Microsoft.Agents.AI.Abstractions/ChatHistoryProvider.cs @@ -37,23 +37,23 @@ namespace Microsoft.Agents.AI; /// public abstract class ChatHistoryProvider { - private readonly string _sourceName; + private readonly string _sourceId; /// /// Initializes a new instance of the class. /// protected ChatHistoryProvider() { - this._sourceName = this.GetType().FullName!; + this._sourceId = this.GetType().FullName!; } /// - /// Initializes a new instance of the class with the specified source name. + /// Initializes a new instance of the class with the specified source id. /// - /// The source name to stamp on for each messages produced by the . - protected ChatHistoryProvider(string sourceName) + /// The source id to stamp on for each messages produced by the . + protected ChatHistoryProvider(string sourceId) { - this._sourceName = sourceName; + this._sourceId = sourceId; } /// @@ -89,27 +89,7 @@ public abstract class ChatHistoryProvider { var messages = await this.InvokingCoreAsync(context, cancellationToken).ConfigureAwait(false); - return messages.Select(message => - { - if (message.AdditionalProperties != null - // Check if the message was already tagged with this provider's source type - && message.AdditionalProperties.TryGetValue(AgentRequestMessageSourceType.AdditionalPropertiesKey, out var messageSourceType) - && messageSourceType is AgentRequestMessageSourceType typedMessageSourceType - && typedMessageSourceType == AgentRequestMessageSourceType.ChatHistory - // Check if the message was already tagged with this provider's source - && message.AdditionalProperties.TryGetValue(AgentRequestMessageSource.AdditionalPropertiesKey, out var messageSource) - && messageSource is string typedMessageSource - && typedMessageSource == this._sourceName) - { - return message; - } - - message = message.Clone(); - message.AdditionalProperties ??= new(); - message.AdditionalProperties[AgentRequestMessageSourceType.AdditionalPropertiesKey] = AgentRequestMessageSourceType.ChatHistory; - message.AdditionalProperties[AgentRequestMessageSource.AdditionalPropertiesKey] = this._sourceName; - return message; - }); + return messages.Select(message => message.AsAgentRequestMessageSourcedMessage(AgentRequestMessageSourceType.ChatHistory, this._sourceId)); } /// diff --git a/dotnet/src/Microsoft.Agents.AI.Abstractions/ChatHistoryProviderExtensions.cs b/dotnet/src/Microsoft.Agents.AI.Abstractions/ChatHistoryProviderExtensions.cs index c2ff8bf3e5..4c8ef2489f 100644 --- a/dotnet/src/Microsoft.Agents.AI.Abstractions/ChatHistoryProviderExtensions.cs +++ b/dotnet/src/Microsoft.Agents.AI.Abstractions/ChatHistoryProviderExtensions.cs @@ -45,7 +45,7 @@ public static class ChatHistoryProviderExtensions innerProvider: provider, invokedMessagesFilter: (ctx) => { - ctx.RequestMessages = ctx.RequestMessages.Where(x => x.GetAgentRequestMessageSource() != AgentRequestMessageSourceType.AIContextProvider); + ctx.RequestMessages = ctx.RequestMessages.Where(x => x.GetAgentRequestMessageSourceType() != AgentRequestMessageSourceType.AIContextProvider); return ctx; }); } diff --git a/dotnet/src/Microsoft.Agents.AI.Abstractions/ChatMessageExtensions.cs b/dotnet/src/Microsoft.Agents.AI.Abstractions/ChatMessageExtensions.cs index 01edcb4eff..052e48ce56 100644 --- a/dotnet/src/Microsoft.Agents.AI.Abstractions/ChatMessageExtensions.cs +++ b/dotnet/src/Microsoft.Agents.AI.Abstractions/ChatMessageExtensions.cs @@ -10,18 +10,66 @@ namespace Microsoft.Agents.AI; public static class ChatMessageExtensions { /// - /// Gets the source of the provided in the context of messages passed into an agent run. + /// Gets the source type of the provided in the context of messages passed into an agent run. /// - /// The for which we need the source. - /// An value indicating the source of the . Defaults to The for which we need the source type. + /// An value indicating the source type of the . Defaults to if no explicit source is defined. - public static AgentRequestMessageSourceType GetAgentRequestMessageSource(this ChatMessage message) + public static AgentRequestMessageSourceType GetAgentRequestMessageSourceType(this ChatMessage message) { - if (message.AdditionalProperties?.TryGetValue(AgentRequestMessageSourceType.AdditionalPropertiesKey, out var source) is true && source is AgentRequestMessageSourceType typedSource) + if (message.AdditionalProperties?.TryGetValue(AgentRequestMessageSourceAttribution.AdditionalPropertiesKey, out var attribution) is true + && attribution is AgentRequestMessageSourceAttribution typedAttribution) { - return typedSource; + return typedAttribution.SourceType; } return AgentRequestMessageSourceType.External; } + + /// + /// Gets the source id of the provided in the context of messages passed into an agent run. + /// + /// The for which we need the source id. + /// An value indicating the source id of the . Defaults to + /// if no explicit source id is defined. + public static string? GetAgentRequestMessageSourceId(this ChatMessage message) + { + if (message.AdditionalProperties?.TryGetValue(AgentRequestMessageSourceAttribution.AdditionalPropertiesKey, out var attribution) is true + && attribution is AgentRequestMessageSourceAttribution typedAttribution) + { + return typedAttribution.SourceId; + } + + return null; + } + + /// + /// Ensure that the provided message is tagged with the provided source type and source id in the context of a specific agent run. + /// + /// The message to tag. + /// The source type to tag the message with. + /// The source id to tag the message with. + /// The tagged message. + /// + /// If the message is already tagged with the provided source type and source id, it is returned as is. + /// Otherwise, a cloned message is returned with the appropriate tagging in the AdditionalProperties. + /// + public static ChatMessage AsAgentRequestMessageSourcedMessage(this ChatMessage message, AgentRequestMessageSourceType sourceType, string? sourceId = null) + { + if (message.AdditionalProperties != null + // Check if the message was already tagged with the required source type and source id + && message.AdditionalProperties.TryGetValue(AgentRequestMessageSourceAttribution.AdditionalPropertiesKey, out var messageSourceAttribution) + && messageSourceAttribution is AgentRequestMessageSourceAttribution typedMessageSourceAttribution + && typedMessageSourceAttribution.SourceType == sourceType + && typedMessageSourceAttribution.SourceId == sourceId) + { + return message; + } + + message = message.Clone(); + message.AdditionalProperties ??= new(); + message.AdditionalProperties[AgentRequestMessageSourceAttribution.AdditionalPropertiesKey] = + new AgentRequestMessageSourceAttribution(sourceType, sourceId); + return message; + } } diff --git a/dotnet/src/Microsoft.Agents.AI.Mem0/Mem0Provider.cs b/dotnet/src/Microsoft.Agents.AI.Mem0/Mem0Provider.cs index 8a0c016f07..a230eb0e4e 100644 --- a/dotnet/src/Microsoft.Agents.AI.Mem0/Mem0Provider.cs +++ b/dotnet/src/Microsoft.Agents.AI.Mem0/Mem0Provider.cs @@ -138,7 +138,7 @@ public sealed class Mem0Provider : AIContextProvider string queryText = string.Join( Environment.NewLine, context.RequestMessages - .Where(m => m.GetAgentRequestMessageSource() == AgentRequestMessageSourceType.External) + .Where(m => m.GetAgentRequestMessageSourceType() == AgentRequestMessageSourceType.External) .Where(m => !string.IsNullOrWhiteSpace(m.Text)) .Select(m => m.Text)); @@ -217,7 +217,7 @@ public sealed class Mem0Provider : AIContextProvider // Persist request and response messages after invocation. await this.PersistMessagesAsync( context.RequestMessages - .Where(m => m.GetAgentRequestMessageSource() == AgentRequestMessageSourceType.External) + .Where(m => m.GetAgentRequestMessageSourceType() == AgentRequestMessageSourceType.External) .Concat(context.ResponseMessages ?? []), cancellationToken).ConfigureAwait(false); } diff --git a/dotnet/src/Microsoft.Agents.AI/Memory/ChatHistoryMemoryProvider.cs b/dotnet/src/Microsoft.Agents.AI/Memory/ChatHistoryMemoryProvider.cs index c63e8ac682..b8e495152c 100644 --- a/dotnet/src/Microsoft.Agents.AI/Memory/ChatHistoryMemoryProvider.cs +++ b/dotnet/src/Microsoft.Agents.AI/Memory/ChatHistoryMemoryProvider.cs @@ -189,7 +189,7 @@ public sealed class ChatHistoryMemoryProvider : AIContextProvider, IDisposable { // Get the text from the current request messages var requestText = string.Join("\n", context.RequestMessages - .Where(m => m.GetAgentRequestMessageSource() == AgentRequestMessageSourceType.External) + .Where(m => m.GetAgentRequestMessageSourceType() == AgentRequestMessageSourceType.External) .Where(m => m != null && !string.IsNullOrWhiteSpace(m.Text)) .Select(m => m.Text)); @@ -245,7 +245,7 @@ public sealed class ChatHistoryMemoryProvider : AIContextProvider, IDisposable var collection = await this.EnsureCollectionExistsAsync(cancellationToken).ConfigureAwait(false); List> itemsToStore = context.RequestMessages - .Where(m => m.GetAgentRequestMessageSource() == AgentRequestMessageSourceType.External) + .Where(m => m.GetAgentRequestMessageSourceType() == AgentRequestMessageSourceType.External) .Concat(context.ResponseMessages ?? []) .Select(message => new Dictionary { diff --git a/dotnet/src/Microsoft.Agents.AI/TextSearchProvider.cs b/dotnet/src/Microsoft.Agents.AI/TextSearchProvider.cs index ee87d4f00c..f29aadf808 100644 --- a/dotnet/src/Microsoft.Agents.AI/TextSearchProvider.cs +++ b/dotnet/src/Microsoft.Agents.AI/TextSearchProvider.cs @@ -118,7 +118,7 @@ public sealed class TextSearchProvider : AIContextProvider // Aggregate text from memory + current request messages. var sbInput = new StringBuilder(); var requestMessagesText = context.RequestMessages - .Where(m => m.GetAgentRequestMessageSource() == AgentRequestMessageSourceType.External) + .Where(m => m.GetAgentRequestMessageSourceType() == AgentRequestMessageSourceType.External) .Where(x => !string.IsNullOrWhiteSpace(x?.Text)).Select(x => x.Text); foreach (var messageText in this._recentMessagesText.Concat(requestMessagesText)) { @@ -182,7 +182,7 @@ public sealed class TextSearchProvider : AIContextProvider } var messagesText = context.RequestMessages - .Where(m => m.GetAgentRequestMessageSource() == AgentRequestMessageSourceType.External) + .Where(m => m.GetAgentRequestMessageSourceType() == AgentRequestMessageSourceType.External) .Concat(context.ResponseMessages ?? []) .Where(m => this._recentMessageRolesIncluded.Contains(m.Role) && diff --git a/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AIContextProviderTests.cs b/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AIContextProviderTests.cs index 44d1be2e74..aa41a03efc 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AIContextProviderTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AIContextProviderTests.cs @@ -19,7 +19,7 @@ public class AIContextProviderTests #region InvokingAsync Message Stamping Tests [Fact] - public async Task InvokingAsync_StampsMessagesWithSourceTypeAndSourceAsync() + public async Task InvokingAsync_StampsMessagesWithSourceTypeAndSourceIdAsync() { // Arrange var provider = new TestAIContextProviderWithMessages(); @@ -32,18 +32,18 @@ public class AIContextProviderTests Assert.NotNull(aiContext.Messages); ChatMessage message = aiContext.Messages.Single(); Assert.NotNull(message.AdditionalProperties); - Assert.True(message.AdditionalProperties.TryGetValue(AgentRequestMessageSourceType.AdditionalPropertiesKey, out object? sourceType)); - Assert.Equal(AgentRequestMessageSourceType.AIContextProvider, sourceType); - Assert.True(message.AdditionalProperties.TryGetValue(AgentRequestMessageSource.AdditionalPropertiesKey, out object? source)); - Assert.Equal(typeof(TestAIContextProviderWithMessages).FullName, source); + Assert.True(message.AdditionalProperties.TryGetValue(AgentRequestMessageSourceAttribution.AdditionalPropertiesKey, out object? attribution)); + var typedAttribution = Assert.IsType(attribution); + Assert.Equal(AgentRequestMessageSourceType.AIContextProvider, typedAttribution.SourceType); + Assert.Equal(typeof(TestAIContextProviderWithMessages).FullName, typedAttribution.SourceId); } [Fact] - public async Task InvokingAsync_WithCustomSourceName_StampsMessagesWithCustomSourceAsync() + public async Task InvokingAsync_WithCustomSourceId_StampsMessagesWithCustomSourceIdAsync() { // Arrange - const string CustomSourceName = "CustomContextSource"; - var provider = new TestAIContextProviderWithCustomSource(CustomSourceName); + const string CustomSourceId = "CustomContextSource"; + var provider = new TestAIContextProviderWithCustomSource(CustomSourceId); var context = new AIContextProvider.InvokingContext(s_mockAgent, s_mockSession, [new ChatMessage(ChatRole.User, "Request")]); // Act @@ -53,10 +53,10 @@ public class AIContextProviderTests Assert.NotNull(aiContext.Messages); ChatMessage message = aiContext.Messages.Single(); Assert.NotNull(message.AdditionalProperties); - Assert.True(message.AdditionalProperties.TryGetValue(AgentRequestMessageSourceType.AdditionalPropertiesKey, out object? sourceType)); - Assert.Equal(AgentRequestMessageSourceType.AIContextProvider, sourceType); - Assert.True(message.AdditionalProperties.TryGetValue(AgentRequestMessageSource.AdditionalPropertiesKey, out object? source)); - Assert.Equal(CustomSourceName, source); + Assert.True(message.AdditionalProperties.TryGetValue(AgentRequestMessageSourceAttribution.AdditionalPropertiesKey, out object? attribution)); + var typedAttribution = Assert.IsType(attribution); + Assert.Equal(AgentRequestMessageSourceType.AIContextProvider, typedAttribution.SourceType); + Assert.Equal(CustomSourceId, typedAttribution.SourceId); } [Fact] @@ -73,10 +73,10 @@ public class AIContextProviderTests Assert.NotNull(aiContext.Messages); ChatMessage message = aiContext.Messages.Single(); Assert.NotNull(message.AdditionalProperties); - Assert.True(message.AdditionalProperties.TryGetValue(AgentRequestMessageSourceType.AdditionalPropertiesKey, out object? sourceType)); - Assert.Equal(AgentRequestMessageSourceType.AIContextProvider, sourceType); - Assert.True(message.AdditionalProperties.TryGetValue(AgentRequestMessageSource.AdditionalPropertiesKey, out object? source)); - Assert.Equal(typeof(TestAIContextProviderWithPreStampedMessages).FullName, source); + Assert.True(message.AdditionalProperties.TryGetValue(AgentRequestMessageSourceAttribution.AdditionalPropertiesKey, out object? attribution)); + var typedAttribution = Assert.IsType(attribution); + Assert.Equal(AgentRequestMessageSourceType.AIContextProvider, typedAttribution.SourceType); + Assert.Equal(typeof(TestAIContextProviderWithPreStampedMessages).FullName, typedAttribution.SourceId); } [Fact] @@ -97,10 +97,10 @@ public class AIContextProviderTests foreach (ChatMessage message in messageList) { Assert.NotNull(message.AdditionalProperties); - Assert.True(message.AdditionalProperties.TryGetValue(AgentRequestMessageSourceType.AdditionalPropertiesKey, out object? sourceType)); - Assert.Equal(AgentRequestMessageSourceType.AIContextProvider, sourceType); - Assert.True(message.AdditionalProperties.TryGetValue(AgentRequestMessageSource.AdditionalPropertiesKey, out object? source)); - Assert.Equal(typeof(TestAIContextProviderWithMultipleMessages).FullName, source); + Assert.True(message.AdditionalProperties.TryGetValue(AgentRequestMessageSourceAttribution.AdditionalPropertiesKey, out object? attribution)); + var typedAttribution = Assert.IsType(attribution); + Assert.Equal(AgentRequestMessageSourceType.AIContextProvider, typedAttribution.SourceType); + Assert.Equal(typeof(TestAIContextProviderWithMultipleMessages).FullName, typedAttribution.SourceId); } } @@ -486,7 +486,7 @@ public class AIContextProviderTests private sealed class TestAIContextProviderWithCustomSource : AIContextProvider { - public TestAIContextProviderWithCustomSource(string sourceName) : base(sourceName) + public TestAIContextProviderWithCustomSource(string sourceId) : base(sourceId) { } @@ -504,8 +504,7 @@ public class AIContextProviderTests var message = new ChatMessage(ChatRole.System, "Pre-stamped Message"); message.AdditionalProperties = new AdditionalPropertiesDictionary { - [AgentRequestMessageSourceType.AdditionalPropertiesKey] = AgentRequestMessageSourceType.AIContextProvider, - [AgentRequestMessageSource.AdditionalPropertiesKey] = this.GetType().FullName! + [AgentRequestMessageSourceAttribution.AdditionalPropertiesKey] = new AgentRequestMessageSourceAttribution(AgentRequestMessageSourceType.AIContextProvider, this.GetType().FullName!) }; return new(new AIContext { diff --git a/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AgentRequestMessageSourceAttributionTests.cs b/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AgentRequestMessageSourceAttributionTests.cs new file mode 100644 index 0000000000..5aee121097 --- /dev/null +++ b/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AgentRequestMessageSourceAttributionTests.cs @@ -0,0 +1,466 @@ +// Copyright (c) Microsoft. All rights reserved. + +namespace Microsoft.Agents.AI.Abstractions.UnitTests; + +/// +/// Contains tests for the struct. +/// +public sealed class AgentRequestMessageSourceAttributionTests +{ + #region Constructor Tests + + [Fact] + public void Constructor_SetsSourceTypeAndSourceId() + { + // Arrange + AgentRequestMessageSourceType expectedType = AgentRequestMessageSourceType.AIContextProvider; + const string ExpectedId = "MyProvider"; + + // Act + AgentRequestMessageSourceAttribution attribution = new(expectedType, ExpectedId); + + // Assert + Assert.Equal(expectedType, attribution.SourceType); + Assert.Equal(ExpectedId, attribution.SourceId); + } + + [Fact] + public void Constructor_WithNullSourceId_SetsNullSourceId() + { + // Arrange + AgentRequestMessageSourceType sourceType = AgentRequestMessageSourceType.ChatHistory; + + // Act + AgentRequestMessageSourceAttribution attribution = new(sourceType, null); + + // Assert + Assert.Equal(sourceType, attribution.SourceType); + Assert.Null(attribution.SourceId); + } + + #endregion + + #region AdditionalPropertiesKey Tests + + [Fact] + public void AdditionalPropertiesKey_IsAttribution() + { + // Assert + Assert.Equal("_attribution", AgentRequestMessageSourceAttribution.AdditionalPropertiesKey); + } + + #endregion + + #region Default Value Tests + + [Fact] + public void Default_HasDefaultSourceTypeAndNullSourceId() + { + // Arrange & Act + AgentRequestMessageSourceAttribution attribution = default; + + // Assert + Assert.Equal(default, attribution.SourceType); + Assert.Null(attribution.SourceId); + } + + #endregion + + #region Equals (IEquatable) Tests + + [Fact] + public void Equals_WithSameSourceTypeAndSourceId_ReturnsTrue() + { + // Arrange + AgentRequestMessageSourceAttribution attribution1 = new(AgentRequestMessageSourceType.AIContextProvider, "Provider1"); + AgentRequestMessageSourceAttribution attribution2 = new(AgentRequestMessageSourceType.AIContextProvider, "Provider1"); + + // Act + bool result = attribution1.Equals(attribution2); + + // Assert + Assert.True(result); + } + + [Fact] + public void Equals_WithDifferentSourceType_ReturnsFalse() + { + // Arrange + AgentRequestMessageSourceAttribution attribution1 = new(AgentRequestMessageSourceType.AIContextProvider, "Provider1"); + AgentRequestMessageSourceAttribution attribution2 = new(AgentRequestMessageSourceType.ChatHistory, "Provider1"); + + // Act + bool result = attribution1.Equals(attribution2); + + // Assert + Assert.False(result); + } + + [Fact] + public void Equals_WithDifferentSourceId_ReturnsFalse() + { + // Arrange + AgentRequestMessageSourceAttribution attribution1 = new(AgentRequestMessageSourceType.AIContextProvider, "Provider1"); + AgentRequestMessageSourceAttribution attribution2 = new(AgentRequestMessageSourceType.AIContextProvider, "Provider2"); + + // Act + bool result = attribution1.Equals(attribution2); + + // Assert + Assert.False(result); + } + + [Fact] + public void Equals_WithDifferentSourceTypeAndSourceId_ReturnsFalse() + { + // Arrange + AgentRequestMessageSourceAttribution attribution1 = new(AgentRequestMessageSourceType.AIContextProvider, "Provider1"); + AgentRequestMessageSourceAttribution attribution2 = new(AgentRequestMessageSourceType.ChatHistory, "Provider2"); + + // Act + bool result = attribution1.Equals(attribution2); + + // Assert + Assert.False(result); + } + + [Fact] + public void Equals_WithDifferentCaseSourceId_ReturnsFalse() + { + // Arrange + AgentRequestMessageSourceAttribution attribution1 = new(AgentRequestMessageSourceType.AIContextProvider, "Provider"); + AgentRequestMessageSourceAttribution attribution2 = new(AgentRequestMessageSourceType.AIContextProvider, "provider"); + + // Act + bool result = attribution1.Equals(attribution2); + + // Assert + Assert.False(result); + } + + [Fact] + public void Equals_BothDefaultValues_ReturnsTrue() + { + // Arrange + AgentRequestMessageSourceAttribution attribution1 = default; + AgentRequestMessageSourceAttribution attribution2 = default; + + // Act + bool result = attribution1.Equals(attribution2); + + // Assert + Assert.True(result); + } + + [Fact] + public void Equals_WithBothNullSourceIds_ReturnsTrue() + { + // Arrange + AgentRequestMessageSourceAttribution attribution1 = new(AgentRequestMessageSourceType.External, null!); + AgentRequestMessageSourceAttribution attribution2 = new(AgentRequestMessageSourceType.External, null!); + + // Act + bool result = attribution1.Equals(attribution2); + + // Assert + Assert.True(result); + } + + [Fact] + public void Equals_WithOneNullSourceId_ReturnsFalse() + { + // Arrange + AgentRequestMessageSourceAttribution attribution1 = new(AgentRequestMessageSourceType.External, "Provider1"); + AgentRequestMessageSourceAttribution attribution2 = new(AgentRequestMessageSourceType.External, null!); + + // Act + bool result = attribution1.Equals(attribution2); + + // Assert + Assert.False(result); + } + + #endregion + + #region Object.Equals Tests + + [Fact] + public void ObjectEquals_WithEqualAttribution_ReturnsTrue() + { + // Arrange + AgentRequestMessageSourceAttribution attribution1 = new(AgentRequestMessageSourceType.ChatHistory, "Provider"); + object attribution2 = new AgentRequestMessageSourceAttribution(AgentRequestMessageSourceType.ChatHistory, "Provider"); + + // Act + bool result = attribution1.Equals(attribution2); + + // Assert + Assert.True(result); + } + + [Fact] + public void ObjectEquals_WithDifferentType_ReturnsFalse() + { + // Arrange + AgentRequestMessageSourceAttribution attribution = new(AgentRequestMessageSourceType.ChatHistory, "Provider"); + object other = "NotAnAttribution"; + + // Act + bool result = attribution.Equals(other); + + // Assert + Assert.False(result); + } + + [Fact] + public void ObjectEquals_WithNullObject_ReturnsFalse() + { + // Arrange + AgentRequestMessageSourceAttribution attribution = new(AgentRequestMessageSourceType.ChatHistory, "Provider"); + object? other = null; + + // Act + bool result = attribution.Equals(other); + + // Assert + Assert.False(result); + } + + [Fact] + public void ObjectEquals_WithBoxedDifferentAttribution_ReturnsFalse() + { + // Arrange + AgentRequestMessageSourceAttribution attribution1 = new(AgentRequestMessageSourceType.ChatHistory, "Provider1"); + object attribution2 = new AgentRequestMessageSourceAttribution(AgentRequestMessageSourceType.ChatHistory, "Provider2"); + + // Act + bool result = attribution1.Equals(attribution2); + + // Assert + Assert.False(result); + } + + #endregion + + #region GetHashCode Tests + + [Fact] + public void GetHashCode_WithSameValues_ReturnsSameHashCode() + { + // Arrange + AgentRequestMessageSourceAttribution attribution1 = new(AgentRequestMessageSourceType.AIContextProvider, "Provider"); + AgentRequestMessageSourceAttribution attribution2 = new(AgentRequestMessageSourceType.AIContextProvider, "Provider"); + + // Act + int hashCode1 = attribution1.GetHashCode(); + int hashCode2 = attribution2.GetHashCode(); + + // Assert + Assert.Equal(hashCode1, hashCode2); + } + + [Fact] + public void GetHashCode_WithDifferentSourceType_ReturnsDifferentHashCode() + { + // Arrange + AgentRequestMessageSourceAttribution attribution1 = new(AgentRequestMessageSourceType.AIContextProvider, "Provider"); + AgentRequestMessageSourceAttribution attribution2 = new(AgentRequestMessageSourceType.ChatHistory, "Provider"); + + // Act + int hashCode1 = attribution1.GetHashCode(); + int hashCode2 = attribution2.GetHashCode(); + + // Assert + Assert.NotEqual(hashCode1, hashCode2); + } + + [Fact] + public void GetHashCode_WithDifferentSourceId_ReturnsDifferentHashCode() + { + // Arrange + AgentRequestMessageSourceAttribution attribution1 = new(AgentRequestMessageSourceType.AIContextProvider, "Provider1"); + AgentRequestMessageSourceAttribution attribution2 = new(AgentRequestMessageSourceType.AIContextProvider, "Provider2"); + + // Act + int hashCode1 = attribution1.GetHashCode(); + int hashCode2 = attribution2.GetHashCode(); + + // Assert + Assert.NotEqual(hashCode1, hashCode2); + } + + [Fact] + public void GetHashCode_ConsistentWithEquals() + { + // Arrange + AgentRequestMessageSourceAttribution attribution1 = new(AgentRequestMessageSourceType.External, "Provider"); + AgentRequestMessageSourceAttribution attribution2 = new(AgentRequestMessageSourceType.External, "Provider"); + + // Act & Assert + Assert.True(attribution1.Equals(attribution2)); + Assert.Equal(attribution1.GetHashCode(), attribution2.GetHashCode()); + } + + [Fact] + public void GetHashCode_WithNullSourceId_DoesNotThrow() + { + // Arrange + AgentRequestMessageSourceAttribution attribution = new(AgentRequestMessageSourceType.External, null!); + + // Act + int hashCode = attribution.GetHashCode(); + + // Assert + Assert.IsType(hashCode); + } + + #endregion + + #region Equality Operator Tests + + [Fact] + public void EqualityOperator_WithEqualValues_ReturnsTrue() + { + // Arrange + AgentRequestMessageSourceAttribution attribution1 = new(AgentRequestMessageSourceType.AIContextProvider, "Provider"); + AgentRequestMessageSourceAttribution attribution2 = new(AgentRequestMessageSourceType.AIContextProvider, "Provider"); + + // Act + bool result = attribution1 == attribution2; + + // Assert + Assert.True(result); + } + + [Fact] + public void EqualityOperator_WithDifferentValues_ReturnsFalse() + { + // Arrange + AgentRequestMessageSourceAttribution attribution1 = new(AgentRequestMessageSourceType.AIContextProvider, "Provider1"); + AgentRequestMessageSourceAttribution attribution2 = new(AgentRequestMessageSourceType.ChatHistory, "Provider2"); + + // Act + bool result = attribution1 == attribution2; + + // Assert + Assert.False(result); + } + + [Fact] + public void EqualityOperator_WithBothDefault_ReturnsTrue() + { + // Arrange + AgentRequestMessageSourceAttribution attribution1 = default; + AgentRequestMessageSourceAttribution attribution2 = default; + + // Act + bool result = attribution1 == attribution2; + + // Assert + Assert.True(result); + } + + [Fact] + public void EqualityOperator_WithDifferentSourceTypeOnly_ReturnsFalse() + { + // Arrange + AgentRequestMessageSourceAttribution attribution1 = new(AgentRequestMessageSourceType.AIContextProvider, "Provider"); + AgentRequestMessageSourceAttribution attribution2 = new(AgentRequestMessageSourceType.External, "Provider"); + + // Act + bool result = attribution1 == attribution2; + + // Assert + Assert.False(result); + } + + [Fact] + public void EqualityOperator_WithDifferentSourceIdOnly_ReturnsFalse() + { + // Arrange + AgentRequestMessageSourceAttribution attribution1 = new(AgentRequestMessageSourceType.AIContextProvider, "Provider1"); + AgentRequestMessageSourceAttribution attribution2 = new(AgentRequestMessageSourceType.AIContextProvider, "Provider2"); + + // Act + bool result = attribution1 == attribution2; + + // Assert + Assert.False(result); + } + + #endregion + + #region Inequality Operator Tests + + [Fact] + public void InequalityOperator_WithEqualValues_ReturnsFalse() + { + // Arrange + AgentRequestMessageSourceAttribution attribution1 = new(AgentRequestMessageSourceType.AIContextProvider, "Provider"); + AgentRequestMessageSourceAttribution attribution2 = new(AgentRequestMessageSourceType.AIContextProvider, "Provider"); + + // Act + bool result = attribution1 != attribution2; + + // Assert + Assert.False(result); + } + + [Fact] + public void InequalityOperator_WithDifferentValues_ReturnsTrue() + { + // Arrange + AgentRequestMessageSourceAttribution attribution1 = new(AgentRequestMessageSourceType.AIContextProvider, "Provider1"); + AgentRequestMessageSourceAttribution attribution2 = new(AgentRequestMessageSourceType.ChatHistory, "Provider2"); + + // Act + bool result = attribution1 != attribution2; + + // Assert + Assert.True(result); + } + + [Fact] + public void InequalityOperator_WithBothDefault_ReturnsFalse() + { + // Arrange + AgentRequestMessageSourceAttribution attribution1 = default; + AgentRequestMessageSourceAttribution attribution2 = default; + + // Act + bool result = attribution1 != attribution2; + + // Assert + Assert.False(result); + } + + [Fact] + public void InequalityOperator_WithDifferentSourceTypeOnly_ReturnsTrue() + { + // Arrange + AgentRequestMessageSourceAttribution attribution1 = new(AgentRequestMessageSourceType.AIContextProvider, "Provider"); + AgentRequestMessageSourceAttribution attribution2 = new(AgentRequestMessageSourceType.External, "Provider"); + + // Act + bool result = attribution1 != attribution2; + + // Assert + Assert.True(result); + } + + [Fact] + public void InequalityOperator_WithDifferentSourceIdOnly_ReturnsTrue() + { + // Arrange + AgentRequestMessageSourceAttribution attribution1 = new(AgentRequestMessageSourceType.AIContextProvider, "Provider1"); + AgentRequestMessageSourceAttribution attribution2 = new(AgentRequestMessageSourceType.AIContextProvider, "Provider2"); + + // Act + bool result = attribution1 != attribution2; + + // Assert + Assert.True(result); + } + + #endregion +} diff --git a/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AgentRequestMessageSourceTypeTests.cs b/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AgentRequestMessageSourceTypeTests.cs index f6149092f3..000505fe32 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AgentRequestMessageSourceTypeTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AgentRequestMessageSourceTypeTests.cs @@ -5,7 +5,7 @@ using System; namespace Microsoft.Agents.AI.Abstractions.UnitTests; /// -/// Contains tests for the class. +/// Contains tests for the struct. /// public sealed class AgentRequestMessageSourceTypeTests { @@ -38,6 +38,16 @@ public sealed class AgentRequestMessageSourceTypeTests Assert.Throws(() => new AgentRequestMessageSourceType(string.Empty)); } + [Fact] + public void Default_DefaultsToExternal() + { + // Act + AgentRequestMessageSourceType defaultSource = default; + + // Assert + Assert.Equal(AgentRequestMessageSourceType.External, defaultSource); + } + #endregion #region Static Properties Tests @@ -49,7 +59,6 @@ public sealed class AgentRequestMessageSourceTypeTests AgentRequestMessageSourceType source = AgentRequestMessageSourceType.External; // Assert - Assert.NotNull(source); Assert.Equal("External", source.Value); } @@ -60,7 +69,6 @@ public sealed class AgentRequestMessageSourceTypeTests AgentRequestMessageSourceType source = AgentRequestMessageSourceType.AIContextProvider; // Assert - Assert.NotNull(source); Assert.Equal("AIContextProvider", source.Value); } @@ -71,22 +79,11 @@ public sealed class AgentRequestMessageSourceTypeTests AgentRequestMessageSourceType source = AgentRequestMessageSourceType.ChatHistory; // Assert - Assert.NotNull(source); Assert.Equal("ChatHistory", source.Value); } [Fact] - public void AdditionalPropertiesKey_ReturnsExpectedValue() - { - // Arrange & Act - string key = AgentRequestMessageSourceType.AdditionalPropertiesKey; - - // Assert - Assert.Equal("Agent.RequestMessageSourceType", key); - } - - [Fact] - public void StaticProperties_ReturnSameInstanceOnMultipleCalls() + public void StaticProperties_ReturnEqualValuesOnMultipleCalls() { // Arrange & Act AgentRequestMessageSourceType external1 = AgentRequestMessageSourceType.External; @@ -97,9 +94,9 @@ public sealed class AgentRequestMessageSourceTypeTests AgentRequestMessageSourceType chatHistory2 = AgentRequestMessageSourceType.ChatHistory; // Assert - Assert.Same(external1, external2); - Assert.Same(aiContextProvider1, aiContextProvider2); - Assert.Same(chatHistory1, chatHistory2); + Assert.Equal(external1, external2); + Assert.Equal(aiContextProvider1, aiContextProvider2); + Assert.Equal(chatHistory1, chatHistory2); } #endregion @@ -148,7 +145,7 @@ public sealed class AgentRequestMessageSourceTypeTests } [Fact] - public void Equals_WithNull_ReturnsFalse() + public void Equals_WithNullObject_ReturnsFalse() { // Arrange AgentRequestMessageSourceType source = new("Test"); @@ -314,11 +311,11 @@ public sealed class AgentRequestMessageSourceTypeTests } [Fact] - public void EqualityOperator_WithBothNull_ReturnsTrue() + public void EqualityOperator_WithDefaultValues_ReturnsTrue() { // Arrange - AgentRequestMessageSourceType? source1 = null; - AgentRequestMessageSourceType? source2 = null; + AgentRequestMessageSourceType source1 = default; + AgentRequestMessageSourceType source2 = default; // Act bool result = source1 == source2; @@ -327,34 +324,6 @@ public sealed class AgentRequestMessageSourceTypeTests Assert.True(result); } - [Fact] - public void EqualityOperator_WithLeftNull_ReturnsFalse() - { - // Arrange - AgentRequestMessageSourceType? source1 = null; - AgentRequestMessageSourceType source2 = new("Test"); - - // Act - bool result = source1 == source2; - - // Assert - Assert.False(result); - } - - [Fact] - public void EqualityOperator_WithRightNull_ReturnsFalse() - { - // Arrange - AgentRequestMessageSourceType source1 = new("Test"); - AgentRequestMessageSourceType? source2 = null; - - // Act - bool result = source1 == source2; - - // Assert - Assert.False(result); - } - [Fact] public void EqualityOperator_WithStaticInstances_ReturnsTrue() { @@ -416,11 +385,11 @@ public sealed class AgentRequestMessageSourceTypeTests } [Fact] - public void InequalityOperator_WithBothNull_ReturnsFalse() + public void InequalityOperator_WithBothDefault_ReturnsFalse() { // Arrange - AgentRequestMessageSourceType? source1 = null; - AgentRequestMessageSourceType? source2 = null; + AgentRequestMessageSourceType source1 = default; + AgentRequestMessageSourceType source2 = default; // Act bool result = source1 != source2; @@ -429,34 +398,6 @@ public sealed class AgentRequestMessageSourceTypeTests Assert.False(result); } - [Fact] - public void InequalityOperator_WithLeftNull_ReturnsTrue() - { - // Arrange - AgentRequestMessageSourceType? source1 = null; - AgentRequestMessageSourceType source2 = new("Test"); - - // Act - bool result = source1 != source2; - - // Assert - Assert.True(result); - } - - [Fact] - public void InequalityOperator_WithRightNull_ReturnsTrue() - { - // Arrange - AgentRequestMessageSourceType source1 = new("Test"); - AgentRequestMessageSourceType? source2 = null; - - // Act - bool result = source1 != source2; - - // Assert - Assert.True(result); - } - [Fact] public void InequalityOperator_DifferentStaticInstances_ReturnsTrue() { diff --git a/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/ChatHistoryProviderExtensionsTests.cs b/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/ChatHistoryProviderExtensionsTests.cs index 1244209a97..1fcbe37e25 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/ChatHistoryProviderExtensionsTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/ChatHistoryProviderExtensionsTests.cs @@ -64,7 +64,7 @@ public sealed class ChatHistoryProviderExtensionsTests Mock providerMock = new(); List requestMessages = [ - new(ChatRole.System, "System") { AdditionalProperties = new() { { AgentRequestMessageSourceType.AdditionalPropertiesKey, AgentRequestMessageSourceType.ChatHistory } } }, + new(ChatRole.System, "System") { AdditionalProperties = new() { { AgentRequestMessageSourceAttribution.AdditionalPropertiesKey, new AgentRequestMessageSourceAttribution(AgentRequestMessageSourceType.ChatHistory, "TestSource") } } }, new(ChatRole.User, "Hello") ]; ChatHistoryProvider.InvokedContext context = new(s_mockAgent, s_mockSession, requestMessages) @@ -114,9 +114,9 @@ public sealed class ChatHistoryProviderExtensionsTests Mock providerMock = new(); List requestMessages = [ - new(ChatRole.System, "System") { AdditionalProperties = new() { { AgentRequestMessageSourceType.AdditionalPropertiesKey, AgentRequestMessageSourceType.ChatHistory } } }, + new(ChatRole.System, "System") { AdditionalProperties = new() { { AgentRequestMessageSourceAttribution.AdditionalPropertiesKey, new AgentRequestMessageSourceAttribution(AgentRequestMessageSourceType.ChatHistory, "TestSource") } } }, new(ChatRole.User, "Hello"), - new(ChatRole.System, "Context") { AdditionalProperties = new() { { AgentRequestMessageSourceType.AdditionalPropertiesKey, AgentRequestMessageSourceType.AIContextProvider } } } + new(ChatRole.System, "Context") { AdditionalProperties = new() { { AgentRequestMessageSourceAttribution.AdditionalPropertiesKey, new AgentRequestMessageSourceAttribution(AgentRequestMessageSourceType.AIContextProvider, "TestContextSource") } } } ]; ChatHistoryProvider.InvokedContext context = new(s_mockAgent, s_mockSession, requestMessages); diff --git a/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/ChatHistoryProviderMessageFilterTests.cs b/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/ChatHistoryProviderMessageFilterTests.cs index 5b48d025be..75d8f554c5 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/ChatHistoryProviderMessageFilterTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/ChatHistoryProviderMessageFilterTests.cs @@ -157,7 +157,7 @@ public sealed class ChatHistoryProviderMessageFilterTests var innerProviderMock = new Mock(); List requestMessages = [ - new(ChatRole.System, "System") { AdditionalProperties = new() { { AgentRequestMessageSourceType.AdditionalPropertiesKey, AgentRequestMessageSourceType.ChatHistory } } }, + new(ChatRole.System, "System") { AdditionalProperties = new() { { AgentRequestMessageSourceAttribution.AdditionalPropertiesKey, new AgentRequestMessageSourceAttribution(AgentRequestMessageSourceType.ChatHistory, "TestSource") } } }, new(ChatRole.User, "Hello"), ]; var responseMessages = new List { new(ChatRole.Assistant, "Response") }; @@ -176,7 +176,7 @@ public sealed class ChatHistoryProviderMessageFilterTests // Filter that modifies the context ChatHistoryProvider.InvokedContext InvokedFilter(ChatHistoryProvider.InvokedContext ctx) { - var modifiedRequestMessages = ctx.RequestMessages.Where(x => x.GetAgentRequestMessageSource() == AgentRequestMessageSourceType.External).Select(m => new ChatMessage(m.Role, $"[FILTERED] {m.Text}")).ToList(); + var modifiedRequestMessages = ctx.RequestMessages.Where(x => x.GetAgentRequestMessageSourceType() == AgentRequestMessageSourceType.External).Select(m => new ChatMessage(m.Role, $"[FILTERED] {m.Text}")).ToList(); return new ChatHistoryProvider.InvokedContext(s_mockAgent, s_mockSession, modifiedRequestMessages) { ResponseMessages = ctx.ResponseMessages, diff --git a/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/ChatHistoryProviderTests.cs b/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/ChatHistoryProviderTests.cs index e158b159ca..8b07366b03 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/ChatHistoryProviderTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/ChatHistoryProviderTests.cs @@ -22,7 +22,7 @@ public class ChatHistoryProviderTests #region InvokingAsync Message Stamping Tests [Fact] - public async Task InvokingAsync_StampsMessagesWithSourceTypeAndSourceAsync() + public async Task InvokingAsync_StampsMessagesWithSourceTypeAndSourceIdAsync() { // Arrange var provider = new TestChatHistoryProvider(); @@ -34,18 +34,18 @@ public class ChatHistoryProviderTests // Assert ChatMessage message = messages.Single(); Assert.NotNull(message.AdditionalProperties); - Assert.True(message.AdditionalProperties.TryGetValue(AgentRequestMessageSourceType.AdditionalPropertiesKey, out object? sourceType)); - Assert.Equal(AgentRequestMessageSourceType.ChatHistory, sourceType); - Assert.True(message.AdditionalProperties.TryGetValue(AgentRequestMessageSource.AdditionalPropertiesKey, out object? source)); - Assert.Equal(typeof(TestChatHistoryProvider).FullName, source); + Assert.True(message.AdditionalProperties.TryGetValue(AgentRequestMessageSourceAttribution.AdditionalPropertiesKey, out object? attribution)); + var typedAttribution = Assert.IsType(attribution); + Assert.Equal(AgentRequestMessageSourceType.ChatHistory, typedAttribution.SourceType); + Assert.Equal(typeof(TestChatHistoryProvider).FullName, typedAttribution.SourceId); } [Fact] - public async Task InvokingAsync_WithCustomSourceName_StampsMessagesWithCustomSourceAsync() + public async Task InvokingAsync_WithCustomSourceId_StampsMessagesWithCustomSourceIdAsync() { // Arrange - const string CustomSourceName = "CustomHistorySource"; - var provider = new TestChatHistoryProviderWithCustomSource(CustomSourceName); + const string CustomSourceId = "CustomHistorySource"; + var provider = new TestChatHistoryProviderWithCustomSource(CustomSourceId); var context = new ChatHistoryProvider.InvokingContext(s_mockAgent, s_mockSession, [new ChatMessage(ChatRole.User, "Request")]); // Act @@ -54,10 +54,10 @@ public class ChatHistoryProviderTests // Assert ChatMessage message = messages.Single(); Assert.NotNull(message.AdditionalProperties); - Assert.True(message.AdditionalProperties.TryGetValue(AgentRequestMessageSourceType.AdditionalPropertiesKey, out object? sourceType)); - Assert.Equal(AgentRequestMessageSourceType.ChatHistory, sourceType); - Assert.True(message.AdditionalProperties.TryGetValue(AgentRequestMessageSource.AdditionalPropertiesKey, out object? source)); - Assert.Equal(CustomSourceName, source); + Assert.True(message.AdditionalProperties.TryGetValue(AgentRequestMessageSourceAttribution.AdditionalPropertiesKey, out object? attribution)); + var typedAttribution = Assert.IsType(attribution); + Assert.Equal(AgentRequestMessageSourceType.ChatHistory, typedAttribution.SourceType); + Assert.Equal(CustomSourceId, typedAttribution.SourceId); } [Fact] @@ -73,10 +73,10 @@ public class ChatHistoryProviderTests // Assert ChatMessage message = messages.Single(); Assert.NotNull(message.AdditionalProperties); - Assert.True(message.AdditionalProperties.TryGetValue(AgentRequestMessageSourceType.AdditionalPropertiesKey, out object? sourceType)); - Assert.Equal(AgentRequestMessageSourceType.ChatHistory, sourceType); - Assert.True(message.AdditionalProperties.TryGetValue(AgentRequestMessageSource.AdditionalPropertiesKey, out object? source)); - Assert.Equal(typeof(TestChatHistoryProviderWithPreStampedMessages).FullName, source); + Assert.True(message.AdditionalProperties.TryGetValue(AgentRequestMessageSourceAttribution.AdditionalPropertiesKey, out object? attribution)); + var typedAttribution = Assert.IsType(attribution); + Assert.Equal(AgentRequestMessageSourceType.ChatHistory, typedAttribution.SourceType); + Assert.Equal(typeof(TestChatHistoryProviderWithPreStampedMessages).FullName, typedAttribution.SourceId); } [Fact] @@ -96,10 +96,10 @@ public class ChatHistoryProviderTests foreach (ChatMessage message in messageList) { Assert.NotNull(message.AdditionalProperties); - Assert.True(message.AdditionalProperties.TryGetValue(AgentRequestMessageSourceType.AdditionalPropertiesKey, out object? sourceType)); - Assert.Equal(AgentRequestMessageSourceType.ChatHistory, sourceType); - Assert.True(message.AdditionalProperties.TryGetValue(AgentRequestMessageSource.AdditionalPropertiesKey, out object? source)); - Assert.Equal(typeof(TestChatHistoryProviderWithMultipleMessages).FullName, source); + Assert.True(message.AdditionalProperties.TryGetValue(AgentRequestMessageSourceAttribution.AdditionalPropertiesKey, out object? attribution)); + var typedAttribution = Assert.IsType(attribution); + Assert.Equal(AgentRequestMessageSourceType.ChatHistory, typedAttribution.SourceType); + Assert.Equal(typeof(TestChatHistoryProviderWithMultipleMessages).FullName, typedAttribution.SourceId); } } @@ -383,7 +383,7 @@ public class ChatHistoryProviderTests private sealed class TestChatHistoryProviderWithCustomSource : ChatHistoryProvider { - public TestChatHistoryProviderWithCustomSource(string sourceName) : base(sourceName) + public TestChatHistoryProviderWithCustomSource(string sourceId) : base(sourceId) { } @@ -404,8 +404,7 @@ public class ChatHistoryProviderTests var message = new ChatMessage(ChatRole.User, "Pre-stamped Message"); message.AdditionalProperties = new AdditionalPropertiesDictionary { - [AgentRequestMessageSourceType.AdditionalPropertiesKey] = AgentRequestMessageSourceType.ChatHistory, - [AgentRequestMessageSource.AdditionalPropertiesKey] = this.GetType().FullName! + [AgentRequestMessageSourceAttribution.AdditionalPropertiesKey] = new AgentRequestMessageSourceAttribution(AgentRequestMessageSourceType.ChatHistory, this.GetType().FullName!) }; return new([message]); } diff --git a/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/ChatMessageExtensionsTests.cs b/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/ChatMessageExtensionsTests.cs index f389c567d2..97050c3071 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/ChatMessageExtensionsTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/ChatMessageExtensionsTests.cs @@ -9,23 +9,23 @@ namespace Microsoft.Agents.AI.Abstractions.UnitTests; /// public sealed class ChatMessageExtensionsTests { - #region GetAgentRequestMessageSource Tests + #region GetAgentRequestMessageSourceType Tests [Fact] - public void GetAgentRequestMessageSource_WithNoAdditionalProperties_ReturnsExternal() + public void GetAgentRequestMessageSourceType_WithNoAdditionalProperties_ReturnsExternal() { // Arrange ChatMessage message = new(ChatRole.User, "Hello"); // Act - AgentRequestMessageSourceType result = message.GetAgentRequestMessageSource(); + AgentRequestMessageSourceType result = message.GetAgentRequestMessageSourceType(); // Assert Assert.Equal(AgentRequestMessageSourceType.External, result); } [Fact] - public void GetAgentRequestMessageSource_WithNullAdditionalProperties_ReturnsExternal() + public void GetAgentRequestMessageSourceType_WithNullAdditionalProperties_ReturnsExternal() { // Arrange ChatMessage message = new(ChatRole.User, "Hello") @@ -34,14 +34,14 @@ public sealed class ChatMessageExtensionsTests }; // Act - AgentRequestMessageSourceType result = message.GetAgentRequestMessageSource(); + AgentRequestMessageSourceType result = message.GetAgentRequestMessageSourceType(); // Assert Assert.Equal(AgentRequestMessageSourceType.External, result); } [Fact] - public void GetAgentRequestMessageSource_WithEmptyAdditionalProperties_ReturnsExternal() + public void GetAgentRequestMessageSourceType_WithEmptyAdditionalProperties_ReturnsExternal() { // Arrange ChatMessage message = new(ChatRole.User, "Hello") @@ -50,130 +50,130 @@ public sealed class ChatMessageExtensionsTests }; // Act - AgentRequestMessageSourceType result = message.GetAgentRequestMessageSource(); + AgentRequestMessageSourceType result = message.GetAgentRequestMessageSourceType(); // Assert Assert.Equal(AgentRequestMessageSourceType.External, result); } [Fact] - public void GetAgentRequestMessageSource_WithExternalSource_ReturnsExternal() + public void GetAgentRequestMessageSourceType_WithExternalSourceType_ReturnsExternal() { // Arrange ChatMessage message = new(ChatRole.User, "Hello") { AdditionalProperties = new AdditionalPropertiesDictionary { - { AgentRequestMessageSourceType.AdditionalPropertiesKey, AgentRequestMessageSourceType.External } + { AgentRequestMessageSourceAttribution.AdditionalPropertiesKey, new AgentRequestMessageSourceAttribution(AgentRequestMessageSourceType.External, "TestSourceId") } } }; // Act - AgentRequestMessageSourceType result = message.GetAgentRequestMessageSource(); + AgentRequestMessageSourceType result = message.GetAgentRequestMessageSourceType(); // Assert Assert.Equal(AgentRequestMessageSourceType.External, result); } [Fact] - public void GetAgentRequestMessageSource_WithAIContextProviderSource_ReturnsAIContextProvider() + public void GetAgentRequestMessageSourceType_WithAIContextProviderSourceType_ReturnsAIContextProvider() { // Arrange ChatMessage message = new(ChatRole.User, "Hello") { AdditionalProperties = new AdditionalPropertiesDictionary { - { AgentRequestMessageSourceType.AdditionalPropertiesKey, AgentRequestMessageSourceType.AIContextProvider } + { AgentRequestMessageSourceAttribution.AdditionalPropertiesKey, new AgentRequestMessageSourceAttribution(AgentRequestMessageSourceType.AIContextProvider, "TestSourceId") } } }; // Act - AgentRequestMessageSourceType result = message.GetAgentRequestMessageSource(); + AgentRequestMessageSourceType result = message.GetAgentRequestMessageSourceType(); // Assert Assert.Equal(AgentRequestMessageSourceType.AIContextProvider, result); } [Fact] - public void GetAgentRequestMessageSource_WithChatHistorySource_ReturnsChatHistory() + public void GetAgentRequestMessageSourceType_WithChatHistorySourceType_ReturnsChatHistory() { // Arrange ChatMessage message = new(ChatRole.User, "Hello") { AdditionalProperties = new AdditionalPropertiesDictionary { - { AgentRequestMessageSourceType.AdditionalPropertiesKey, AgentRequestMessageSourceType.ChatHistory } + { AgentRequestMessageSourceAttribution.AdditionalPropertiesKey, new AgentRequestMessageSourceAttribution(AgentRequestMessageSourceType.ChatHistory, "TestSourceId") } } }; // Act - AgentRequestMessageSourceType result = message.GetAgentRequestMessageSource(); + AgentRequestMessageSourceType result = message.GetAgentRequestMessageSourceType(); // Assert Assert.Equal(AgentRequestMessageSourceType.ChatHistory, result); } [Fact] - public void GetAgentRequestMessageSource_WithCustomSource_ReturnsCustomSource() + public void GetAgentRequestMessageSourceType_WithCustomSourceType_ReturnsCustomSourceType() { // Arrange - AgentRequestMessageSourceType customSource = new("CustomSource"); + AgentRequestMessageSourceType customSourceType = new("CustomSourceType"); ChatMessage message = new(ChatRole.User, "Hello") { AdditionalProperties = new AdditionalPropertiesDictionary { - { AgentRequestMessageSourceType.AdditionalPropertiesKey, customSource } + { AgentRequestMessageSourceAttribution.AdditionalPropertiesKey, new AgentRequestMessageSourceAttribution(customSourceType, "TestSourceId") } } }; // Act - AgentRequestMessageSourceType result = message.GetAgentRequestMessageSource(); + AgentRequestMessageSourceType result = message.GetAgentRequestMessageSourceType(); // Assert - Assert.Equal(customSource, result); - Assert.Equal("CustomSource", result.Value); + Assert.Equal(customSourceType, result); + Assert.Equal("CustomSourceType", result.Value); } [Fact] - public void GetAgentRequestMessageSource_WithWrongKeyType_ReturnsExternal() + public void GetAgentRequestMessageSourceType_WithWrongAttributionType_ReturnsExternal() { // Arrange ChatMessage message = new(ChatRole.User, "Hello") { AdditionalProperties = new AdditionalPropertiesDictionary { - { AgentRequestMessageSourceType.AdditionalPropertiesKey, "NotAnAgentRequestMessageSource" } + { AgentRequestMessageSourceAttribution.AdditionalPropertiesKey, "NotAnAgentRequestMessageSourceAttribution" } } }; // Act - AgentRequestMessageSourceType result = message.GetAgentRequestMessageSource(); + AgentRequestMessageSourceType result = message.GetAgentRequestMessageSourceType(); // Assert Assert.Equal(AgentRequestMessageSourceType.External, result); } [Fact] - public void GetAgentRequestMessageSource_WithNullValue_ReturnsExternal() + public void GetAgentRequestMessageSourceType_WithNullAttributionValue_ReturnsExternal() { // Arrange ChatMessage message = new(ChatRole.User, "Hello") { AdditionalProperties = new AdditionalPropertiesDictionary { - { AgentRequestMessageSourceType.AdditionalPropertiesKey, null! } + { AgentRequestMessageSourceAttribution.AdditionalPropertiesKey, null! } } }; // Act - AgentRequestMessageSourceType result = message.GetAgentRequestMessageSource(); + AgentRequestMessageSourceType result = message.GetAgentRequestMessageSourceType(); // Assert Assert.Equal(AgentRequestMessageSourceType.External, result); } [Fact] - public void GetAgentRequestMessageSource_WithMultipleProperties_ReturnsCorrectSource() + public void GetAgentRequestMessageSourceType_WithMultipleProperties_ReturnsCorrectSourceType() { // Arrange ChatMessage message = new(ChatRole.User, "Hello") @@ -181,17 +181,345 @@ public sealed class ChatMessageExtensionsTests AdditionalProperties = new AdditionalPropertiesDictionary { { "OtherProperty", "SomeValue" }, - { AgentRequestMessageSourceType.AdditionalPropertiesKey, AgentRequestMessageSourceType.ChatHistory }, + { AgentRequestMessageSourceAttribution.AdditionalPropertiesKey, new AgentRequestMessageSourceAttribution(AgentRequestMessageSourceType.ChatHistory, "TestSourceId") }, { "AnotherProperty", 123 } } }; // Act - AgentRequestMessageSourceType result = message.GetAgentRequestMessageSource(); + AgentRequestMessageSourceType result = message.GetAgentRequestMessageSourceType(); // Assert Assert.Equal(AgentRequestMessageSourceType.ChatHistory, result); } #endregion + + #region GetAgentRequestMessageSourceId Tests + + [Fact] + public void GetAgentRequestMessageSourceId_WithNoAdditionalProperties_ReturnsNull() + { + // Arrange + ChatMessage message = new(ChatRole.User, "Hello"); + + // Act + string? result = message.GetAgentRequestMessageSourceId(); + + // Assert + Assert.Null(result); + } + + [Fact] + public void GetAgentRequestMessageSourceId_WithNullAdditionalProperties_ReturnsNull() + { + // Arrange + ChatMessage message = new(ChatRole.User, "Hello") + { + AdditionalProperties = null + }; + + // Act + string? result = message.GetAgentRequestMessageSourceId(); + + // Assert + Assert.Null(result); + } + + [Fact] + public void GetAgentRequestMessageSourceId_WithEmptyAdditionalProperties_ReturnsNull() + { + // Arrange + ChatMessage message = new(ChatRole.User, "Hello") + { + AdditionalProperties = new AdditionalPropertiesDictionary() + }; + + // Act + string? result = message.GetAgentRequestMessageSourceId(); + + // Assert + Assert.Null(result); + } + + [Fact] + public void GetAgentRequestMessageSourceId_WithAttribution_ReturnsSourceId() + { + // Arrange + ChatMessage message = new(ChatRole.User, "Hello") + { + AdditionalProperties = new AdditionalPropertiesDictionary + { + { AgentRequestMessageSourceAttribution.AdditionalPropertiesKey, new AgentRequestMessageSourceAttribution(AgentRequestMessageSourceType.AIContextProvider, "MyProvider.FullName") } + } + }; + + // Act + string? result = message.GetAgentRequestMessageSourceId(); + + // Assert + Assert.Equal("MyProvider.FullName", result); + } + + [Fact] + public void GetAgentRequestMessageSourceId_WithDifferentSourceIds_ReturnsCorrectSourceId() + { + // Arrange + ChatMessage message = new(ChatRole.User, "Hello") + { + AdditionalProperties = new AdditionalPropertiesDictionary + { + { AgentRequestMessageSourceAttribution.AdditionalPropertiesKey, new AgentRequestMessageSourceAttribution(AgentRequestMessageSourceType.ChatHistory, "CustomHistorySourceId") } + } + }; + + // Act + string? result = message.GetAgentRequestMessageSourceId(); + + // Assert + Assert.Equal("CustomHistorySourceId", result); + } + + [Fact] + public void GetAgentRequestMessageSourceId_WithWrongAttributionType_ReturnsNull() + { + // Arrange + ChatMessage message = new(ChatRole.User, "Hello") + { + AdditionalProperties = new AdditionalPropertiesDictionary + { + { AgentRequestMessageSourceAttribution.AdditionalPropertiesKey, "NotAnAgentRequestMessageSourceAttribution" } + } + }; + + // Act + string? result = message.GetAgentRequestMessageSourceId(); + + // Assert + Assert.Null(result); + } + + [Fact] + public void GetAgentRequestMessageSourceId_WithNullAttributionValue_ReturnsNull() + { + // Arrange + ChatMessage message = new(ChatRole.User, "Hello") + { + AdditionalProperties = new AdditionalPropertiesDictionary + { + { AgentRequestMessageSourceAttribution.AdditionalPropertiesKey, null! } + } + }; + + // Act + string? result = message.GetAgentRequestMessageSourceId(); + + // Assert + Assert.Null(result); + } + + [Fact] + public void GetAgentRequestMessageSourceId_WithMultipleProperties_ReturnsCorrectSourceId() + { + // Arrange + ChatMessage message = new(ChatRole.User, "Hello") + { + AdditionalProperties = new AdditionalPropertiesDictionary + { + { "OtherProperty", "SomeValue" }, + { AgentRequestMessageSourceAttribution.AdditionalPropertiesKey, new AgentRequestMessageSourceAttribution(AgentRequestMessageSourceType.External, "ExpectedSourceId") }, + { "AnotherProperty", 123 } + } + }; + + // Act + string? result = message.GetAgentRequestMessageSourceId(); + + // Assert + Assert.Equal("ExpectedSourceId", result); + } + + #endregion + + #region AsAgentRequestMessageSourcedMessage Tests + + [Fact] + public void AsAgentRequestMessageSourcedMessage_WithNoAdditionalProperties_ReturnsClonesMessageWithAttribution() + { + // Arrange + ChatMessage message = new(ChatRole.User, "Hello"); + + // Act + ChatMessage result = message.AsAgentRequestMessageSourcedMessage(AgentRequestMessageSourceType.External, "TestSourceId"); + + // Assert + Assert.NotSame(message, result); + Assert.Equal(AgentRequestMessageSourceType.External, result.GetAgentRequestMessageSourceType()); + Assert.Equal("TestSourceId", result.GetAgentRequestMessageSourceId()); + } + + [Fact] + public void AsAgentRequestMessageSourcedMessage_WithNullAdditionalProperties_ReturnsClonesMessageWithAttribution() + { + // Arrange + ChatMessage message = new(ChatRole.User, "Hello") + { + AdditionalProperties = null + }; + + // Act + ChatMessage result = message.AsAgentRequestMessageSourcedMessage(AgentRequestMessageSourceType.AIContextProvider, "ProviderSourceId"); + + // Assert + Assert.NotSame(message, result); + Assert.Equal(AgentRequestMessageSourceType.AIContextProvider, result.GetAgentRequestMessageSourceType()); + Assert.Equal("ProviderSourceId", result.GetAgentRequestMessageSourceId()); + } + + [Fact] + public void AsAgentRequestMessageSourcedMessage_WithMatchingSourceTypeAndSourceId_ReturnsSameInstance() + { + // Arrange + ChatMessage message = new(ChatRole.User, "Hello") + { + AdditionalProperties = new AdditionalPropertiesDictionary + { + { AgentRequestMessageSourceAttribution.AdditionalPropertiesKey, new AgentRequestMessageSourceAttribution(AgentRequestMessageSourceType.ChatHistory, "HistoryId") } + } + }; + + // Act + ChatMessage result = message.AsAgentRequestMessageSourcedMessage(AgentRequestMessageSourceType.ChatHistory, "HistoryId"); + + // Assert + Assert.Same(message, result); + } + + [Fact] + public void AsAgentRequestMessageSourcedMessage_WithDifferentSourceType_ReturnsClonesMessageWithNewAttribution() + { + // Arrange + ChatMessage message = new(ChatRole.User, "Hello") + { + AdditionalProperties = new AdditionalPropertiesDictionary + { + { AgentRequestMessageSourceAttribution.AdditionalPropertiesKey, new AgentRequestMessageSourceAttribution(AgentRequestMessageSourceType.External, "SourceId") } + } + }; + + // Act + ChatMessage result = message.AsAgentRequestMessageSourcedMessage(AgentRequestMessageSourceType.AIContextProvider, "SourceId"); + + // Assert + Assert.NotSame(message, result); + Assert.Equal(AgentRequestMessageSourceType.AIContextProvider, result.GetAgentRequestMessageSourceType()); + Assert.Equal("SourceId", result.GetAgentRequestMessageSourceId()); + } + + [Fact] + public void AsAgentRequestMessageSourcedMessage_WithDifferentSourceId_ReturnsClonesMessageWithNewAttribution() + { + // Arrange + ChatMessage message = new(ChatRole.User, "Hello") + { + AdditionalProperties = new AdditionalPropertiesDictionary + { + { AgentRequestMessageSourceAttribution.AdditionalPropertiesKey, new AgentRequestMessageSourceAttribution(AgentRequestMessageSourceType.External, "OriginalId") } + } + }; + + // Act + ChatMessage result = message.AsAgentRequestMessageSourcedMessage(AgentRequestMessageSourceType.External, "NewId"); + + // Assert + Assert.NotSame(message, result); + Assert.Equal(AgentRequestMessageSourceType.External, result.GetAgentRequestMessageSourceType()); + Assert.Equal("NewId", result.GetAgentRequestMessageSourceId()); + } + + [Fact] + public void AsAgentRequestMessageSourcedMessage_WithDefaultNullSourceId_ReturnsClonesMessageWithNullSourceId() + { + // Arrange + ChatMessage message = new(ChatRole.User, "Hello"); + + // Act + ChatMessage result = message.AsAgentRequestMessageSourcedMessage(AgentRequestMessageSourceType.ChatHistory); + + // Assert + Assert.NotSame(message, result); + Assert.Equal(AgentRequestMessageSourceType.ChatHistory, result.GetAgentRequestMessageSourceType()); + Assert.Null(result.GetAgentRequestMessageSourceId()); + } + + [Fact] + public void AsAgentRequestMessageSourcedMessage_WithMatchingSourceTypeAndNullSourceId_ReturnsSameInstance() + { + // Arrange + ChatMessage message = new(ChatRole.User, "Hello") + { + AdditionalProperties = new AdditionalPropertiesDictionary + { + { AgentRequestMessageSourceAttribution.AdditionalPropertiesKey, new AgentRequestMessageSourceAttribution(AgentRequestMessageSourceType.External, null) } + } + }; + + // Act + ChatMessage result = message.AsAgentRequestMessageSourcedMessage(AgentRequestMessageSourceType.External); + + // Assert + Assert.Same(message, result); + } + + [Fact] + public void AsAgentRequestMessageSourcedMessage_DoesNotModifyOriginalMessage() + { + // Arrange + ChatMessage message = new(ChatRole.User, "Hello"); + + // Act + ChatMessage result = message.AsAgentRequestMessageSourcedMessage(AgentRequestMessageSourceType.AIContextProvider, "ProviderId"); + + // Assert + Assert.Null(message.AdditionalProperties); + Assert.NotNull(result.AdditionalProperties); + Assert.Equal(AgentRequestMessageSourceType.AIContextProvider, result.GetAgentRequestMessageSourceType()); + } + + [Fact] + public void AsAgentRequestMessageSourcedMessage_WithWrongAttributionType_ReturnsClonesMessageWithNewAttribution() + { + // Arrange + ChatMessage message = new(ChatRole.User, "Hello") + { + AdditionalProperties = new AdditionalPropertiesDictionary + { + { AgentRequestMessageSourceAttribution.AdditionalPropertiesKey, "NotAnAttribution" } + } + }; + + // Act + ChatMessage result = message.AsAgentRequestMessageSourcedMessage(AgentRequestMessageSourceType.External, "SourceId"); + + // Assert + Assert.NotSame(message, result); + Assert.Equal(AgentRequestMessageSourceType.External, result.GetAgentRequestMessageSourceType()); + Assert.Equal("SourceId", result.GetAgentRequestMessageSourceId()); + } + + [Fact] + public void AsAgentRequestMessageSourcedMessage_PreservesMessageContent() + { + // Arrange + ChatMessage message = new(ChatRole.Assistant, "Test content"); + + // Act + ChatMessage result = message.AsAgentRequestMessageSourcedMessage(AgentRequestMessageSourceType.ChatHistory, "HistoryId"); + + // Assert + Assert.Equal(ChatRole.Assistant, result.Role); + Assert.Equal("Test content", result.Text); + } + + #endregion } diff --git a/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/InMemoryChatHistoryProviderTests.cs b/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/InMemoryChatHistoryProviderTests.cs index 75232073a6..cecce5bea1 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/InMemoryChatHistoryProviderTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/InMemoryChatHistoryProviderTests.cs @@ -55,7 +55,7 @@ public class InMemoryChatHistoryProviderTests var requestMessages = new List { new(ChatRole.User, "Hello"), - new(ChatRole.System, "additional context") { AdditionalProperties = new() { { AgentRequestMessageSourceType.AdditionalPropertiesKey, AgentRequestMessageSourceType.ChatHistory } } }, + new(ChatRole.System, "additional context") { AdditionalProperties = new() { { AgentRequestMessageSourceAttribution.AdditionalPropertiesKey, new AgentRequestMessageSourceAttribution(AgentRequestMessageSourceType.ChatHistory, "TestSource") } } }, }; var responseMessages = 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 e1d3c612c8..e7276f70b1 100644 --- a/dotnet/tests/Microsoft.Agents.AI.CosmosNoSql.UnitTests/CosmosChatHistoryProviderTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.CosmosNoSql.UnitTests/CosmosChatHistoryProviderTests.cs @@ -286,7 +286,7 @@ public sealed class CosmosChatHistoryProviderTests : IAsyncLifetime, IDisposable new ChatMessage(ChatRole.User, "First message"), new ChatMessage(ChatRole.Assistant, "Second message"), new ChatMessage(ChatRole.User, "Third message"), - new ChatMessage(ChatRole.System, "System context message") { AdditionalProperties = new() { { AgentRequestMessageSourceType.AdditionalPropertiesKey, AgentRequestMessageSourceType.AIContextProvider } } } + new ChatMessage(ChatRole.System, "System context message") { AdditionalProperties = new() { { AgentRequestMessageSourceAttribution.AdditionalPropertiesKey, new AgentRequestMessageSourceAttribution(AgentRequestMessageSourceType.AIContextProvider, "TestSource") } } } }; var responseMessages = new[] {