From ff3c772822349528fe05adda1c00d2a187d51666 Mon Sep 17 00:00:00 2001 From: "Ethan T." Date: Fri, 13 Mar 2026 13:35:30 +0800 Subject: [PATCH 1/2] fix: preserve unrecognized XML/HTML tags in prompt templates When a prompt template contains HTML tags like

, the XML parser in from_rendered_prompt() would silently drop them because they are not recognized as 'message' or 'chat_history' elements. This fix serializes unrecognized XML elements back to their string form and appends them to the preceding message content, preserving HTML tags like

,

, etc. in the final prompt sent to the model. Fixes microsoft#13632 --- python/semantic_kernel/contents/chat_history.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/python/semantic_kernel/contents/chat_history.py b/python/semantic_kernel/contents/chat_history.py index e5daec19fef5..97e325b23405 100644 --- a/python/semantic_kernel/contents/chat_history.py +++ b/python/semantic_kernel/contents/chat_history.py @@ -350,11 +350,23 @@ def from_rendered_prompt(cls: type[_T], rendered_prompt: str) -> _T: for item in xml_prompt: if item.tag == CHAT_MESSAGE_CONTENT_TAG: messages.append(ChatMessageContent.from_element(item)) + if item.tail and item.tail.strip(): + messages.append(ChatMessageContent(role=AuthorRole.USER, content=unescape(item.tail.strip()))) elif item.tag == CHAT_HISTORY_TAG: for message in item: messages.append(ChatMessageContent.from_element(message)) - if item.tail and item.tail.strip(): - messages.append(ChatMessageContent(role=AuthorRole.USER, content=unescape(item.tail.strip()))) + if item.tail and item.tail.strip(): + messages.append(ChatMessageContent(role=AuthorRole.USER, content=unescape(item.tail.strip()))) + else: + # Unrecognized XML element (e.g. HTML tags like

,

, etc.) + # Serialize it back to string and append to the last message's content + # or create a new user message, so that HTML tags in prompts are preserved. + raw = tostring(item, encoding="unicode", short_empty_elements=False) + tail = item.tail or "" + if messages: + messages[-1].content = (messages[-1].content or "") + raw + tail + else: + messages.append(ChatMessageContent(role=AuthorRole.USER, content=raw + tail)) if len(messages) == 1 and messages[0].role == AuthorRole.SYSTEM: messages[0].role = AuthorRole.USER return cls(messages=messages) From b61a78f9aabf99dff3c0023790817768a396e8ce Mon Sep 17 00:00:00 2001 From: "Ethan T." Date: Fri, 13 Mar 2026 13:36:32 +0800 Subject: [PATCH 2/2] test: add regression tests for HTML tags in prompt templates Add tests verifying that HTML tags like

, , in prompt templates are preserved when parsed by from_rendered_prompt(). Covers the scenario reported in microsoft#13632. --- .../tests/unit/contents/test_chat_history.py | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/python/tests/unit/contents/test_chat_history.py b/python/tests/unit/contents/test_chat_history.py index ac41949a7325..ceb06f487f0a 100644 --- a/python/tests/unit/contents/test_chat_history.py +++ b/python/tests/unit/contents/test_chat_history.py @@ -628,3 +628,42 @@ def __str__(self) -> str: ]) chat_history.add_tool_message([FunctionResultContent(id="test1", result=custom_result)]) assert "CustomResultTestValue" in chat_history.serialize() + + +def test_chat_history_from_rendered_prompt_with_html_tags(): + """Test that HTML tags like

in prompts are preserved and not silently dropped. + + Regression test for https://github.com/microsoft/semantic-kernel/issues/13632 + """ + rendered = 'Translate following message from English language into the Spanish language - "

What is your name?

"' + chat_history = ChatHistory.from_rendered_prompt(rendered) + assert len(chat_history.messages) == 1 + assert chat_history.messages[0].role == AuthorRole.USER + assert "

" in chat_history.messages[0].content + assert "What is your name?" in chat_history.messages[0].content + assert "

" in chat_history.messages[0].content + + +def test_chat_history_from_rendered_prompt_with_multiple_html_tags(): + """Test that multiple HTML tags in prompts are preserved.""" + rendered = "Here is bold and italic text" + chat_history = ChatHistory.from_rendered_prompt(rendered) + assert len(chat_history.messages) == 1 + assert chat_history.messages[0].role == AuthorRole.USER + assert "bold" in chat_history.messages[0].content + assert "italic" in chat_history.messages[0].content + + +def test_chat_history_from_rendered_prompt_html_with_message_tags(): + """Test that HTML tags work alongside recognized message tags.""" + rendered = 'You are helpfulTranslate:

Hello

please' + chat_history = ChatHistory.from_rendered_prompt(rendered) + assert chat_history.messages[0].role == AuthorRole.SYSTEM + assert chat_history.messages[0].content == "You are helpful" + # The HTML content and surrounding text should be preserved + found_html = False + for msg in chat_history.messages[1:]: + if "

" in (msg.content or "") and "Hello" in (msg.content or ""): + found_html = True + break + assert found_html, f"HTML tag

Hello

not found in messages: {[m.content for m in chat_history.messages]}"