diff --git a/dotnet/eng/Version.props b/dotnet/eng/Version.props index 41892aa94132..b9fc4367194c 100644 --- a/dotnet/eng/Version.props +++ b/dotnet/eng/Version.props @@ -2,8 +2,8 @@ 1.0.0-beta.17 - 1.7.1 - 1.7.1-alpha + 1.10.0 + 1.10.0-alpha 5.0.0 4.3.0 6.0.0 diff --git a/dotnet/sample/AutoGen.BasicSamples/AutoGen.BasicSample.csproj b/dotnet/sample/AutoGen.BasicSamples/AutoGen.BasicSample.csproj index 3c2b51669885..0cafff3c0d0a 100644 --- a/dotnet/sample/AutoGen.BasicSamples/AutoGen.BasicSample.csproj +++ b/dotnet/sample/AutoGen.BasicSamples/AutoGen.BasicSample.csproj @@ -6,7 +6,7 @@ enable enable True - $(NoWarn);CS8981;CS8600;CS8602;CS8604;CS8618;CS0219;SKEXP0054;SKEXP0050 + $(NoWarn);CS8981;CS8600;CS8602;CS8604;CS8618;CS0219;SKEXP0054;SKEXP0050;SKEXP0110 diff --git a/dotnet/sample/AutoGen.BasicSamples/CodeSnippet/SemanticKernelCodeSnippet.cs b/dotnet/sample/AutoGen.BasicSamples/CodeSnippet/SemanticKernelCodeSnippet.cs index b0366eb2b3fa..20dd12d90ced 100644 --- a/dotnet/sample/AutoGen.BasicSamples/CodeSnippet/SemanticKernelCodeSnippet.cs +++ b/dotnet/sample/AutoGen.BasicSamples/CodeSnippet/SemanticKernelCodeSnippet.cs @@ -98,5 +98,4 @@ public async Task SemanticKernelChatMessageContentConnector() } #endregion register_semantic_kernel_chat_message_content_connector } - } diff --git a/dotnet/src/AutoGen.SemanticKernel/AutoGen.SemanticKernel.csproj b/dotnet/src/AutoGen.SemanticKernel/AutoGen.SemanticKernel.csproj index 06c464fc95f5..be2fa0a574ba 100644 --- a/dotnet/src/AutoGen.SemanticKernel/AutoGen.SemanticKernel.csproj +++ b/dotnet/src/AutoGen.SemanticKernel/AutoGen.SemanticKernel.csproj @@ -5,6 +5,10 @@ AutoGen.SemanticKernel + + $(NoWarn);SKEXP0110 + + @@ -18,6 +22,7 @@ + diff --git a/dotnet/src/AutoGen.SemanticKernel/SemanticKernelChatCompletionAgent.cs b/dotnet/src/AutoGen.SemanticKernel/SemanticKernelChatCompletionAgent.cs new file mode 100644 index 000000000000..82d83a9e8556 --- /dev/null +++ b/dotnet/src/AutoGen.SemanticKernel/SemanticKernelChatCompletionAgent.cs @@ -0,0 +1,51 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// SemanticKernelChatCompletionAgent.cs + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.SemanticKernel; +using Microsoft.SemanticKernel.Agents; +using Microsoft.SemanticKernel.ChatCompletion; + +namespace AutoGen.SemanticKernel; + +public class SemanticKernelChatCompletionAgent : IAgent +{ + public string Name { get; } + private readonly ChatCompletionAgent _chatCompletionAgent; + + public SemanticKernelChatCompletionAgent(ChatCompletionAgent chatCompletionAgent) + { + this.Name = chatCompletionAgent.Name ?? throw new ArgumentNullException(nameof(chatCompletionAgent.Name)); + this._chatCompletionAgent = chatCompletionAgent; + } + + public async Task GenerateReplyAsync(IEnumerable messages, GenerateReplyOptions? options = null, + CancellationToken cancellationToken = default) + { + ChatMessageContent[] reply = await _chatCompletionAgent + .InvokeAsync(BuildChatHistory(messages), cancellationToken) + .ToArrayAsync(cancellationToken: cancellationToken); + + return reply.Length > 1 + ? throw new InvalidOperationException("ResultsPerPrompt greater than 1 is not supported in this semantic kernel agent") + : new MessageEnvelope(reply[0], from: this.Name); + } + + private ChatHistory BuildChatHistory(IEnumerable messages) + { + return new ChatHistory(ProcessMessage(messages)); + } + + private IEnumerable ProcessMessage(IEnumerable messages) + { + return messages.Select(m => m switch + { + IMessage cmc => cmc.Content, + _ => throw new ArgumentException("Invalid message type") + }); + } +} diff --git a/dotnet/test/AutoGen.Tests/AutoGen.Tests.csproj b/dotnet/test/AutoGen.Tests/AutoGen.Tests.csproj index 9a7b07b34dd7..740772c04079 100644 --- a/dotnet/test/AutoGen.Tests/AutoGen.Tests.csproj +++ b/dotnet/test/AutoGen.Tests/AutoGen.Tests.csproj @@ -3,7 +3,7 @@ $(TestTargetFramework) True - $(NoWarn);xUnit1013 + $(NoWarn);xUnit1013;SKEXP0110 diff --git a/dotnet/test/AutoGen.Tests/SemanticKernelAgentTest.cs b/dotnet/test/AutoGen.Tests/SemanticKernelAgentTest.cs index dcb5cd47b0d8..0fcf5a6abe6d 100644 --- a/dotnet/test/AutoGen.Tests/SemanticKernelAgentTest.cs +++ b/dotnet/test/AutoGen.Tests/SemanticKernelAgentTest.cs @@ -8,7 +8,9 @@ using AutoGen.SemanticKernel.Extension; using FluentAssertions; using Microsoft.SemanticKernel; +using Microsoft.SemanticKernel.Agents; using Microsoft.SemanticKernel.ChatCompletion; +using Microsoft.SemanticKernel.Connectors.OpenAI; namespace AutoGen.Tests; @@ -69,8 +71,7 @@ public async Task SemanticKernelChatMessageContentConnectorTestAsync() var messages = new IMessage[] { MessageEnvelope.Create(new ChatMessageContent(AuthorRole.Assistant, "Hello")), - new TextMessage(Role.Assistant, "Hello", from: "user"), - new MultiModalMessage(Role.Assistant, + new TextMessage(Role.Assistant, "Hello", from: "user"), new MultiModalMessage(Role.Assistant, [ new TextMessage(Role.Assistant, "Hello", from: "user"), ], @@ -128,4 +129,110 @@ public async Task SemanticKernelPluginTestAsync() reply.GetContent()!.ToLower().Should().Contain("seattle"); reply.GetContent()!.ToLower().Should().Contain("sunny"); } + + + [ApiKeyFact("AZURE_OPENAI_API_KEY", "AZURE_OPENAI_ENDPOINT")] + public async Task BasicSkChatCompletionAgentConversationTestAsync() + { + var endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new Exception("Please set AZURE_OPENAI_ENDPOINT environment variable."); + var key = Environment.GetEnvironmentVariable("AZURE_OPENAI_API_KEY") ?? throw new Exception("Please set AZURE_OPENAI_API_KEY environment variable."); + var builder = Kernel.CreateBuilder() + .AddAzureOpenAIChatCompletion("gpt-35-turbo-16k", endpoint, key); + + var kernel = builder.Build(); + var agent = new ChatCompletionAgent() + { + Kernel = kernel, + Name = "assistant", + Instructions = "You are a helpful AI assistant" + }; + + var skAgent = new SemanticKernelChatCompletionAgent(agent); + + var chatMessageContent = MessageEnvelope.Create(new ChatMessageContent(AuthorRole.Assistant, "Hello")); + var reply = await skAgent.SendAsync(chatMessageContent); + + reply.Should().BeOfType>(); + reply.As>().From.Should().Be("assistant"); + } + + [ApiKeyFact("AZURE_OPENAI_API_KEY", "AZURE_OPENAI_ENDPOINT")] + public async Task SkChatCompletionAgentChatMessageContentConnectorTestAsync() + { + var endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new Exception("Please set AZURE_OPENAI_ENDPOINT environment variable."); + var key = Environment.GetEnvironmentVariable("AZURE_OPENAI_API_KEY") ?? throw new Exception("Please set AZURE_OPENAI_API_KEY environment variable."); + var builder = Kernel.CreateBuilder() + .AddAzureOpenAIChatCompletion("gpt-35-turbo-16k", endpoint, key); + + var kernel = builder.Build(); + + var connector = new SemanticKernelChatMessageContentConnector(); + var agent = new ChatCompletionAgent() + { + Kernel = kernel, + Name = "assistant", + Instructions = "You are a helpful AI assistant" + }; + var skAgent = new SemanticKernelChatCompletionAgent(agent) + .RegisterMiddleware(connector); + + var messages = new IMessage[] + { + MessageEnvelope.Create(new ChatMessageContent(AuthorRole.Assistant, "Hello")), + new TextMessage(Role.Assistant, "Hello", from: "user"), new MultiModalMessage(Role.Assistant, + [ + new TextMessage(Role.Assistant, "Hello", from: "user"), + ], + from: "user"), + }; + + foreach (var message in messages) + { + var reply = await skAgent.SendAsync(message); + + reply.Should().BeOfType(); + reply.As().From.Should().Be("assistant"); + } + } + + [ApiKeyFact("AZURE_OPENAI_API_KEY", "AZURE_OPENAI_ENDPOINT")] + public async Task SkChatCompletionAgentPluginTestAsync() + { + var endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new Exception("Please set AZURE_OPENAI_ENDPOINT environment variable."); + var key = Environment.GetEnvironmentVariable("AZURE_OPENAI_API_KEY") ?? throw new Exception("Please set AZURE_OPENAI_API_KEY environment variable."); + var builder = Kernel.CreateBuilder() + .AddAzureOpenAIChatCompletion("gpt-35-turbo-16k", endpoint, key); + + var parameters = this.GetWeatherAsyncFunctionContract.Parameters!.Select(p => new KernelParameterMetadata(p.Name!) + { + Description = p.Description, + DefaultValue = p.DefaultValue, + IsRequired = p.IsRequired, + ParameterType = p.ParameterType, + }); + var function = KernelFunctionFactory.CreateFromMethod(this.GetWeatherAsync, this.GetWeatherAsyncFunctionContract.Name, this.GetWeatherAsyncFunctionContract.Description, parameters); + builder.Plugins.AddFromFunctions("plugins", [function]); + var kernel = builder.Build(); + + var agent = new ChatCompletionAgent() + { + Kernel = kernel, + Name = "assistant", + Instructions = "You are a helpful AI assistant", + ExecutionSettings = + new OpenAIPromptExecutionSettings() + { + ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions + } + }; + var skAgent = + new SemanticKernelChatCompletionAgent(agent).RegisterMiddleware( + new SemanticKernelChatMessageContentConnector()); + + var question = "What is the weather in Seattle?"; + var reply = await skAgent.SendAsync(question); + + reply.GetContent()!.ToLower().Should().Contain("seattle"); + reply.GetContent()!.ToLower().Should().Contain("sunny"); + } }