From 19df4da111c00caab4915cb9d7a172b05da64bd6 Mon Sep 17 00:00:00 2001 From: xbotter Date: Sun, 12 Nov 2023 22:04:00 +0800 Subject: [PATCH] format code and add unit tests --- BotSharp.sln | 13 +++- .../BotSharp.Plugin.SemanticKernel.csproj | 5 ++ .../SemanticKernelChatCompletionProvider.cs | 15 ++-- .../SemanticKernelPlugin.cs | 4 +- .../SemanticKernelTextCompletionProvider.cs | 18 ++--- ...arp.Plugin.SemanticKernel.UnitTests.csproj | 31 +++++++++ .../Helpers/MockChatResult.cs | 29 ++++++++ ...manticKernelChatCompletionProviderTests.cs | 68 +++++++++++++++++++ .../SemanticKernelPluginTests.cs | 24 +++++++ ...manticKernelTextCompletionProviderTests.cs | 57 ++++++++++++++++ .../Usings.cs | 1 + 11 files changed, 244 insertions(+), 21 deletions(-) create mode 100644 tests/BotSharp.Plugin.SemanticKernel.UnitTests/BotSharp.Plugin.SemanticKernel.UnitTests.csproj create mode 100644 tests/BotSharp.Plugin.SemanticKernel.UnitTests/Helpers/MockChatResult.cs create mode 100644 tests/BotSharp.Plugin.SemanticKernel.UnitTests/SemanticKernelChatCompletionProviderTests.cs create mode 100644 tests/BotSharp.Plugin.SemanticKernel.UnitTests/SemanticKernelPluginTests.cs create mode 100644 tests/BotSharp.Plugin.SemanticKernel.UnitTests/SemanticKernelTextCompletionProviderTests.cs create mode 100644 tests/BotSharp.Plugin.SemanticKernel.UnitTests/Usings.cs diff --git a/BotSharp.sln b/BotSharp.sln index 768ae6ee..0ca78843 100644 --- a/BotSharp.sln +++ b/BotSharp.sln @@ -63,7 +63,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BotSharp.Plugin.MongoStorag EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BotSharp.Plugin.GoogleAI", "src\Plugins\BotSharp.Plugin.GoogleAI\BotSharp.Plugin.GoogleAI.csproj", "{8BC29F8A-78D6-422C-B522-10687ADC38ED}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BotSharp.Plugin.SemanticKernel", "src\Plugins\BotSharp.Plugin.SemanticKernel\BotSharp.Plugin.SemanticKernel.csproj", "{73EE2CD0-3B27-4F02-A67B-762CBDD740D0}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BotSharp.Plugin.SemanticKernel", "src\Plugins\BotSharp.Plugin.SemanticKernel\BotSharp.Plugin.SemanticKernel.csproj", "{73EE2CD0-3B27-4F02-A67B-762CBDD740D0}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BotSharp.Plugin.SemanticKernel.UnitTests", "tests\BotSharp.Plugin.SemanticKernel.UnitTests\BotSharp.Plugin.SemanticKernel.UnitTests.csproj", "{BC57D428-A1A4-4D38-A2D0-AC6CA943F247}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -233,6 +235,14 @@ Global {73EE2CD0-3B27-4F02-A67B-762CBDD740D0}.Release|Any CPU.Build.0 = Release|Any CPU {73EE2CD0-3B27-4F02-A67B-762CBDD740D0}.Release|x64.ActiveCfg = Release|Any CPU {73EE2CD0-3B27-4F02-A67B-762CBDD740D0}.Release|x64.Build.0 = Release|Any CPU + {BC57D428-A1A4-4D38-A2D0-AC6CA943F247}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BC57D428-A1A4-4D38-A2D0-AC6CA943F247}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BC57D428-A1A4-4D38-A2D0-AC6CA943F247}.Debug|x64.ActiveCfg = Debug|Any CPU + {BC57D428-A1A4-4D38-A2D0-AC6CA943F247}.Debug|x64.Build.0 = Debug|Any CPU + {BC57D428-A1A4-4D38-A2D0-AC6CA943F247}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BC57D428-A1A4-4D38-A2D0-AC6CA943F247}.Release|Any CPU.Build.0 = Release|Any CPU + {BC57D428-A1A4-4D38-A2D0-AC6CA943F247}.Release|x64.ActiveCfg = Release|Any CPU + {BC57D428-A1A4-4D38-A2D0-AC6CA943F247}.Release|x64.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -265,6 +275,7 @@ Global {DB3DE37B-1208-4ED3-9615-A52AD0AAD69C} = {5CD330E1-9E5A-4112-8346-6E31CA98EF78} {8BC29F8A-78D6-422C-B522-10687ADC38ED} = {D5293208-2BEF-42FC-A64C-5954F61720BA} {73EE2CD0-3B27-4F02-A67B-762CBDD740D0} = {D5293208-2BEF-42FC-A64C-5954F61720BA} + {BC57D428-A1A4-4D38-A2D0-AC6CA943F247} = {32FAFFFE-A4CB-4FEE-BF7C-84518BBC6DCC} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {A9969D89-C98B-40A5-A12B-FC87E55B3A19} diff --git a/src/Plugins/BotSharp.Plugin.SemanticKernel/BotSharp.Plugin.SemanticKernel.csproj b/src/Plugins/BotSharp.Plugin.SemanticKernel/BotSharp.Plugin.SemanticKernel.csproj index 58a950cb..9cbe6db9 100644 --- a/src/Plugins/BotSharp.Plugin.SemanticKernel/BotSharp.Plugin.SemanticKernel.csproj +++ b/src/Plugins/BotSharp.Plugin.SemanticKernel/BotSharp.Plugin.SemanticKernel.csproj @@ -7,10 +7,15 @@ + + + + + diff --git a/src/Plugins/BotSharp.Plugin.SemanticKernel/SemanticKernelChatCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.SemanticKernel/SemanticKernelChatCompletionProvider.cs index 19cce5ed..c6f9e5e9 100644 --- a/src/Plugins/BotSharp.Plugin.SemanticKernel/SemanticKernelChatCompletionProvider.cs +++ b/src/Plugins/BotSharp.Plugin.SemanticKernel/SemanticKernelChatCompletionProvider.cs @@ -1,17 +1,15 @@ -using BotSharp.Abstraction.Agents.Enums; using BotSharp.Abstraction.Agents; +using BotSharp.Abstraction.Agents.Enums; using BotSharp.Abstraction.Agents.Models; +using BotSharp.Abstraction.Conversations; using BotSharp.Abstraction.Conversations.Models; using BotSharp.Abstraction.MLTasks; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.SemanticKernel; using System; using System.Collections.Generic; using System.Linq; -using System.Runtime; using System.Threading.Tasks; -using BotSharp.Abstraction.Conversations; -using Microsoft.SemanticKernel; -using Microsoft.Extensions.DependencyInjection; -using BotSharp.Abstraction.Models; namespace BotSharp.Plugin.SemanticKernel { @@ -33,7 +31,6 @@ namespace BotSharp.Plugin.SemanticKernel this._tokenStatistics = tokenStatistics; } - public RoleDialogModel GetChatCompletions(Agent agent, List conversations) { var hooks = _services.GetServices().ToList(); @@ -61,7 +58,6 @@ namespace BotSharp.Plugin.SemanticKernel } } - var response = completion.GetChatCompletionsAsync(chatHistory) .ContinueWith(async t => { @@ -71,7 +67,6 @@ namespace BotSharp.Plugin.SemanticKernel }).ConfigureAwait(false).GetAwaiter().GetResult() .ConfigureAwait(false).GetAwaiter().GetResult(); - var msg = new RoleDialogModel(AgentRole.Assistant, response) { CurrentAgentId = agent.Id @@ -102,4 +97,4 @@ namespace BotSharp.Plugin.SemanticKernel this._model = model; } } -} +} \ No newline at end of file diff --git a/src/Plugins/BotSharp.Plugin.SemanticKernel/SemanticKernelPlugin.cs b/src/Plugins/BotSharp.Plugin.SemanticKernel/SemanticKernelPlugin.cs index 93d59982..1494bf56 100644 --- a/src/Plugins/BotSharp.Plugin.SemanticKernel/SemanticKernelPlugin.cs +++ b/src/Plugins/BotSharp.Plugin.SemanticKernel/SemanticKernelPlugin.cs @@ -2,7 +2,6 @@ using BotSharp.Abstraction.MLTasks; using BotSharp.Abstraction.Plugins; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -using System.Text; namespace BotSharp.Plugin.SemanticKernel { @@ -10,10 +9,11 @@ namespace BotSharp.Plugin.SemanticKernel { public string Name => "Semantic Kernel"; public string Description => "Semantic Kernel Service"; + public void RegisterDI(IServiceCollection services, IConfiguration config) { services.AddScoped(); services.AddScoped(); } } -} +} \ No newline at end of file diff --git a/src/Plugins/BotSharp.Plugin.SemanticKernel/SemanticKernelTextCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.SemanticKernel/SemanticKernelTextCompletionProvider.cs index e4d5cfb3..d0a23ed7 100644 --- a/src/Plugins/BotSharp.Plugin.SemanticKernel/SemanticKernelTextCompletionProvider.cs +++ b/src/Plugins/BotSharp.Plugin.SemanticKernel/SemanticKernelTextCompletionProvider.cs @@ -1,15 +1,16 @@ using BotSharp.Abstraction.Agents.Enums; using BotSharp.Abstraction.Agents.Models; -using BotSharp.Abstraction.Conversations.Models; using BotSharp.Abstraction.Conversations; +using BotSharp.Abstraction.Conversations.Models; using BotSharp.Abstraction.MLTasks; -using Microsoft.SemanticKernel; -using System; -using System.Threading.Tasks; +using Microsoft; using Microsoft.Extensions.DependencyInjection; -using System.Linq; -using System.Collections.Generic; +using Microsoft.SemanticKernel; using Microsoft.SemanticKernel.AI.TextCompletion; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; namespace BotSharp.Plugin.SemanticKernel { @@ -26,12 +27,13 @@ namespace BotSharp.Plugin.SemanticKernel IServiceProvider services, ITokenStatistics tokenStatistics) { + Requires.NotNull(kernel, nameof(kernel)); + this._kernel = kernel; this._services = services; this._tokenStatistics = tokenStatistics; } - public async Task GetCompletion(string text, string agentId, string messageId) { var hooks = _services.GetServices().ToList(); @@ -68,4 +70,4 @@ namespace BotSharp.Plugin.SemanticKernel this._model = model; } } -} +} \ No newline at end of file diff --git a/tests/BotSharp.Plugin.SemanticKernel.UnitTests/BotSharp.Plugin.SemanticKernel.UnitTests.csproj b/tests/BotSharp.Plugin.SemanticKernel.UnitTests/BotSharp.Plugin.SemanticKernel.UnitTests.csproj new file mode 100644 index 00000000..aa984d28 --- /dev/null +++ b/tests/BotSharp.Plugin.SemanticKernel.UnitTests/BotSharp.Plugin.SemanticKernel.UnitTests.csproj @@ -0,0 +1,31 @@ + + + + net6.0 + enable + enable + + false + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + diff --git a/tests/BotSharp.Plugin.SemanticKernel.UnitTests/Helpers/MockChatResult.cs b/tests/BotSharp.Plugin.SemanticKernel.UnitTests/Helpers/MockChatResult.cs new file mode 100644 index 00000000..c34d65ba --- /dev/null +++ b/tests/BotSharp.Plugin.SemanticKernel.UnitTests/Helpers/MockChatResult.cs @@ -0,0 +1,29 @@ +using Microsoft.SemanticKernel.AI.ChatCompletion; +using Microsoft.SemanticKernel.Orchestration; + +namespace BotSharp.Plugin.SemanticKernel.UnitTests.Helpers +{ + public class MockChatResult : IChatResult + { + public ModelResult ModelResult { get; set; } + private string _response; + + public MockChatResult(string response) + { + ModelResult = new ModelResult(response); + _response = response; + } + + public async Task GetChatMessageAsync(CancellationToken cancellationToken = default) + { + return await Task.FromResult(new MockModelResult(_response)); + } + + public class MockModelResult : ChatMessageBase + { + public MockModelResult(string content) : base(AuthorRole.Assistant, content, null) + { + } + } + } +} \ No newline at end of file diff --git a/tests/BotSharp.Plugin.SemanticKernel.UnitTests/SemanticKernelChatCompletionProviderTests.cs b/tests/BotSharp.Plugin.SemanticKernel.UnitTests/SemanticKernelChatCompletionProviderTests.cs new file mode 100644 index 00000000..62f19ae2 --- /dev/null +++ b/tests/BotSharp.Plugin.SemanticKernel.UnitTests/SemanticKernelChatCompletionProviderTests.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using BotSharp.Abstraction.Agents; +using BotSharp.Abstraction.Conversations.Models; +using BotSharp.Abstraction.MLTasks; +using BotSharp.Plugin.SemanticKernel; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.SemanticKernel; +using Moq; +using Xunit; +using BotSharp.Abstraction.Agents.Enums; +using BotSharp.Abstraction.Agents.Models; +using System.Linq; +using System.Runtime; +using BotSharp.Abstraction.Conversations; +using BotSharp.Abstraction.Models; +using Microsoft.SemanticKernel.AI.ChatCompletion; +using Microsoft.SemanticKernel.AI; +using Microsoft.SemanticKernel.Connectors.AI.OpenAI.AzureSdk; + +namespace BotSharp.Plugin.SemanticKernel.Tests +{ + public class SemanticKernelChatCompletionProviderTests + { + private readonly Mock _kernelMock; + private readonly Mock _servicesMock; + private readonly Mock _tokenStatisticsMock; + private readonly SemanticKernelChatCompletionProvider _provider; + + public SemanticKernelChatCompletionProviderTests() + { + _kernelMock = new Mock(); + _servicesMock = new Mock(); + _tokenStatisticsMock = new Mock(); + _provider = new SemanticKernelChatCompletionProvider(_kernelMock.Object, _servicesMock.Object, _tokenStatisticsMock.Object); + } + + [Fact] + public void GetChatCompletions_Returns_RoleDialogModel() + { + // Arrange + var agent = new Agent(); + var conversations = new List + { + new RoleDialogModel(AgentRole.User, "Hello") + }; + + var chatHistoryMock = new Mock(); + var chatCompletionMock = new Mock(); + chatCompletionMock.Setup(x => x.CreateNewChat(It.IsAny())).Returns(chatHistoryMock.Object); + chatCompletionMock.Setup(x => x.GetChatCompletionsAsync(chatHistoryMock.Object, It.IsAny(), It.IsAny())) + .ReturnsAsync(new List + { + new MockChatResult("How can I help you?") + }); + + _kernelMock.Setup(x => x.GetService(null)).Returns(chatCompletionMock.Object); + + // Act + var result = _provider.GetChatCompletions(agent, conversations); + + // Assert + Assert.IsType(result); + } + } + +} diff --git a/tests/BotSharp.Plugin.SemanticKernel.UnitTests/SemanticKernelPluginTests.cs b/tests/BotSharp.Plugin.SemanticKernel.UnitTests/SemanticKernelPluginTests.cs new file mode 100644 index 00000000..8110c4a1 --- /dev/null +++ b/tests/BotSharp.Plugin.SemanticKernel.UnitTests/SemanticKernelPluginTests.cs @@ -0,0 +1,24 @@ +using BotSharp.Abstraction.MLTasks; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; + +namespace BotSharp.Plugin.SemanticKernel.Tests +{ + public class SemanticKernelPluginTests + { + [Fact] + public void TestRegisterDI() + { + var services = new ServiceCollection(); + var config = new ConfigurationBuilder().Build(); + var plugin = new SemanticKernelPlugin(); + + plugin.RegisterDI(services, config); + + var provider = services.BuildServiceProvider(); + + Assert.NotNull(provider.GetService()); + Assert.NotNull(provider.GetService()); + } + } +} \ No newline at end of file diff --git a/tests/BotSharp.Plugin.SemanticKernel.UnitTests/SemanticKernelTextCompletionProviderTests.cs b/tests/BotSharp.Plugin.SemanticKernel.UnitTests/SemanticKernelTextCompletionProviderTests.cs new file mode 100644 index 00000000..5bb0e57e --- /dev/null +++ b/tests/BotSharp.Plugin.SemanticKernel.UnitTests/SemanticKernelTextCompletionProviderTests.cs @@ -0,0 +1,57 @@ +using BotSharp.Abstraction.Conversations.Models; +using BotSharp.Abstraction.MLTasks; +using BotSharp.Plugin.SemanticKernel; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.SemanticKernel; +using Microsoft.SemanticKernel.AI.TextCompletion; +using Moq; +using System.Collections.Generic; +using System.Threading.Tasks; +using Xunit; +using BotSharp.Abstraction.Agents.Enums; +using BotSharp.Abstraction.Agents.Models; +using BotSharp.Abstraction.Conversations; +using System; +using System.Linq; +using Microsoft; +using Microsoft.SemanticKernel.AI; + +namespace BotSharp.Plugin.SemanticKernel.Tests +{ + public class SemanticKernelTextCompletionProviderTests + { + private readonly Mock _kernel; + private readonly IServiceProvider _services; + private readonly ITokenStatistics _tokenStatistics; + + public SemanticKernelTextCompletionProviderTests() + { + _kernel = new Mock(); + _services = new ServiceCollection().BuildServiceProvider(); + _tokenStatistics = Mock.Of(); + } + + [Fact] + public async Task GetCompletion_ReturnsExpectedResult() + { + // Arrange + var provider = new SemanticKernelTextCompletionProvider(_kernel.Object, _services, _tokenStatistics); + var text = "Hello"; + var agentId = "agent1"; + var messageId = "message1"; + var expected = "Hello, world!"; + + var mockCompletion = new Mock(); + mockCompletion.Setup(c => c.CompleteAsync(text, It.IsAny(), It.IsAny())).ReturnsAsync(expected); + _kernel.Setup(c => c.GetService(It.IsAny())).Returns(mockCompletion.Object); + + // Act + var result = await provider.GetCompletion(text, agentId, messageId); + + // Assert + Assert.Equal(expected, result); + mockCompletion.Verify(c => c.CompleteAsync(text, null, default(CancellationToken)), Times.Once); + + } + } +} diff --git a/tests/BotSharp.Plugin.SemanticKernel.UnitTests/Usings.cs b/tests/BotSharp.Plugin.SemanticKernel.UnitTests/Usings.cs new file mode 100644 index 00000000..8c927eb7 --- /dev/null +++ b/tests/BotSharp.Plugin.SemanticKernel.UnitTests/Usings.cs @@ -0,0 +1 @@ +global using Xunit; \ No newline at end of file