Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions python/semantic_kernel/contents/chat_history.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 <p>, <div>, 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)
Expand Down
39 changes: 39 additions & 0 deletions python/tests/unit/contents/test_chat_history.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 <p> 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 - "<p>What is your name?</p>"'
chat_history = ChatHistory.from_rendered_prompt(rendered)
assert len(chat_history.messages) == 1
assert chat_history.messages[0].role == AuthorRole.USER
assert "<p>" in chat_history.messages[0].content
assert "What is your name?" in chat_history.messages[0].content
assert "</p>" 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 <b>bold</b> and <i>italic</i> text"
chat_history = ChatHistory.from_rendered_prompt(rendered)
assert len(chat_history.messages) == 1
assert chat_history.messages[0].role == AuthorRole.USER
assert "<b>bold</b>" in chat_history.messages[0].content
assert "<i>italic</i>" 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 = '<message role="system">You are helpful</message>Translate: <p>Hello</p> 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 "<p>" in (msg.content or "") and "Hello" in (msg.content or ""):
found_html = True
break
assert found_html, f"HTML tag <p>Hello</p> not found in messages: {[m.content for m in chat_history.messages]}"
Loading