diff --git a/src/Plugins/BotSharp.Plugin.SemanticKernel/BotSharp.Plugin.SemanticKernel.csproj b/src/Plugins/BotSharp.Plugin.SemanticKernel/BotSharp.Plugin.SemanticKernel.csproj index 9cbe6db9..9ff34130 100644 --- a/src/Plugins/BotSharp.Plugin.SemanticKernel/BotSharp.Plugin.SemanticKernel.csproj +++ b/src/Plugins/BotSharp.Plugin.SemanticKernel/BotSharp.Plugin.SemanticKernel.csproj @@ -3,10 +3,14 @@ netstandard2.1 enable + $(LangVersion) + $(BotSharpVersion) + $(GeneratePackageOnBuild) + True - + @@ -15,7 +19,7 @@ - + diff --git a/src/Plugins/BotSharp.Plugin.SemanticKernel/SemanticKernelChatCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.SemanticKernel/SemanticKernelChatCompletionProvider.cs index c6f9e5e9..1d65ee45 100644 --- a/src/Plugins/BotSharp.Plugin.SemanticKernel/SemanticKernelChatCompletionProvider.cs +++ b/src/Plugins/BotSharp.Plugin.SemanticKernel/SemanticKernelChatCompletionProvider.cs @@ -20,7 +20,7 @@ namespace BotSharp.Plugin.SemanticKernel private ITokenStatistics _tokenStatistics; private string? _model = null; - public string Provider => throw new NotImplementedException(); + public string Provider => "semantic-kernel"; public SemanticKernelChatCompletionProvider(IKernel kernel, IServiceProvider services, @@ -92,9 +92,11 @@ namespace BotSharp.Plugin.SemanticKernel throw new NotImplementedException(); } + public void SetModelName(string model) { - this._model = model; + if (!string.IsNullOrWhiteSpace(model)) + this._model = model; } } } \ 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 d0a23ed7..07b6749a 100644 --- a/src/Plugins/BotSharp.Plugin.SemanticKernel/SemanticKernelTextCompletionProvider.cs +++ b/src/Plugins/BotSharp.Plugin.SemanticKernel/SemanticKernelTextCompletionProvider.cs @@ -14,6 +14,9 @@ using System.Threading.Tasks; namespace BotSharp.Plugin.SemanticKernel { + /// + /// User Semantic Kernel as text completion provider + /// public class SemanticKernelTextCompletionProvider : Abstraction.MLTasks.ITextCompletion { private readonly IKernel _kernel; @@ -21,19 +24,21 @@ namespace BotSharp.Plugin.SemanticKernel private readonly ITokenStatistics _tokenStatistics; private string? _model = null; + // public string Provider => "semantic-kernel"; public SemanticKernelTextCompletionProvider(IKernel kernel, IServiceProvider services, ITokenStatistics tokenStatistics) { - Requires.NotNull(kernel, nameof(kernel)); + Requires.NotNull(kernel, nameof(IKernel)); this._kernel = kernel; this._services = services; this._tokenStatistics = tokenStatistics; } + /// public async Task GetCompletion(string text, string agentId, string messageId) { var hooks = _services.GetServices().ToList(); @@ -65,9 +70,11 @@ namespace BotSharp.Plugin.SemanticKernel return result; } + /// public void SetModelName(string model) { - this._model = model; + if (!string.IsNullOrWhiteSpace(model)) + 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 index aa984d28..057c117c 100644 --- a/tests/BotSharp.Plugin.SemanticKernel.UnitTests/BotSharp.Plugin.SemanticKernel.UnitTests.csproj +++ b/tests/BotSharp.Plugin.SemanticKernel.UnitTests/BotSharp.Plugin.SemanticKernel.UnitTests.csproj @@ -12,6 +12,7 @@ + diff --git a/tests/BotSharp.Plugin.SemanticKernel.UnitTests/Helpers/MockChatResult.cs b/tests/BotSharp.Plugin.SemanticKernel.UnitTests/Helpers/ResultHelper.cs similarity index 71% rename from tests/BotSharp.Plugin.SemanticKernel.UnitTests/Helpers/MockChatResult.cs rename to tests/BotSharp.Plugin.SemanticKernel.UnitTests/Helpers/ResultHelper.cs index c34d65ba..84d9bbcc 100644 --- a/tests/BotSharp.Plugin.SemanticKernel.UnitTests/Helpers/MockChatResult.cs +++ b/tests/BotSharp.Plugin.SemanticKernel.UnitTests/Helpers/ResultHelper.cs @@ -1,14 +1,15 @@ using Microsoft.SemanticKernel.AI.ChatCompletion; +using Microsoft.SemanticKernel.AI.TextCompletion; using Microsoft.SemanticKernel.Orchestration; namespace BotSharp.Plugin.SemanticKernel.UnitTests.Helpers { - public class MockChatResult : IChatResult + public class ResultHelper : IChatResult, ITextResult { public ModelResult ModelResult { get; set; } private string _response; - public MockChatResult(string response) + public ResultHelper(string response) { ModelResult = new ModelResult(response); _response = response; @@ -19,6 +20,11 @@ namespace BotSharp.Plugin.SemanticKernel.UnitTests.Helpers return await Task.FromResult(new MockModelResult(_response)); } + public Task GetCompletionAsync(CancellationToken cancellationToken = default) + { + return Task.FromResult(_response); + } + public class MockModelResult : ChatMessageBase { public MockModelResult(string content) : base(AuthorRole.Assistant, content, null) diff --git a/tests/BotSharp.Plugin.SemanticKernel.UnitTests/Helpers/SemanticKernelHelper.cs b/tests/BotSharp.Plugin.SemanticKernel.UnitTests/Helpers/SemanticKernelHelper.cs new file mode 100644 index 00000000..ef775f76 --- /dev/null +++ b/tests/BotSharp.Plugin.SemanticKernel.UnitTests/Helpers/SemanticKernelHelper.cs @@ -0,0 +1,47 @@ +using Microsoft.SemanticKernel.AI; +using Microsoft.SemanticKernel.AI.ChatCompletion; +using Microsoft.SemanticKernel.AI.TextCompletion; +using Microsoft.SemanticKernel.Services; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BotSharp.Plugin.SemanticKernel.UnitTests.Helpers +{ + internal class SemanticKernelHelper : IChatCompletion, ITextCompletion, IAIService + { + private readonly string _excepted; + + public SemanticKernelHelper(string excepted) + { + this._excepted = excepted; + } + + public ChatHistory CreateNewChat(string? instructions = null) + { + return new ChatHistory(); + } + + public Task> GetChatCompletionsAsync(ChatHistory chat, AIRequestSettings? requestSettings = null, CancellationToken cancellationToken = default) + { + return Task.FromResult>( new List { new ResultHelper(_excepted) }); + } + + public Task> GetCompletionsAsync(string text, AIRequestSettings? requestSettings = null, CancellationToken cancellationToken = default) + { + return Task.FromResult>(new List { new ResultHelper(_excepted) }); + } + + public IAsyncEnumerable GetStreamingChatCompletionsAsync(ChatHistory chat, AIRequestSettings? requestSettings = null, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public IAsyncEnumerable GetStreamingCompletionsAsync(string text, AIRequestSettings? requestSettings = null, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + } +} diff --git a/tests/BotSharp.Plugin.SemanticKernel.UnitTests/SemanticKernelChatCompletionProviderTests.cs b/tests/BotSharp.Plugin.SemanticKernel.UnitTests/SemanticKernelChatCompletionProviderTests.cs index 62f19ae2..5be823fa 100644 --- a/tests/BotSharp.Plugin.SemanticKernel.UnitTests/SemanticKernelChatCompletionProviderTests.cs +++ b/tests/BotSharp.Plugin.SemanticKernel.UnitTests/SemanticKernelChatCompletionProviderTests.cs @@ -18,6 +18,7 @@ using BotSharp.Abstraction.Models; using Microsoft.SemanticKernel.AI.ChatCompletion; using Microsoft.SemanticKernel.AI; using Microsoft.SemanticKernel.Connectors.AI.OpenAI.AzureSdk; +using BotSharp.Plugin.SemanticKernel.UnitTests.Helpers; namespace BotSharp.Plugin.SemanticKernel.Tests { @@ -46,13 +47,20 @@ namespace BotSharp.Plugin.SemanticKernel.Tests new RoleDialogModel(AgentRole.User, "Hello") }; + _servicesMock.Setup(x => x.GetService(typeof(IEnumerable))) + .Returns(new List()); + var agentService = new Mock(); + agentService.Setup(x => x.RenderedInstruction(agent)).Returns(""); + _servicesMock.Setup(x => x.GetService(typeof(IAgentService))) + .Returns(agentService.Object); + 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?") + new ResultHelper("How can I help you?") }); _kernelMock.Setup(x => x.GetService(null)).Returns(chatCompletionMock.Object); @@ -64,5 +72,5 @@ namespace BotSharp.Plugin.SemanticKernel.Tests Assert.IsType(result); } } - + } diff --git a/tests/BotSharp.Plugin.SemanticKernel.UnitTests/SemanticKernelPluginTests.cs b/tests/BotSharp.Plugin.SemanticKernel.UnitTests/SemanticKernelPluginTests.cs index 8110c4a1..a1ba7214 100644 --- a/tests/BotSharp.Plugin.SemanticKernel.UnitTests/SemanticKernelPluginTests.cs +++ b/tests/BotSharp.Plugin.SemanticKernel.UnitTests/SemanticKernelPluginTests.cs @@ -1,6 +1,9 @@ +using BotSharp.Abstraction.Conversations; using BotSharp.Abstraction.MLTasks; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Microsoft.SemanticKernel; +using Moq; namespace BotSharp.Plugin.SemanticKernel.Tests { @@ -12,6 +15,14 @@ namespace BotSharp.Plugin.SemanticKernel.Tests var services = new ServiceCollection(); var config = new ConfigurationBuilder().Build(); var plugin = new SemanticKernelPlugin(); + services.AddScoped(x => + { + return new KernelBuilder() + .WithAzureOpenAIChatCompletionService("test", "test", "test") + .Build(); + }); + services.AddScoped(x=> Mock.Of()); + plugin.RegisterDI(services, config); diff --git a/tests/BotSharp.Plugin.SemanticKernel.UnitTests/SemanticKernelTextCompletionProviderTests.cs b/tests/BotSharp.Plugin.SemanticKernel.UnitTests/SemanticKernelTextCompletionProviderTests.cs index 5bb0e57e..1e463cdf 100644 --- a/tests/BotSharp.Plugin.SemanticKernel.UnitTests/SemanticKernelTextCompletionProviderTests.cs +++ b/tests/BotSharp.Plugin.SemanticKernel.UnitTests/SemanticKernelTextCompletionProviderTests.cs @@ -1,5 +1,4 @@ using BotSharp.Abstraction.Conversations.Models; -using BotSharp.Abstraction.MLTasks; using BotSharp.Plugin.SemanticKernel; using Microsoft.Extensions.DependencyInjection; using Microsoft.SemanticKernel; @@ -15,18 +14,18 @@ using System; using System.Linq; using Microsoft; using Microsoft.SemanticKernel.AI; +using BotSharp.Plugin.SemanticKernel.UnitTests.Helpers; 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(); } @@ -35,22 +34,19 @@ namespace BotSharp.Plugin.SemanticKernel.Tests 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); + var _kernel = new KernelBuilder() + .WithAIService("", new SemanticKernelHelper(expected)) + .Build(); + var provider = new SemanticKernelTextCompletionProvider(_kernel, _services, _tokenStatistics); // Act - var result = await provider.GetCompletion(text, agentId, messageId); + var result = await provider.GetCompletion(text, "agent1", "message1"); // Assert Assert.Equal(expected, result); - mockCompletion.Verify(c => c.CompleteAsync(text, null, default(CancellationToken)), Times.Once); } }