Files
microsoft--semantic-kernel/python/tests/unit/contents/test_chat_history.py
T
wehub-resource-sync b957a53def
CodeQL / Analyze (csharp) (push) Waiting to run
CodeQL / Analyze (python) (push) Waiting to run
chore: import upstream snapshot with attribution
2026-07-13 13:21:23 +08:00

673 lines
28 KiB
Python

# Copyright (c) Microsoft. All rights reserved.
import pytest
from openai.types.chat.chat_completion import ChatCompletion, Choice
from openai.types.chat.chat_completion_message import ChatCompletionMessage
from semantic_kernel.contents.chat_history import ChatHistory
from semantic_kernel.contents.chat_message_content import ChatMessageContent
from semantic_kernel.contents.function_call_content import FunctionCallContent
from semantic_kernel.contents.function_result_content import FunctionResultContent
from semantic_kernel.contents.image_content import ImageContent
from semantic_kernel.contents.text_content import TextContent
from semantic_kernel.contents.utils.author_role import AuthorRole
from semantic_kernel.exceptions import ContentInitializationError
from semantic_kernel.functions.kernel_arguments import KernelArguments
from semantic_kernel.kernel import Kernel
from semantic_kernel.prompt_template.input_variable import InputVariable
from semantic_kernel.prompt_template.kernel_prompt_template import KernelPromptTemplate
from semantic_kernel.prompt_template.prompt_template_config import PromptTemplateConfig
@pytest.fixture
def mock_chat_completion_response() -> ChatCompletion:
return ChatCompletion(
id="test_id",
choices=[
Choice(index=0, message=ChatCompletionMessage(content="test", role="assistant"), finish_reason="stop")
],
created=0,
model="test",
object="chat.completion",
)
def test_init_with_system_message_only():
system_msg = "test message"
chat_history = ChatHistory(system_message=system_msg)
assert len(chat_history.messages) == 1
assert chat_history.messages[0].content == system_msg
def test_init_with_messages_only():
msgs = [ChatMessageContent(role=AuthorRole.USER, content=f"Message {i}") for i in range(3)]
chat_history = ChatHistory(messages=msgs)
assert chat_history.messages == msgs, "Chat history should contain exactly the provided messages"
def test_init_with_messages_and_system_message():
system_msg = "a test system prompt"
msgs = [ChatMessageContent(role=AuthorRole.USER, content=f"Message {i}") for i in range(3)]
chat_history = ChatHistory(messages=msgs, system_message=system_msg)
assert chat_history.messages[0].role == AuthorRole.SYSTEM, "System message should be the first in history"
assert chat_history.messages[0].content == system_msg, "System message should be the first in history"
assert chat_history.messages[1:] == msgs, "Remaining messages should follow the system message"
def test_init_without_messages_and_system_message(chat_history: ChatHistory):
assert chat_history.messages == [], "Chat history should be empty if no messages and system_message are provided"
def test_add_system_message(chat_history: ChatHistory):
content = "System message"
chat_history.add_system_message(content)
assert chat_history.messages[-1].content == content
assert chat_history.messages[-1].role == AuthorRole.SYSTEM
def test_add_system_message_item(chat_history: ChatHistory):
content = [TextContent(text="System message")]
chat_history.add_system_message(content)
assert chat_history.messages[-1].content == str(content[0])
assert chat_history.messages[-1].role == AuthorRole.SYSTEM
def test_add_system_message_at_init():
content = "System message"
chat_history = ChatHistory(system_message=content)
assert chat_history.messages[-1].content == content
assert chat_history.messages[-1].role == AuthorRole.SYSTEM
def test_add_user_message(chat_history: ChatHistory):
content = "User message"
chat_history.add_user_message(content)
assert chat_history.messages[-1].content == content
assert chat_history.messages[-1].role == AuthorRole.USER
def test_add_user_message_list(chat_history: ChatHistory):
content = [TextContent(text="User message")]
chat_history.add_user_message(content)
assert chat_history.messages[-1].content == content[0].text
assert chat_history.messages[-1].role == AuthorRole.USER
def test_add_assistant_message(chat_history: ChatHistory):
content = "Assistant message"
chat_history.add_assistant_message(content)
assert chat_history.messages[-1].content == content
assert chat_history.messages[-1].role == AuthorRole.ASSISTANT
def test_add_assistant_message_list(chat_history: ChatHistory):
content = [TextContent(text="Assistant message")]
chat_history.add_assistant_message(content)
assert chat_history.messages[-1].content == content[0].text
assert chat_history.messages[-1].role == AuthorRole.ASSISTANT
def test_add_tool_message_raises_without_tool_call_id(chat_history: ChatHistory):
content = "Tool message"
with pytest.raises(ContentInitializationError):
chat_history.add_tool_message(content)
def test_add_tool_message(chat_history: ChatHistory):
content = "Tool message"
chat_history.add_tool_message(content, tool_call_id="call_123")
def test_add_tool_message_to_dict_succeeds(chat_history: ChatHistory):
content = "Tool message"
chat_history.add_tool_message(content, tool_call_id="call_123", function_name="test_function")
assert chat_history.messages[-1].role == AuthorRole.TOOL
msg = chat_history.messages[-1]
assert isinstance(msg.items[0], FunctionResultContent)
assert msg.items[0].function_name == "test_function"
result = msg.to_dict()
assert result["content"] == content
assert result["role"] == AuthorRole.TOOL
assert result["tool_call_id"] == "call_123"
def test_add_tool_message_list(chat_history: ChatHistory):
content = [FunctionResultContent(id="test", result="Tool message")]
chat_history.add_tool_message(content)
assert chat_history.messages[-1].items[0].result == content[0].result
assert chat_history.messages[-1].role == AuthorRole.TOOL
def test_add_message(chat_history: ChatHistory):
content = "Test message"
role = AuthorRole.USER
encoding = "utf-8"
chat_history.add_message(message={"role": role, "content": content}, encoding=encoding, metadata={"test": "test"})
assert chat_history.messages[-1].content == content
assert chat_history.messages[-1].role == role
assert chat_history.messages[-1].encoding == encoding
assert chat_history.messages[-1].metadata == {"test": "test"}
def test_add_message_with_image(chat_history: ChatHistory):
content = "Test message"
role = AuthorRole.USER
encoding = "utf-8"
chat_history.add_message(
ChatMessageContent(
role=role,
items=[
TextContent(text=content),
ImageContent(uri="https://test/"),
],
encoding=encoding,
)
)
assert chat_history.messages[-1].content == content
assert chat_history.messages[-1].role == role
assert chat_history.messages[-1].encoding == encoding
assert str(chat_history.messages[-1].items[1].uri) == "https://test/"
def test_add_message_invalid_message(chat_history: ChatHistory):
content = "Test message"
with pytest.raises(ContentInitializationError):
chat_history.add_message(message={"content": content})
def test_add_message_invalid_type(chat_history: ChatHistory):
content = "Test message"
with pytest.raises(ContentInitializationError):
chat_history.add_message(message=content)
def test_remove_message(chat_history: ChatHistory):
content = "Message to remove"
role = AuthorRole.USER
encoding = "utf-8"
message = ChatMessageContent(role=role, content=content, encoding=encoding)
chat_history.messages.append(message)
assert chat_history.remove_message(message) is True
assert message not in chat_history.messages
def test_remove_message_invalid(chat_history: ChatHistory):
content = "Message to remove"
role = AuthorRole.USER
encoding = "utf-8"
message = ChatMessageContent(role=role, content=content, encoding=encoding)
chat_history.messages.append(message)
assert chat_history.remove_message("random") is False
def test_len(chat_history: ChatHistory):
content = "Message"
chat_history.add_user_message(content)
chat_history.add_system_message(content)
assert len(chat_history) == 2
def test_getitem(chat_history: ChatHistory):
content = "Message for index"
chat_history.add_user_message(content)
assert chat_history[0].content == content
def test_contains(chat_history: ChatHistory):
content = "Message to check"
role = AuthorRole.USER
encoding = "utf-8"
message = ChatMessageContent(role=role, content=content, encoding=encoding)
chat_history.messages.append(message)
assert message in chat_history
def test_iter(chat_history: ChatHistory):
messages = ["Message 1", "Message 2"]
for msg in messages:
chat_history.add_user_message(msg)
for i, message in enumerate(chat_history):
assert message.content == messages[i]
def test_eq():
# Create two instances of ChatHistory
chat_history1 = ChatHistory()
chat_history2 = ChatHistory()
# Populate both instances with the same set of messages
messages = [("Message 1", AuthorRole.USER), ("Message 2", AuthorRole.ASSISTANT)]
for content, role in messages:
chat_history1.add_message({"role": role, "content": content})
chat_history2.add_message({"role": role, "content": content})
# Assert that the two instances are considered equal
assert chat_history1 == chat_history2
# Additionally, test inequality by adding an extra message to one of the histories
chat_history1.add_user_message("Extra message")
assert chat_history1 != chat_history2
def test_eq_invalid(chat_history: ChatHistory):
# Populate both instances with the same set of messages
messages = [("Message 1", AuthorRole.USER), ("Message 2", AuthorRole.ASSISTANT)]
for content, role in messages:
chat_history.add_message({"role": role, "content": content})
assert chat_history != "other"
def test_dump():
system_msg = "a test system prompt"
chat_history = ChatHistory(
messages=[ChatMessageContent(role=AuthorRole.USER, content="Message")], system_message=system_msg
)
dump = chat_history.model_dump(exclude_none=True)
assert dump is not None
assert dump["messages"][0]["role"] == AuthorRole.SYSTEM
assert dump["messages"][0]["items"][0]["text"] == system_msg
assert dump["messages"][1]["role"] == AuthorRole.USER
assert dump["messages"][1]["items"][0]["text"] == "Message"
def test_serialize():
system_msg = "a test system prompt"
chat_history = ChatHistory(
messages=[
ChatMessageContent(
role=AuthorRole.USER,
items=[
TextContent(text="Message"),
ImageContent(uri="http://test.com/image.jpg"),
ImageContent(data_uri="data:image/jpeg;base64,dGVzdF9kYXRh"),
],
)
],
system_message=system_msg,
)
json_str = chat_history.serialize()
assert json_str is not None
assert (
json_str
== '{\n "messages": [\n {\n "metadata": {},\n "content_type": "message",\n "role": "system",\n "items": [\n {\n "metadata": {},\n "content_type": "text",\n "text": "a test system prompt"\n }\n ]\n },\n {\n "metadata": {},\n "content_type": "message",\n "role": "user",\n "items": [\n {\n "metadata": {},\n "content_type": "text",\n "text": "Message"\n },\n {\n "metadata": {},\n "content_type": "image",\n "uri": "http://test.com/image.jpg",\n "data_uri": ""\n },\n {\n "metadata": {},\n "content_type": "image",\n "data_uri": "data:image/jpeg;base64,dGVzdF9kYXRh"\n }\n ]\n }\n ]\n}' # noqa: E501
)
def test_serialize_and_deserialize_to_chat_history(mock_chat_completion_response: ChatCompletion):
system_msg = "a test system prompt"
msgs = [
ChatMessageContent(
role=AuthorRole.USER,
content=f"Message {i}",
inner_content=mock_chat_completion_response,
)
for i in range(3)
]
chat_history = ChatHistory(messages=msgs, system_message=system_msg)
json_str = chat_history.serialize()
new_chat_history = ChatHistory.restore_chat_history(json_str)
assert len(new_chat_history.messages) == len(chat_history.messages)
for original_msg, restored_msg in zip(chat_history.messages, new_chat_history.messages):
assert original_msg.role == restored_msg.role
assert original_msg.content == restored_msg.content
def test_deserialize_invalid_json_raises_exception():
invalid_json = "invalid json"
with pytest.raises(ContentInitializationError):
ChatHistory.restore_chat_history(invalid_json)
def test_chat_history_to_prompt_empty(chat_history: ChatHistory):
prompt = str(chat_history)
assert prompt == "<chat_history />"
def test_chat_history_to_prompt(chat_history: ChatHistory):
chat_history.add_system_message("I am an AI assistant")
chat_history.add_user_message("What can you do?")
prompt = chat_history.to_prompt()
assert (
prompt
== '<chat_history><message role="system"><text>I am an AI assistant</text></message><message role="user"><text>What can you do?</text></message></chat_history>' # noqa: E501
)
def test_chat_history_from_rendered_prompt_empty():
rendered = ""
chat_history = ChatHistory.from_rendered_prompt(rendered)
assert chat_history.messages == []
def test_chat_history_from_rendered_prompt():
rendered = '<message role="system"><text>I am an AI assistant</text></message><message role="user"><text>What can you do?</text></message>' # noqa: E501
chat_history = ChatHistory.from_rendered_prompt(rendered)
assert chat_history.messages[0].content == "I am an AI assistant"
assert chat_history.messages[0].role == AuthorRole.SYSTEM
assert chat_history.messages[1].content == "What can you do?"
assert chat_history.messages[1].role == AuthorRole.USER
def test_chat_history_from_rendered_prompt_multi_line():
rendered = """<message role="system">I am an AI assistant
and I can do
stuff</message>
<message role="user">What can you do?</message>"""
chat_history = ChatHistory.from_rendered_prompt(rendered)
assert chat_history.messages[0].content == "I am an AI assistant\nand I can do \nstuff"
assert chat_history.messages[0].role == AuthorRole.SYSTEM
assert chat_history.messages[1].content == "What can you do?"
assert chat_history.messages[1].role == AuthorRole.USER
async def test_template_unsafe(chat_history: ChatHistory):
chat_history.add_assistant_message("I am an AI assistant")
template = "system stuff{{$chat_history}}{{$input}}"
rendered = await KernelPromptTemplate(
prompt_template_config=PromptTemplateConfig(name="test", description="test", template=template),
allow_dangerously_set_content=True,
).render(
kernel=Kernel(),
arguments=KernelArguments(chat_history=chat_history, input="What can you do?"),
)
assert "system stuff" in rendered
assert "I am an AI assistant" in rendered
assert "What can you do?" in rendered
chat_history_2 = ChatHistory.from_rendered_prompt(rendered)
assert chat_history_2.messages[0].content == "system stuff"
assert chat_history_2.messages[0].role == AuthorRole.SYSTEM
assert chat_history_2.messages[1].content == "I am an AI assistant"
assert chat_history_2.messages[1].role == AuthorRole.ASSISTANT
assert chat_history_2.messages[2].content == "What can you do?"
assert chat_history_2.messages[2].role == AuthorRole.USER
async def test_template_safe(chat_history: ChatHistory):
chat_history.add_assistant_message("I am an AI assistant")
template = "system stuff{{$chat_history}}{{$input}}"
rendered = await KernelPromptTemplate(
prompt_template_config=PromptTemplateConfig(name="test", description="test", template=template),
allow_dangerously_set_content=True,
).render(
kernel=Kernel(),
arguments=KernelArguments(chat_history=chat_history, input="What can you do?"),
)
assert "system stuff" in rendered
assert "I am an AI assistant" in rendered
assert "What can you do?" in rendered
chat_history_2 = ChatHistory.from_rendered_prompt(rendered)
assert chat_history_2.messages[0].content == "system stuff"
assert chat_history_2.messages[0].role == AuthorRole.SYSTEM
assert chat_history_2.messages[1].content == "I am an AI assistant"
assert chat_history_2.messages[1].role == AuthorRole.ASSISTANT
assert chat_history_2.messages[2].content == "What can you do?"
assert chat_history_2.messages[2].role == AuthorRole.USER
async def test_template_two_histories(): # ignore: E501
chat_history1 = ChatHistory()
chat_history1.add_assistant_message("I am an AI assistant")
chat_history2 = ChatHistory()
chat_history2.add_assistant_message("I like to be added later on")
template = "system prompt{{$chat_history1}}{{$input}}{{$chat_history2}}"
rendered = await KernelPromptTemplate(
prompt_template_config=PromptTemplateConfig(name="test", description="test", template=template),
allow_dangerously_set_content=True,
).render(
kernel=Kernel(),
arguments=KernelArguments(chat_history1=chat_history1, chat_history2=chat_history2, input="What can you do?"),
)
assert "I am an AI assistant" in rendered
assert "What can you do?" in rendered
assert "I like to be added later on" in rendered
chat_history_out = ChatHistory.from_rendered_prompt(rendered)
assert chat_history_out.messages[0].content == "system prompt"
assert chat_history_out.messages[0].role == AuthorRole.SYSTEM
assert chat_history_out.messages[1].content == "I am an AI assistant"
assert chat_history_out.messages[1].role == AuthorRole.ASSISTANT
assert chat_history_out.messages[2].content == "What can you do?"
assert chat_history_out.messages[2].role == AuthorRole.USER
assert chat_history_out.messages[3].content == "I like to be added later on"
assert chat_history_out.messages[3].role == AuthorRole.ASSISTANT
async def test_template_two_histories_one_empty():
chat_history1 = ChatHistory()
chat_history2 = ChatHistory()
chat_history2.add_assistant_message("I am an AI assistant")
template = "system prompt{{$chat_history1}}{{$input}}{{$chat_history2}}"
rendered = await KernelPromptTemplate(
prompt_template_config=PromptTemplateConfig(name="test", description="test", template=template),
allow_dangerously_set_content=True,
).render(
kernel=Kernel(),
arguments=KernelArguments(chat_history1=chat_history1, chat_history2=chat_history2, input="What can you do?"),
)
chat_history_out = ChatHistory.from_rendered_prompt(rendered)
assert chat_history_out.messages[0].content == "system prompt"
assert chat_history_out.messages[0].role == AuthorRole.SYSTEM
assert chat_history_out.messages[1].content == "What can you do?"
assert chat_history_out.messages[1].role == AuthorRole.USER
assert chat_history_out.messages[2].content == "I am an AI assistant"
assert chat_history_out.messages[2].role == AuthorRole.ASSISTANT
async def test_template_history_only(chat_history: ChatHistory):
chat_history.add_assistant_message("I am an AI assistant")
template = "{{$chat_history}}"
rendered = await KernelPromptTemplate(
prompt_template_config=PromptTemplateConfig(name="test", description="test", template=template),
allow_dangerously_set_content=True,
).render(kernel=Kernel(), arguments=KernelArguments(chat_history=chat_history))
chat_history_2 = ChatHistory.from_rendered_prompt(rendered)
assert chat_history_2.messages[0].content == "I am an AI assistant"
assert chat_history_2.messages[0].role == AuthorRole.ASSISTANT
async def test_template_without_chat_history():
template = "{{$input}}"
rendered = await KernelPromptTemplate(
prompt_template_config=PromptTemplateConfig(name="test", description="test", template=template)
).render(kernel=Kernel(), arguments=KernelArguments(input="What can you do?"))
assert rendered == "What can you do?"
chat_history = ChatHistory.from_rendered_prompt(rendered)
assert chat_history.messages[0].content == "What can you do?"
assert chat_history.messages[0].role == AuthorRole.USER
async def test_handwritten_xml():
template = '<message role="user">test content</message>'
rendered = await KernelPromptTemplate(
prompt_template_config=PromptTemplateConfig(name="test", description="test", template=template)
).render(kernel=Kernel(), arguments=KernelArguments())
chat_history = ChatHistory.from_rendered_prompt(rendered)
assert chat_history.messages[0].content == "test content"
assert chat_history.messages[0].role == AuthorRole.USER
async def test_empty_text_content_message():
template = '<message role="assistant"><text></text></message><message role="user">test content</message>'
rendered = await KernelPromptTemplate(
prompt_template_config=PromptTemplateConfig(name="test", description="test", template=template)
).render(kernel=Kernel(), arguments=KernelArguments())
chat_history = ChatHistory.from_rendered_prompt(rendered)
assert chat_history.messages[0].role == AuthorRole.ASSISTANT
assert chat_history.messages[1].content == "test content"
assert chat_history.messages[1].role == AuthorRole.USER
async def test_handwritten_xml_invalid():
template = '<message role="user"test content</message>'
rendered = await KernelPromptTemplate(
prompt_template_config=PromptTemplateConfig(name="test", description="test", template=template)
).render(kernel=Kernel(), arguments=KernelArguments())
chat_history = ChatHistory.from_rendered_prompt(rendered)
assert chat_history.messages[0].content == '<message role="user"test content</message>'
assert chat_history.messages[0].role == AuthorRole.USER
async def test_handwritten_xml_as_arg_safe():
template = "{{$input}}"
rendered = await KernelPromptTemplate(
prompt_template_config=PromptTemplateConfig(
name="test",
description="test",
template=template,
),
).render(
kernel=Kernel(),
arguments=KernelArguments(input='<message role="user">test content</message>'),
)
chat_history = ChatHistory.from_rendered_prompt(rendered)
assert chat_history.messages[0].content == '<message role="user">test content</message>'
assert chat_history.messages[0].role == AuthorRole.USER
async def test_handwritten_xml_as_arg_unsafe_template():
template = "{{$input}}"
rendered = await KernelPromptTemplate(
prompt_template_config=PromptTemplateConfig(name="test", description="test", template=template),
allow_dangerously_set_content=True,
).render(
kernel=Kernel(),
arguments=KernelArguments(input='<message role="user">test content</message>'),
)
chat_history = ChatHistory.from_rendered_prompt(rendered)
assert chat_history.messages[0].content == "test content"
assert chat_history.messages[0].role == AuthorRole.USER
async def test_handwritten_xml_as_arg_unsafe_variable():
template = "{{$input}}"
rendered = await KernelPromptTemplate(
prompt_template_config=PromptTemplateConfig(
name="test",
description="test",
template=template,
input_variables=[InputVariable(name="input", allow_dangerously_set_content=True)],
),
).render(
kernel=Kernel(),
arguments=KernelArguments(input='<message role="user">test content</message>'),
)
chat_history = ChatHistory.from_rendered_prompt(rendered)
assert chat_history.messages[0].content == "test content"
assert chat_history.messages[0].role == AuthorRole.USER
async def test_template_empty_history(chat_history: ChatHistory):
template = "system stuff{{$chat_history}}{{$input}}"
rendered = await KernelPromptTemplate(
prompt_template_config=PromptTemplateConfig(name="test", description="test", template=template),
allow_dangerously_set_content=True,
).render(
kernel=Kernel(),
arguments=KernelArguments(chat_history=chat_history, input="What can you do?"),
)
chat_history_2 = ChatHistory.from_rendered_prompt(rendered)
assert chat_history_2.messages[0].content == "system stuff"
assert chat_history_2.messages[0].role == AuthorRole.SYSTEM
assert chat_history_2.messages[1].content == "What can you do?"
assert chat_history_2.messages[1].role == AuthorRole.USER
def test_to_from_file(chat_history: ChatHistory, tmp_path):
chat_history.add_system_message("You are an AI assistant")
chat_history.add_user_message("What is the weather in Seattle?")
chat_history.add_assistant_message([
FunctionCallContent(id="test1", name="WeatherPlugin-GetWeather", arguments='{{ "location": "Seattle" }}')
])
chat_history.add_tool_message([FunctionResultContent(id="test1", result="It is raining")])
chat_history.add_assistant_message("It is raining in Seattle, what else can I help you with?")
file_path = tmp_path / "chat_history.json"
chat_history.store_chat_history_to_file(file_path)
chat_history_2 = ChatHistory.load_chat_history_from_file(file_path)
assert len(chat_history_2.messages) == len(chat_history.messages)
assert chat_history_2.messages[0] == chat_history.messages[0]
assert chat_history_2.messages[1] == chat_history.messages[1]
assert chat_history_2.messages[2] == chat_history.messages[2]
assert chat_history_2.messages[3] == chat_history.messages[3]
assert chat_history_2.messages[4] == chat_history.messages[4]
def test_from_rendered_prompt_preserves_html_p_tag():
"""HTML <p> tags in prompts should be preserved as text, not treated as template tags.
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>What is your name?</p>" in chat_history.messages[0].content
def test_from_rendered_prompt_preserves_multiple_html_tags():
"""Multiple HTML tags in prompts should be preserved as text."""
rendered = "<p>First paragraph</p><div>A div</div>"
chat_history = ChatHistory.from_rendered_prompt(rendered)
assert len(chat_history.messages) == 1
assert "<p>First paragraph</p>" in chat_history.messages[0].content
assert "<div>A div</div>" in chat_history.messages[0].content
def test_from_rendered_prompt_preserves_html_with_text_around():
"""HTML tags surrounded by plain text should preserve all content."""
rendered = "Hello <b>world</b> today"
chat_history = ChatHistory.from_rendered_prompt(rendered)
assert len(chat_history.messages) == 1
assert "Hello" in chat_history.messages[0].content
assert "<b>world</b>" in chat_history.messages[0].content
assert "today" in chat_history.messages[0].content
def test_from_rendered_prompt_sk_tags_still_work_with_html():
"""SK template tags should still be parsed correctly even when HTML tags are present."""
rendered = '<message role="user">Tell me about <b>bold</b> text</message>'
chat_history = ChatHistory.from_rendered_prompt(rendered)
# The <b> tag inside a <message> is handled by ChatMessageContent.from_element
assert len(chat_history.messages) == 1
assert chat_history.messages[0].role == AuthorRole.USER
def test_chat_history_serialize(chat_history: ChatHistory):
class CustomResultClass:
def __init__(self, result):
self.result = result
def __str__(self) -> str:
return self.result
custom_result = CustomResultClass(result="CustomResultTestValue")
chat_history.add_system_message("You are an AI assistant")
chat_history.add_user_message("What is the weather in Seattle?")
chat_history.add_assistant_message([
FunctionCallContent(id="test1", name="WeatherPlugin-GetWeather", arguments='{{ "location": "Seattle" }}')
])
chat_history.add_tool_message([FunctionResultContent(id="test1", result=custom_result)])
assert "CustomResultTestValue" in chat_history.serialize()