diff --git a/dotnet/src/SemanticKernel.Abstractions/AI/XmlPromptParser.cs b/dotnet/src/SemanticKernel.Abstractions/AI/XmlPromptParser.cs index 3dacedb81f4d..0c7dfdd30fe6 100644 --- a/dotnet/src/SemanticKernel.Abstractions/AI/XmlPromptParser.cs +++ b/dotnet/src/SemanticKernel.Abstractions/AI/XmlPromptParser.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using System.Linq; using System.Web; using System.Xml; @@ -38,9 +39,14 @@ public static bool TryParse(string prompt, [NotNullWhen(true)] out List{prompt}"); } catch (XmlException) @@ -70,8 +76,17 @@ public static bool TryParse(string prompt, [NotNullWhen(true)] out List() + .Where(n => n.NodeType != XmlNodeType.Whitespace) + .FirstOrDefault(); + + var isCData = firstNonWhitespaceChild?.NodeType == XmlNodeType.CDATA; + var nodeContent = isCData + ? node.InnerText.Trim() + : node.InnerXml.Trim(); var promptNode = new PromptNode(node.Name) { diff --git a/dotnet/src/SemanticKernel.UnitTests/Prompt/ChatPromptParserTests.cs b/dotnet/src/SemanticKernel.UnitTests/Prompt/ChatPromptParserTests.cs index 01a4458ce2b3..ecb051b7d7b1 100644 --- a/dotnet/src/SemanticKernel.UnitTests/Prompt/ChatPromptParserTests.cs +++ b/dotnet/src/SemanticKernel.UnitTests/Prompt/ChatPromptParserTests.cs @@ -114,6 +114,55 @@ public void ItReturnsChatHistoryWithValidContentItemsIncludeCData() """, c.Content)); } + [Fact] + public void ItReturnsChatHistoryWithValidContentItemsIncludeCode() + { + // Arrange + string prompt = GetValidPromptWithCodeBlock(); + + // Act + bool result = ChatPromptParser.TryParse(prompt, out var chatHistory); + + // Assert + Assert.True(result); + Assert.NotNull(chatHistory); + + Assert.Collection(chatHistory, + // The first message entry inside prompt is neither wrapped in CDATA or HtmlEncoded, so the single quotes are not preserved. + c => Assert.Equal(""" + + + Text content + + + """, c.Content), + // Since the second message entry inside prompt is wrapped in CDATA, the single quotes are preserved. + c => Assert.Equal(""" + + + Text content + + + """, c.Content), + // Since the third message entry inside prompt is HtmlEncoded, the single quotes are preserved. + c => Assert.Equal(""" + + + Text content + + + """, c.Content), + // In this case, when we trim node.InnerXml only the opening tag is indented. + c => Assert.Equal(""" + + explain image + + https://fake-link-to-image/ + + + """, c.Content)); + } + private static string GetSimpleValidPrompt() { return @@ -181,4 +230,47 @@ private static string GetValidPromptWithCDataSection() """; } + + private static string GetValidPromptWithCodeBlock() + { + return + """ + + + + + Text content + + + + + + + + Text content + + + ]]> + + + + <code> + <message role='system'> + <text>Text content</text> + </message> + </code> + + + + + explain image + + https://fake-link-to-image/ + + + + + """; + } }