diff --git a/dotnet/src/Connectors/Connectors.OpenAI/AzureSdk/ClientCore.cs b/dotnet/src/Connectors/Connectors.OpenAI/AzureSdk/ClientCore.cs index e73584ea62c7..e8ea5c31e9bc 100644 --- a/dotnet/src/Connectors/Connectors.OpenAI/AzureSdk/ClientCore.cs +++ b/dotnet/src/Connectors/Connectors.OpenAI/AzureSdk/ClientCore.cs @@ -799,6 +799,11 @@ private static ChatCompletionsOptions CreateChatCompletionsOptions( } } + if (!string.IsNullOrWhiteSpace(executionSettings?.ChatSystemPrompt) && !chatHistory.Any(m => m.Role == AuthorRole.System)) + { + options.Messages.Add(GetRequestMessage(new ChatMessageContent(AuthorRole.System, executionSettings!.ChatSystemPrompt))); + } + foreach (var message in chatHistory) { options.Messages.Add(GetRequestMessage(message)); diff --git a/dotnet/src/Connectors/Connectors.UnitTests/OpenAI/ChatCompletion/OpenAIChatCompletionTests.cs b/dotnet/src/Connectors/Connectors.UnitTests/OpenAI/ChatCompletion/OpenAIChatCompletionTests.cs index d6638f08350a..c693c8cd3616 100644 --- a/dotnet/src/Connectors/Connectors.UnitTests/OpenAI/ChatCompletion/OpenAIChatCompletionTests.cs +++ b/dotnet/src/Connectors/Connectors.UnitTests/OpenAI/ChatCompletion/OpenAIChatCompletionTests.cs @@ -121,8 +121,8 @@ public async Task ItAddsIdToChatMessageAsync() var actualRequestContent = Encoding.UTF8.GetString(this._messageHandlerStub.RequestContent!); Assert.NotNull(actualRequestContent); var optionsJson = JsonSerializer.Deserialize(actualRequestContent); - Assert.Equal(1, optionsJson.GetProperty("messages").GetArrayLength()); - Assert.Equal("John Doe", optionsJson.GetProperty("messages")[0].GetProperty("tool_call_id").GetString()); + Assert.Equal(2, optionsJson.GetProperty("messages").GetArrayLength()); + Assert.Equal("John Doe", optionsJson.GetProperty("messages")[1].GetProperty("tool_call_id").GetString()); } [Fact] @@ -163,6 +163,30 @@ public async Task ItGetTextContentsShouldHaveModelIdDefinedAsync() Assert.Equal("gpt-3.5-turbo", textContent.ModelId); } + [Fact] + public async Task ItAddsSystemMessageAsync() + { + // Arrange + var chatCompletion = new OpenAIChatCompletionService(modelId: "gpt-3.5-turbo", apiKey: "NOKEY", httpClient: this._httpClient); + this._messageHandlerStub.ResponseToReturn = new HttpResponseMessage(System.Net.HttpStatusCode.OK) + { Content = new StringContent(ChatCompletionResponse) }; + var chatHistory = new ChatHistory(); + chatHistory.AddMessage(AuthorRole.User, "Hello"); + + // Act + await chatCompletion.GetChatMessageContentsAsync(chatHistory, this._executionSettings); + + // Assert + var actualRequestContent = Encoding.UTF8.GetString(this._messageHandlerStub.RequestContent!); + Assert.NotNull(actualRequestContent); + var optionsJson = JsonSerializer.Deserialize(actualRequestContent); + Assert.Equal(2, optionsJson.GetProperty("messages").GetArrayLength()); + Assert.Equal("Assistant is a large language model.", optionsJson.GetProperty("messages")[0].GetProperty("content").GetString()); + Assert.Equal("system", optionsJson.GetProperty("messages")[0].GetProperty("role").GetString()); + Assert.Equal("Hello", optionsJson.GetProperty("messages")[1].GetProperty("content").GetString()); + Assert.Equal("user", optionsJson.GetProperty("messages")[1].GetProperty("role").GetString()); + } + public void Dispose() { this._httpClient.Dispose(); diff --git a/dotnet/src/IntegrationTests/Connectors/OpenAI/ChatHistoryTests.cs b/dotnet/src/IntegrationTests/Connectors/OpenAI/ChatHistoryTests.cs index 3a7e2bf8bb89..220fea717fef 100644 --- a/dotnet/src/IntegrationTests/Connectors/OpenAI/ChatHistoryTests.cs +++ b/dotnet/src/IntegrationTests/Connectors/OpenAI/ChatHistoryTests.cs @@ -70,6 +70,54 @@ public async Task ItSerializesAndDeserializesChatHistoryAsync() Assert.Null(exception); } + [Fact] + public async Task ItUsesChatSystemPromptFromSettingsAsync() + { + // Arrange + this._kernelBuilder.Services.AddSingleton(this._logger); + var builder = this._kernelBuilder; + this.ConfigureAzureOpenAIChatAsText(builder); + builder.Plugins.AddFromType(); + var kernel = builder.Build(); + + string systemPrompt = "You are batman. If asked who you are, say 'I am Batman!'"; + + OpenAIPromptExecutionSettings settings = new() { ChatSystemPrompt = systemPrompt }; + ChatHistory history = new(); + + // Act + history.AddUserMessage("Who are you?"); + var service = kernel.GetRequiredService(); + ChatMessageContent result = await service.GetChatMessageContentAsync(history, settings, kernel); + + // Assert + Assert.Contains("Batman", result.ToString(), StringComparison.OrdinalIgnoreCase); + } + + [Fact] + public async Task ItUsesChatSystemPromptFromChatHistoryAsync() + { + // Arrange + this._kernelBuilder.Services.AddSingleton(this._logger); + var builder = this._kernelBuilder; + this.ConfigureAzureOpenAIChatAsText(builder); + builder.Plugins.AddFromType(); + var kernel = builder.Build(); + + string systemPrompt = "You are batman. If asked who you are, say 'I am Batman!'"; + + OpenAIPromptExecutionSettings settings = new(); + ChatHistory history = new(systemPrompt); + + // Act + history.AddUserMessage("Who are you?"); + var service = kernel.GetRequiredService(); + ChatMessageContent result = await service.GetChatMessageContentAsync(history, settings, kernel); + + // Assert + Assert.Contains("Batman", result.ToString(), StringComparison.OrdinalIgnoreCase); + } + private void ConfigureAzureOpenAIChatAsText(IKernelBuilder kernelBuilder) { var azureOpenAIConfiguration = this._configuration.GetSection("Planners:AzureOpenAI").Get();