Skip to content

Commit cc078ec

Browse files
authored
.Net: Add Support for {Azure}OpenAIPromptExecutionSettings.ChatSystem/DeveloperPrompt for ChatClients (#12619)
### Motivation and Context - Fixes #12546
1 parent 7631ef9 commit cc078ec

File tree

9 files changed

+926
-9
lines changed

9 files changed

+926
-9
lines changed

dotnet/src/Connectors/Connectors.OpenAI.UnitTests/Extensions/ChatHistoryExtensionsTests.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
using Xunit;
1313

1414
namespace SemanticKernel.Connectors.OpenAI.UnitTests.Extensions;
15+
1516
public class ChatHistoryExtensionsTests
1617
{
1718
[Fact]

dotnet/src/Connectors/Connectors.OpenAI.UnitTests/Settings/OpenAIPromptExecutionSettingsTests.cs

Lines changed: 360 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System.Collections.Generic;
55
using System.Text.Json;
66
using Microsoft.SemanticKernel;
7+
using Microsoft.SemanticKernel.ChatCompletion;
78
using Microsoft.SemanticKernel.Connectors.OpenAI;
89
using Xunit;
910

@@ -470,6 +471,365 @@ public void ItCannotCreateOpenAIPromptExecutionSettingsWithInvalidBoolValues(obj
470471
Assert.Throws<ArgumentException>(() => OpenAIPromptExecutionSettings.FromExecutionSettings(originalSettings));
471472
}
472473

474+
[Fact]
475+
public void PrepareChatHistoryToRequestAsyncAddsSystemPromptWhenNotPresent()
476+
{
477+
// Arrange
478+
var settings = new TestableOpenAIPromptExecutionSettings
479+
{
480+
ChatSystemPrompt = "You are a helpful assistant."
481+
};
482+
483+
var chatHistory = new ChatHistory();
484+
chatHistory.AddUserMessage("Hello");
485+
486+
// Act
487+
var result = settings.TestPrepareChatHistoryToRequest(chatHistory);
488+
489+
// Assert
490+
Assert.Same(chatHistory, result); // Should return the same instance
491+
Assert.Equal(2, chatHistory.Count);
492+
Assert.Equal(AuthorRole.System, chatHistory[0].Role);
493+
Assert.Equal("You are a helpful assistant.", chatHistory[0].Content);
494+
Assert.Equal(AuthorRole.User, chatHistory[1].Role);
495+
Assert.Equal("Hello", chatHistory[1].Content);
496+
}
497+
498+
[Fact]
499+
public void PrepareChatHistoryToRequestAsyncAddsSystemPromptAtBeginning()
500+
{
501+
// Arrange
502+
var settings = new TestableOpenAIPromptExecutionSettings
503+
{
504+
ChatSystemPrompt = "You are a helpful assistant."
505+
};
506+
507+
var chatHistory = new ChatHistory();
508+
chatHistory.AddUserMessage("First message");
509+
chatHistory.AddAssistantMessage("First response");
510+
chatHistory.AddUserMessage("Second message");
511+
512+
// Act
513+
var result = settings.TestPrepareChatHistoryToRequest(chatHistory);
514+
515+
// Assert
516+
Assert.Same(chatHistory, result);
517+
Assert.Equal(4, chatHistory.Count);
518+
Assert.Equal(AuthorRole.System, chatHistory[0].Role);
519+
Assert.Equal("You are a helpful assistant.", chatHistory[0].Content);
520+
Assert.Equal(AuthorRole.User, chatHistory[1].Role);
521+
Assert.Equal("First message", chatHistory[1].Content);
522+
Assert.Equal(AuthorRole.Assistant, chatHistory[2].Role);
523+
Assert.Equal("First response", chatHistory[2].Content);
524+
Assert.Equal(AuthorRole.User, chatHistory[3].Role);
525+
Assert.Equal("Second message", chatHistory[3].Content);
526+
}
527+
528+
[Fact]
529+
public void PrepareChatHistoryToRequestAsyncDoesNotAddSystemPromptWhenAlreadyPresent()
530+
{
531+
// Arrange
532+
var settings = new TestableOpenAIPromptExecutionSettings
533+
{
534+
ChatSystemPrompt = "You are a helpful assistant."
535+
};
536+
537+
var chatHistory = new ChatHistory();
538+
chatHistory.AddSystemMessage("Existing system message");
539+
chatHistory.AddUserMessage("Hello");
540+
541+
// Act
542+
var result = settings.TestPrepareChatHistoryToRequest(chatHistory);
543+
544+
// Assert
545+
Assert.Same(chatHistory, result);
546+
Assert.Equal(2, chatHistory.Count);
547+
Assert.Equal(AuthorRole.System, chatHistory[0].Role);
548+
Assert.Equal("Existing system message", chatHistory[0].Content); // Original system message preserved
549+
Assert.Equal(AuthorRole.User, chatHistory[1].Role);
550+
Assert.Equal("Hello", chatHistory[1].Content);
551+
}
552+
553+
[Fact]
554+
public void PrepareChatHistoryToRequestAsyncAddsDeveloperPromptWhenNotPresent()
555+
{
556+
// Arrange
557+
var settings = new TestableOpenAIPromptExecutionSettings
558+
{
559+
ChatDeveloperPrompt = "Debug mode enabled."
560+
};
561+
562+
var chatHistory = new ChatHistory();
563+
chatHistory.AddUserMessage("Hello");
564+
565+
// Act
566+
var result = settings.TestPrepareChatHistoryToRequest(chatHistory);
567+
568+
// Assert
569+
Assert.Same(chatHistory, result);
570+
Assert.Equal(2, chatHistory.Count);
571+
Assert.Equal(AuthorRole.Developer, chatHistory[0].Role);
572+
Assert.Equal("Debug mode enabled.", chatHistory[0].Content);
573+
Assert.Equal(AuthorRole.User, chatHistory[1].Role);
574+
Assert.Equal("Hello", chatHistory[1].Content);
575+
}
576+
577+
[Fact]
578+
public void PrepareChatHistoryToRequestAsyncDoesNotAddDeveloperPromptWhenAlreadyPresent()
579+
{
580+
// Arrange
581+
var settings = new TestableOpenAIPromptExecutionSettings
582+
{
583+
ChatDeveloperPrompt = "Debug mode enabled."
584+
};
585+
586+
var chatHistory = new ChatHistory();
587+
chatHistory.AddDeveloperMessage("Existing developer message");
588+
chatHistory.AddUserMessage("Hello");
589+
590+
// Act
591+
var result = settings.TestPrepareChatHistoryToRequest(chatHistory);
592+
593+
// Assert
594+
Assert.Same(chatHistory, result);
595+
Assert.Equal(2, chatHistory.Count);
596+
Assert.Equal(AuthorRole.Developer, chatHistory[0].Role);
597+
Assert.Equal("Existing developer message", chatHistory[0].Content); // Original developer message preserved
598+
Assert.Equal(AuthorRole.User, chatHistory[1].Role);
599+
Assert.Equal("Hello", chatHistory[1].Content);
600+
}
601+
602+
[Fact]
603+
public void PrepareChatHistoryToRequestAsyncAddsBothSystemAndDeveloperPrompts()
604+
{
605+
// Arrange
606+
var settings = new TestableOpenAIPromptExecutionSettings
607+
{
608+
ChatSystemPrompt = "You are a helpful assistant.",
609+
ChatDeveloperPrompt = "Debug mode enabled."
610+
};
611+
612+
var chatHistory = new ChatHistory();
613+
chatHistory.AddUserMessage("Hello");
614+
615+
// Act
616+
var result = settings.TestPrepareChatHistoryToRequest(chatHistory);
617+
618+
// Assert
619+
Assert.Same(chatHistory, result);
620+
Assert.Equal(3, chatHistory.Count);
621+
Assert.Equal(AuthorRole.System, chatHistory[0].Role);
622+
Assert.Equal("You are a helpful assistant.", chatHistory[0].Content);
623+
Assert.Equal(AuthorRole.Developer, chatHistory[1].Role);
624+
Assert.Equal("Debug mode enabled.", chatHistory[1].Content);
625+
Assert.Equal(AuthorRole.User, chatHistory[2].Role);
626+
Assert.Equal("Hello", chatHistory[2].Content);
627+
}
628+
629+
[Fact]
630+
public void PrepareChatHistoryToRequestAsyncDoesNotAddEmptyOrWhitespacePrompts()
631+
{
632+
// Arrange
633+
var settings = new TestableOpenAIPromptExecutionSettings
634+
{
635+
ChatSystemPrompt = " ", // Whitespace only
636+
ChatDeveloperPrompt = "" // Empty string
637+
};
638+
639+
var chatHistory = new ChatHistory();
640+
chatHistory.AddUserMessage("Hello");
641+
642+
// Act
643+
var result = settings.TestPrepareChatHistoryToRequest(chatHistory);
644+
645+
// Assert
646+
Assert.Same(chatHistory, result);
647+
Assert.Single(chatHistory); // Only the original user message should remain
648+
Assert.Equal(AuthorRole.User, chatHistory[0].Role);
649+
Assert.Equal("Hello", chatHistory[0].Content);
650+
}
651+
652+
[Fact]
653+
public void PrepareChatHistoryToRequestAsyncDoesNotAddNullPrompts()
654+
{
655+
// Arrange
656+
var settings = new TestableOpenAIPromptExecutionSettings
657+
{
658+
ChatSystemPrompt = null,
659+
ChatDeveloperPrompt = null
660+
};
661+
662+
var chatHistory = new ChatHistory();
663+
chatHistory.AddUserMessage("Hello");
664+
665+
// Act
666+
var result = settings.TestPrepareChatHistoryToRequest(chatHistory);
667+
668+
// Assert
669+
Assert.Same(chatHistory, result);
670+
Assert.Single(chatHistory); // Only the original user message should remain
671+
Assert.Equal(AuthorRole.User, chatHistory[0].Role);
672+
Assert.Equal("Hello", chatHistory[0].Content);
673+
}
674+
675+
[Fact]
676+
public void PrepareChatHistoryToRequestAsyncWorksWithEmptyChatHistory()
677+
{
678+
// Arrange
679+
var settings = new TestableOpenAIPromptExecutionSettings
680+
{
681+
ChatSystemPrompt = "You are a helpful assistant.",
682+
ChatDeveloperPrompt = "Debug mode enabled."
683+
};
684+
685+
var chatHistory = new ChatHistory();
686+
687+
// Act
688+
var result = settings.TestPrepareChatHistoryToRequest(chatHistory);
689+
690+
// Assert
691+
Assert.Same(chatHistory, result);
692+
Assert.Equal(2, chatHistory.Count);
693+
Assert.Equal(AuthorRole.System, chatHistory[0].Role);
694+
Assert.Equal("You are a helpful assistant.", chatHistory[0].Content);
695+
Assert.Equal(AuthorRole.Developer, chatHistory[1].Role);
696+
Assert.Equal("Debug mode enabled.", chatHistory[1].Content);
697+
}
698+
699+
[Fact]
700+
public void PrepareChatHistoryToRequestAsyncPreservesExistingMessageOrder()
701+
{
702+
// Arrange
703+
var settings = new TestableOpenAIPromptExecutionSettings
704+
{
705+
ChatSystemPrompt = "You are a helpful assistant."
706+
};
707+
708+
var chatHistory = new ChatHistory();
709+
chatHistory.AddDeveloperMessage("Existing developer message");
710+
chatHistory.AddUserMessage("First user message");
711+
chatHistory.AddAssistantMessage("Assistant response");
712+
chatHistory.AddUserMessage("Second user message");
713+
714+
// Act
715+
var result = settings.TestPrepareChatHistoryToRequest(chatHistory);
716+
717+
// Assert
718+
Assert.Same(chatHistory, result);
719+
Assert.Equal(5, chatHistory.Count);
720+
721+
// System message should be added at the beginning, before existing developer message
722+
Assert.Equal(AuthorRole.System, chatHistory[0].Role);
723+
Assert.Equal("You are a helpful assistant.", chatHistory[0].Content);
724+
Assert.Equal(AuthorRole.Developer, chatHistory[1].Role);
725+
Assert.Equal("Existing developer message", chatHistory[1].Content);
726+
Assert.Equal(AuthorRole.User, chatHistory[2].Role);
727+
Assert.Equal("First user message", chatHistory[2].Content);
728+
Assert.Equal(AuthorRole.Assistant, chatHistory[3].Role);
729+
Assert.Equal("Assistant response", chatHistory[3].Content);
730+
Assert.Equal(AuthorRole.User, chatHistory[4].Role);
731+
Assert.Equal("Second user message", chatHistory[4].Content);
732+
}
733+
734+
[Fact]
735+
public void PrepareChatHistoryToRequestAsyncInsertsSystemBeforeDeveloperWhenBothExist()
736+
{
737+
// Arrange
738+
var settings = new TestableOpenAIPromptExecutionSettings
739+
{
740+
ChatSystemPrompt = "You are a helpful assistant.",
741+
ChatDeveloperPrompt = "Debug mode enabled."
742+
};
743+
744+
var chatHistory = new ChatHistory();
745+
chatHistory.AddDeveloperMessage("Existing developer message");
746+
chatHistory.AddSystemMessage("Existing system message");
747+
chatHistory.AddUserMessage("Hello");
748+
749+
// Act
750+
var result = settings.TestPrepareChatHistoryToRequest(chatHistory);
751+
752+
// Assert
753+
Assert.Same(chatHistory, result);
754+
Assert.Equal(3, chatHistory.Count); // No new messages should be added since both already exist
755+
Assert.Equal(AuthorRole.Developer, chatHistory[0].Role);
756+
Assert.Equal("Existing developer message", chatHistory[0].Content);
757+
Assert.Equal(AuthorRole.System, chatHistory[1].Role);
758+
Assert.Equal("Existing system message", chatHistory[1].Content);
759+
Assert.Equal(AuthorRole.User, chatHistory[2].Role);
760+
Assert.Equal("Hello", chatHistory[2].Content);
761+
}
762+
763+
[Fact]
764+
public void PrepareChatHistoryToRequestAsyncAddsSystemBeforeExistingDeveloper()
765+
{
766+
// Arrange
767+
var settings = new TestableOpenAIPromptExecutionSettings
768+
{
769+
ChatSystemPrompt = "You are a helpful assistant.",
770+
ChatDeveloperPrompt = "Debug mode enabled."
771+
};
772+
773+
var chatHistory = new ChatHistory();
774+
chatHistory.AddDeveloperMessage("Existing developer message");
775+
chatHistory.AddUserMessage("Hello");
776+
777+
// Act
778+
var result = settings.TestPrepareChatHistoryToRequest(chatHistory);
779+
780+
// Assert
781+
Assert.Same(chatHistory, result);
782+
Assert.Equal(3, chatHistory.Count);
783+
784+
// System message should be inserted at the beginning, before existing developer message
785+
Assert.Equal(AuthorRole.System, chatHistory[0].Role);
786+
Assert.Equal("You are a helpful assistant.", chatHistory[0].Content);
787+
Assert.Equal(AuthorRole.Developer, chatHistory[1].Role);
788+
Assert.Equal("Existing developer message", chatHistory[1].Content);
789+
Assert.Equal(AuthorRole.User, chatHistory[2].Role);
790+
Assert.Equal("Hello", chatHistory[2].Content);
791+
}
792+
793+
[Fact]
794+
public void PrepareChatHistoryToRequestAsyncAddsDeveloperWhenSystemExists()
795+
{
796+
// Arrange
797+
var settings = new TestableOpenAIPromptExecutionSettings
798+
{
799+
ChatDeveloperPrompt = "Debug mode enabled."
800+
};
801+
802+
var chatHistory = new ChatHistory();
803+
chatHistory.AddSystemMessage("Existing system message");
804+
chatHistory.AddUserMessage("Hello");
805+
806+
// Act
807+
var result = settings.TestPrepareChatHistoryToRequest(chatHistory);
808+
809+
// Assert
810+
Assert.Same(chatHistory, result);
811+
Assert.Equal(3, chatHistory.Count);
812+
813+
// Developer message should be inserted at the beginning, before existing system message
814+
Assert.Equal(AuthorRole.Developer, chatHistory[0].Role);
815+
Assert.Equal("Debug mode enabled.", chatHistory[0].Content);
816+
Assert.Equal(AuthorRole.System, chatHistory[1].Role);
817+
Assert.Equal("Existing system message", chatHistory[1].Content);
818+
Assert.Equal(AuthorRole.User, chatHistory[2].Role);
819+
Assert.Equal("Hello", chatHistory[2].Content);
820+
}
821+
822+
/// <summary>
823+
/// Test implementation of OpenAIPromptExecutionSettings that exposes the protected PrepareChatHistoryToRequestAsync method.
824+
/// </summary>
825+
private sealed class TestableOpenAIPromptExecutionSettings : OpenAIPromptExecutionSettings
826+
{
827+
public ChatHistory TestPrepareChatHistoryToRequest(ChatHistory chatHistory)
828+
{
829+
return base.PrepareChatHistoryForRequest(chatHistory);
830+
}
831+
}
832+
473833
private static void AssertExecutionSettings(OpenAIPromptExecutionSettings executionSettings)
474834
{
475835
Assert.NotNull(executionSettings);

0 commit comments

Comments
 (0)