diff --git a/src/SemanticAssertions.IntegrationTests/Asserts/Shared/AssertNotTestBase.cs b/src/SemanticAssertions.IntegrationTests/Asserts/Shared/AssertNotTestBase.cs index 3c0a77b..b1a9fee 100644 --- a/src/SemanticAssertions.IntegrationTests/Asserts/Shared/AssertNotTestBase.cs +++ b/src/SemanticAssertions.IntegrationTests/Asserts/Shared/AssertNotTestBase.cs @@ -90,4 +90,28 @@ public async Task not_throw_exception_when_texts_are_in_different_languages() Assert.Null(exception); } + + [Theory] + [InlineData("El Teide es el más alto de España")] + [InlineData("El Teide, que se encuentra en la isla de Tenerife en España, tiene una altura de aproximadamente 3718 metros sobre el nivel del mar. Es el pico más alto de España y uno de los volcanes más altos del mundo si se mide desde su base en el lecho oceánico.")] + public async Task throw_exception_when_text_contains_subset_of_information_included_in_another_text(string actual) + { + var exception = await Record.ExceptionAsync(() => Async.Assert.Not.ContainsInformationSubset( + "El Teide, que se encuentra en la isla de Tenerife en España, tiene una altura de aproximadamente 3718 metros sobre el nivel del mar. Es el pico más alto de España y uno de los volcanes más altos del mundo si se mide desde su base en el lecho oceánico.", + actual)); + + Assert.IsType(exception); + } + + [Theory] + [InlineData("El Teide, que se encuentra en la isla de Tenerife en España, tiene una altura de aproximadamente 3718 metros sobre el nivel del mar. Es el pico más alto de España y uno de los volcanes más altos del mundo si se mide desde su base en el lecho oceánico.")] + [InlineData("Nueva York está en USA")] + public async Task not_throw_exception_when_text_not_contains_subset_of_information_included_in_another_text(string actual) + { + var exception = await Record.ExceptionAsync(() => Async.Assert.Not.ContainsInformationSubset( + "El Teide es el más alto de España", + actual)); + + Assert.Null(exception); + } } \ No newline at end of file diff --git a/src/SemanticAssertions.IntegrationTests/Asserts/Shared/AssertTestBase.cs b/src/SemanticAssertions.IntegrationTests/Asserts/Shared/AssertTestBase.cs index 2b06804..54eb7f8 100644 --- a/src/SemanticAssertions.IntegrationTests/Asserts/Shared/AssertTestBase.cs +++ b/src/SemanticAssertions.IntegrationTests/Asserts/Shared/AssertTestBase.cs @@ -90,4 +90,28 @@ public async Task throw_exception_when_texts_are_in_different_languages() Assert.IsType(exception); } + + [Theory] + [InlineData("El Teide es el más alto de España")] + [InlineData("El Teide, que se encuentra en la isla de Tenerife en España, tiene una altura de aproximadamente 3718 metros sobre el nivel del mar. Es el pico más alto de España y uno de los volcanes más altos del mundo si se mide desde su base en el lecho oceánico.")] + public async Task not_throw_exception_when_text_contains_subset_of_information_included_in_another_text(string actual) + { + var exception = await Record.ExceptionAsync(() => Async.Assert.ContainsInformationSubset( + "El Teide, que se encuentra en la isla de Tenerife en España, tiene una altura de aproximadamente 3718 metros sobre el nivel del mar. Es el pico más alto de España y uno de los volcanes más altos del mundo si se mide desde su base en el lecho oceánico.", + actual)); + + Assert.Null(exception); + } + + [Theory] + [InlineData("El Teide, que se encuentra en la isla de Tenerife en España, tiene una altura de aproximadamente 3718 metros sobre el nivel del mar. Es el pico más alto de España y uno de los volcanes más altos del mundo si se mide desde su base en el lecho oceánico.")] + [InlineData("Nueva York está en USA")] + public async Task throw_exception_when_text_not_contains_subset_of_information_included_in_another_text(string actual) + { + var exception = await Record.ExceptionAsync(() => Async.Assert.ContainsInformationSubset( + "El Teide es el más alto de España", + actual)); + + Assert.IsType(exception); + } } \ No newline at end of file diff --git a/src/SemanticAssertions/Abstractions/IAssertHandler.cs b/src/SemanticAssertions/Abstractions/IAssertHandler.cs index 0022a93..10dc464 100644 --- a/src/SemanticAssertions/Abstractions/IAssertHandler.cs +++ b/src/SemanticAssertions/Abstractions/IAssertHandler.cs @@ -6,5 +6,7 @@ public interface IAssertHandler Task CalculateSimilarityAsync(string expected, string actual); + Task ContainsInformationSubsetAsync(string expected, string actual); + Task AreInSameLanguage(string expected, string actual); } \ No newline at end of file diff --git a/src/SemanticAssertions/Async/Assert.Not.cs b/src/SemanticAssertions/Async/Assert.Not.cs index c517286..1d6f296 100644 --- a/src/SemanticAssertions/Async/Assert.Not.cs +++ b/src/SemanticAssertions/Async/Assert.Not.cs @@ -38,6 +38,22 @@ public static async Task AreSimilar(string expected, string actual, double simil throw new SemanticAssertionsException($"Strings are similar. Max similarity: {similarityThreshold}."); } + +#pragma warning disable S3218 + public static async Task ContainsInformationSubset(string expected, string actual) +#pragma warning restore S3218 + { + try + { + await Assert.ContainsInformationSubset(expected, actual); + } + catch (SemanticAssertionsException) + { + return; + } + + throw new SemanticAssertionsException($"The string {nameof(actual)} does contain information included in the string {nameof(expected)}"); + } #pragma warning disable S3218 public static async Task AreInSameLanguage(string expected, string actual) diff --git a/src/SemanticAssertions/Async/Assert.cs b/src/SemanticAssertions/Async/Assert.cs index 537b400..49afdd2 100644 --- a/src/SemanticAssertions/Async/Assert.cs +++ b/src/SemanticAssertions/Async/Assert.cs @@ -48,6 +48,25 @@ public static async Task AreSimilar(string expected, string actual, double simil throw new SemanticAssertionsException($"Strings are not similar. Expected similarity: {similarityThreshold}. Actual similarity: {result}"); } + + public static async Task ContainsInformationSubset(string expected, string actual) + { + if (string.IsNullOrEmpty(expected) && string.IsNullOrEmpty(actual)) + { + return; + } + + var result = await AssertHandler.ContainsInformationSubsetAsync(expected, actual).ConfigureAwait(false); + + var containsInformation = await ParserProvider.ParseBoolAsync(result).ConfigureAwait(false); + + if (containsInformation) + { + return; + } + + throw new SemanticAssertionsException($"The string {nameof(actual)} does not contain information included in the string {nameof(expected)}"); + } public static async Task AreInSameLanguage(string expected, string actual) { diff --git a/src/SemanticAssertions/Internals/SemanticKernel/Plugins/InformationPlugin/ContainsInformationSubset/config.json b/src/SemanticAssertions/Internals/SemanticKernel/Plugins/InformationPlugin/ContainsInformationSubset/config.json new file mode 100644 index 0000000..fb3b073 --- /dev/null +++ b/src/SemanticAssertions/Internals/SemanticKernel/Plugins/InformationPlugin/ContainsInformationSubset/config.json @@ -0,0 +1,26 @@ +{ + "schema": 1, + "type": "completion", + "description": "Compare subset text", + "completion": { + "max_tokens": 10, + "temperature": 0.0, + "top_p": 0.0, + "presence_penalty": 0.0, + "frequency_penalty": 0.0 + }, + "input": { + "parameters": [ + { + "name": "actual", + "description": "The value to be compared against", + "defaultValue": "" + }, + { + "name": "expected", + "description": "The expected value", + "defaultValue": "" + } + ] + } +} \ No newline at end of file diff --git a/src/SemanticAssertions/Internals/SemanticKernel/Plugins/InformationPlugin/ContainsInformationSubset/skprompt.txt b/src/SemanticAssertions/Internals/SemanticKernel/Plugins/InformationPlugin/ContainsInformationSubset/skprompt.txt new file mode 100644 index 0000000..7fa63c0 --- /dev/null +++ b/src/SemanticAssertions/Internals/SemanticKernel/Plugins/InformationPlugin/ContainsInformationSubset/skprompt.txt @@ -0,0 +1,13 @@ +Your task is to check if the [SUBSET] is a subset of the information provided by [TEXT] text. +If affirmative, return TRUE; if negative, return FALSE. +Analyze the [SUBSET] and [TEXT] and determine if [SUBSET] exclusively contains information included in [TEXT]. +Remember, if [SUBSET] contains more information than [TEXT], return false. +Don't provide any explanation, just return TRUE or FALSE. + +[TEXT] +{{$expected}} +[END TEXT] + +[SUBSET] +{{$actual}} +[END SUBSET] diff --git a/src/SemanticAssertions/Internals/SemanticKernel/Plugins/InformationPlugin/InformationPluginInfo.cs b/src/SemanticAssertions/Internals/SemanticKernel/Plugins/InformationPlugin/InformationPluginInfo.cs new file mode 100644 index 0000000..a7ff82a --- /dev/null +++ b/src/SemanticAssertions/Internals/SemanticKernel/Plugins/InformationPlugin/InformationPluginInfo.cs @@ -0,0 +1,14 @@ +// ReSharper disable MemberHidesStaticFromOuterClass +namespace SemanticAssertions.Internals.SemanticKernel.Plugins.InformationPlugin; + +internal static class InformationPluginInfo +{ + public const string Name = "InformationPlugin"; + + public static class ContainsInformationSubset + { +#pragma warning disable S3218 + public const string Name = nameof(ContainsInformationSubset); +#pragma warning restore S3218 + } +} \ No newline at end of file diff --git a/src/SemanticAssertions/Internals/SemanticKernel/SKAssertHandler.cs b/src/SemanticAssertions/Internals/SemanticKernel/SKAssertHandler.cs index c585bd4..277ecfa 100644 --- a/src/SemanticAssertions/Internals/SemanticKernel/SKAssertHandler.cs +++ b/src/SemanticAssertions/Internals/SemanticKernel/SKAssertHandler.cs @@ -27,7 +27,7 @@ public async Task AreSimilar(string expected, string actual) var result = await RunAsync(kernel, variables, areSimilarFunction).ConfigureAwait(false); - return result; + return result; } public virtual async Task CalculateSimilarityAsync(string expected, string actual) @@ -48,6 +48,24 @@ public virtual async Task CalculateSimilarityAsync(string expected, stri return result; } + public async Task ContainsInformationSubsetAsync(string expected, string actual) + { + var kernel = BuildKernel(); + + var containsInformationSubsetFunction = kernel.Functions.GetFunction( + Plugins.InformationPlugin.InformationPluginInfo.Name, + Plugins.InformationPlugin.InformationPluginInfo.ContainsInformationSubset.Name); + var variables = new ContextVariables + { + [Plugins.PluginsInfo.Parameters.Expected] = expected, + [Plugins.PluginsInfo.Parameters.Actual] = actual + }; + + var result = await RunAsync(kernel, variables, containsInformationSubsetFunction).ConfigureAwait(false); + + return result; + } + public async Task AreInSameLanguage(string expected, string actual) { var kernel = BuildKernel(); @@ -86,7 +104,8 @@ protected static IKernel BuildKernel() kernel.ImportSemanticFunctionsFromDirectory(Plugins.PluginsInfo.Directory, Plugins.SimilarityPlugin.SimilarityPluginInfo.Name, - Plugins.LanguagePlugin.LanguagePluginInfo.Name); + Plugins.LanguagePlugin.LanguagePluginInfo.Name, + Plugins.InformationPlugin.InformationPluginInfo.Name); return kernel; } diff --git a/src/SemanticAssertions/SemanticAssertions.csproj b/src/SemanticAssertions/SemanticAssertions.csproj index 26bc90d..7c767c2 100644 --- a/src/SemanticAssertions/SemanticAssertions.csproj +++ b/src/SemanticAssertions/SemanticAssertions.csproj @@ -39,6 +39,24 @@ Always + + Always + + + Always + + + Always + + + Always + + + Always + + + Always +