# Copyright (c) Microsoft. All rights reserved. import pytest from defusedxml.ElementTree import XML from semantic_kernel.contents.chat_message_content import ChatMessageContent from semantic_kernel.contents.file_reference_content import FileReferenceContent 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.streaming_chat_message_content import StreamingChatMessageContent from semantic_kernel.contents.streaming_text_content import StreamingTextContent from semantic_kernel.contents.text_content import TextContent from semantic_kernel.contents.utils.author_role import AuthorRole from semantic_kernel.contents.utils.finish_reason import FinishReason from semantic_kernel.exceptions.content_exceptions import ContentAdditionException def test_scmc(): message = StreamingChatMessageContent(choice_index=0, role=AuthorRole.USER, content="Hello, world!") assert message.role == AuthorRole.USER assert message.content == "Hello, world!" assert len(message.items) == 1 def test_scmc_str(): message = StreamingChatMessageContent(choice_index=0, role="user", content="Hello, world!") assert str(message) == "Hello, world!" def test_scmc_full(): message = StreamingChatMessageContent( choice_index=0, role=AuthorRole.USER, name="username", content="Hello, world!", inner_content="Hello, world!", encoding="utf-8", ai_model_id="1234", metadata={"test": "test"}, finish_reason="stop", ) assert message.role == AuthorRole.USER assert message.name == "username" assert message.content == "Hello, world!" assert message.finish_reason == FinishReason.STOP assert len(message.items) == 1 def test_scmc_items(): message = StreamingChatMessageContent( choice_index=0, role=AuthorRole.USER, items=[TextContent(text="Hello, world!")] ) assert message.role == AuthorRole.USER assert message.content == "Hello, world!" assert len(message.items) == 1 def test_scmc_items_and_content(): message = StreamingChatMessageContent( choice_index=0, role=AuthorRole.USER, content="text", items=[TextContent(text="Hello, world!")] ) assert message.role == AuthorRole.USER assert message.content == "Hello, world!" assert message.items[0].text == "Hello, world!" assert message.items[1].text == "text" assert len(message.items) == 2 def test_scmc_multiple_items(): message = StreamingChatMessageContent( choice_index=0, role=AuthorRole.SYSTEM, items=[ TextContent(text="Hello, world!"), TextContent(text="Hello, world!"), ], ) assert message.role == AuthorRole.SYSTEM assert message.content == "Hello, world!" assert len(message.items) == 2 def test_scmc_content_set(): message = StreamingChatMessageContent(choice_index=0, role=AuthorRole.USER, content="Hello, world!") assert message.role == AuthorRole.USER assert message.content == "Hello, world!" message.content = "Hello, world to you too!" assert len(message.items) == 1 assert message.items[0].text == "Hello, world to you too!" message.content = "" assert message.items[0].text == "Hello, world to you too!" def test_scmc_content_set_empty(): message = StreamingChatMessageContent(choice_index=0, role=AuthorRole.USER, content="Hello, world!") assert message.role == AuthorRole.USER assert message.content == "Hello, world!" message.items.pop() message.content = "Hello, world to you too!" assert len(message.items) == 1 assert message.items[0].text == "Hello, world to you too!" def test_scmc_to_element(): message = StreamingChatMessageContent(choice_index=0, role=AuthorRole.USER, content="Hello, world!", name=None) element = message.to_element() assert element.tag == "message" assert element.attrib == {"role": "user", "choice_index": "0"} assert element.get("name") is None for child in element: assert child.tag == "text" assert child.text == "Hello, world!" def test_scmc_to_prompt(): message = StreamingChatMessageContent(choice_index=0, role=AuthorRole.USER, content="Hello, world!") prompt = message.to_prompt() assert "Hello, world!" in prompt assert 'choice_index="0"' in prompt assert 'role="user"' in prompt def test_scmc_from_element(): element = StreamingChatMessageContent(choice_index=0, role=AuthorRole.USER, content="Hello, world!").to_element() message = StreamingChatMessageContent.from_element(element) assert message.role == AuthorRole.USER assert message.content == "Hello, world!" assert len(message.items) == 1 def test_scmc_from_element_content(): xml_content = 'Hello, world!' element = XML(text=xml_content) message = StreamingChatMessageContent.from_element(element) assert message.role == AuthorRole.USER assert message.content == "Hello, world!" assert len(message.items) == 1 def test_scmc_from_element_content_missing_choice_index(): xml_content = 'Hello, world!' element = XML(text=xml_content) with pytest.raises(TypeError): StreamingChatMessageContent.from_element(element) @pytest.mark.parametrize( "xml_content, user, text_content, length", [ ('Hello, world!', "user", "Hello, world!", 1), ('Hello, world!', "user", "Hello, world!", 1), ( 'Hello, world!Hello, world!', "user", "Hello, world!Hello, world!", 1, ), ( 'args', # noqa: E501 "assistant", "", 1, ), ( 'function result', # noqa: E501 "tool", "", 1, ), ( 'Hello, world!args', # noqa: E501 "user", "Hello, world!", 2, ), ( 'some random code samplein between texttest', # noqa: E501 "user", "some random code samplein between texttest", 1, # TODO: review this case ), ], ids=["no_tag", "text_tag", "double_text_tag", "function_call", "function_result", "combined", "unknown_tag"], ) def test_scmc_from_element_content_parse(xml_content, user, text_content, length): element = XML(text=xml_content) message = StreamingChatMessageContent.from_element(element) assert message.role.value == user assert str(message) == text_content assert len(message.items) == length def test_scmc_serialize(): message = StreamingChatMessageContent(choice_index=0, role=AuthorRole.USER, content="Hello, world!") dumped = message.model_dump() assert dumped["role"] == AuthorRole.USER assert dumped["items"][0]["text"] == "Hello, world!" def test_scmc_to_dict(): message = StreamingChatMessageContent(choice_index=0, role=AuthorRole.USER, content="Hello, world!") assert message.to_dict() == { "role": "user", "content": "Hello, world!", } def test_scmc_to_dict_keys(): message = StreamingChatMessageContent(choice_index=0, role=AuthorRole.USER, content="Hello, world!") assert message.to_dict(role_key="author", content_key="text") == { "author": "user", "text": "Hello, world!", } @pytest.mark.parametrize( "input_args, expected_dict", [ ({"role": "user", "content": "Hello, world!"}, {"role": "user", "content": "Hello, world!"}), ( {"role": "user", "content": "Hello, world!", "name": "username"}, {"role": "user", "content": "Hello, world!", "name": "username"}, ), ({"role": "user", "items": [TextContent(text="Hello, world!")]}, {"role": "user", "content": "Hello, world!"}), ( {"role": "assistant", "items": [FunctionCallContent(id="test", name="func_name", arguments="args")]}, { "role": "assistant", "tool_calls": [ {"id": "test", "type": "function", "function": {"name": "func_name", "arguments": "args"}} ], }, ), ( {"role": "tool", "items": [FunctionResultContent(id="test", name="func_name", result="result")]}, {"role": "tool", "tool_call_id": "test", "content": "result"}, ), ( { "role": "user", "items": [ TextContent(text="Hello, "), TextContent(text="world!"), ], }, { "role": "user", "content": [{"type": "text", "text": "Hello, "}, {"type": "text", "text": "world!"}], }, ), ], ids=["user_content", "user_with_name", "user_item", "function_call", "function_result", "multiple_items"], ) def test_scmc_to_dict_items(input_args, expected_dict): message = StreamingChatMessageContent(choice_index=0, **input_args) assert message.to_dict() == expected_dict def test_scmc_add(): message1 = StreamingChatMessageContent( choice_index=0, role=AuthorRole.USER, content="Hello, ", inner_content="source1" ) message2 = StreamingChatMessageContent( choice_index=0, role=AuthorRole.USER, content="world!", inner_content="source2" ) combined = message1 + message2 assert combined.role == AuthorRole.USER assert combined.content == "Hello, world!" assert len(combined.items) == 1 assert len(combined.inner_content) == 2 # Make sure the original inner content is preserved assert message1.inner_content == "source1" assert message2.inner_content == "source2" def test_scmc_add_three(): message1 = StreamingChatMessageContent( choice_index=0, role=AuthorRole.USER, content="Hello, ", inner_content="source1" ) message2 = StreamingChatMessageContent( choice_index=0, role=AuthorRole.USER, content="world", inner_content="source2" ) message3 = StreamingChatMessageContent(choice_index=0, role=AuthorRole.USER, content="!", inner_content="source3") combined = message1 + message2 + message3 assert combined.role == AuthorRole.USER assert combined.content == "Hello, world!" assert len(combined.items) == 1 assert len(combined.inner_content) == 3 @pytest.mark.parametrize( "message1, message2", [ ( StreamingChatMessageContent( choice_index=0, role=AuthorRole.USER, items=[StreamingTextContent(choice_index=0, text="Hello, ")], inner_content="source1", ), StreamingChatMessageContent( choice_index=0, role=AuthorRole.USER, items=[FunctionResultContent(id="test", name="test", result="test")], inner_content="source2", ), ), ( StreamingChatMessageContent( choice_index=0, role=AuthorRole.TOOL, items=[FunctionCallContent(id="test1", name="test")], inner_content="source1", ), StreamingChatMessageContent( choice_index=0, role=AuthorRole.TOOL, items=[FunctionCallContent(id="test2", name="test")], inner_content="source2", ), ), ( StreamingChatMessageContent( choice_index=0, role=AuthorRole.USER, items=[StreamingTextContent(text="Hello, ", choice_index=0)] ), StreamingChatMessageContent( choice_index=0, role=AuthorRole.USER, items=[StreamingTextContent(text="world!", choice_index=1)] ), ), ( StreamingChatMessageContent( choice_index=0, role=AuthorRole.USER, items=[StreamingTextContent(text="Hello, ", choice_index=0, ai_model_id="0")], ), StreamingChatMessageContent( choice_index=0, role=AuthorRole.USER, items=[StreamingTextContent(text="world!", choice_index=0, ai_model_id="1")], ), ), ( StreamingChatMessageContent( choice_index=0, role=AuthorRole.USER, items=[StreamingTextContent(text="Hello, ", encoding="utf-8", choice_index=0)], ), StreamingChatMessageContent( choice_index=0, role=AuthorRole.USER, items=[StreamingTextContent(text="world!", encoding="utf-16", choice_index=0)], ), ), ], ids=[ "different_types", "different_fccs", "different_text_content_choice_index", "different_text_content_models", "different_text_content_encoding", ], ) def test_scmc_add_different_items_same_type(message1, message2): combined = message1 + message2 assert len(combined.items) == 2 # Make sure the original items are preserved assert len(message1.items) == 1 assert len(message2.items) == 1 @pytest.mark.parametrize( "message1, message2", [ ( StreamingChatMessageContent(choice_index=0, role=AuthorRole.USER, content="Hello, "), StreamingChatMessageContent(choice_index=0, role=AuthorRole.ASSISTANT, content="world!"), ), ( StreamingChatMessageContent(choice_index=0, role=AuthorRole.USER, content="Hello, "), StreamingChatMessageContent(choice_index=1, role=AuthorRole.USER, content="world!"), ), ( StreamingChatMessageContent(choice_index=0, role=AuthorRole.USER, content="Hello, ", ai_model_id="1234"), StreamingChatMessageContent(choice_index=0, role=AuthorRole.USER, content="world!", ai_model_id="5678"), ), ( StreamingChatMessageContent(choice_index=0, role=AuthorRole.USER, content="Hello, ", encoding="utf-8"), StreamingChatMessageContent(choice_index=0, role=AuthorRole.USER, content="world!", encoding="utf-16"), ), ( StreamingChatMessageContent(choice_index=0, role=AuthorRole.USER, content="Hello, "), ChatMessageContent(role=AuthorRole.USER, content="world!"), ), ], ids=[ "different_roles", "different_index", "different_model", "different_encoding", "different_type", ], ) def test_smsc_add_exception(message1, message2): with pytest.raises(ContentAdditionException): message1 + message2 def test_scmc_bytes(): message = StreamingChatMessageContent(choice_index=0, role=AuthorRole.USER, content="Hello, world!") assert bytes(message) == b"Hello, world!" assert bytes(message.items[0]) == b"Hello, world!" def test_scmc_with_unhashable_types_can_hash(): user_messages = [ StreamingChatMessageContent( role=AuthorRole.USER, items=[ StreamingTextContent(text="Describe this image.", choice_index=0), ImageContent( uri="https://upload.wikimedia.org/wikipedia/commons/thumb/4/47/New_york_times_square-terabass.jpg/1200px-New_york_times_square-terabass.jpg" ), ], choice_index=0, ), StreamingChatMessageContent( role=AuthorRole.USER, items=[ StreamingTextContent(text="What is the main color in this image?", choice_index=0), ImageContent(uri="https://upload.wikimedia.org/wikipedia/commons/5/56/White_shark.jpg"), ], choice_index=0, ), StreamingChatMessageContent( role=AuthorRole.USER, items=[ StreamingTextContent(text="Is there an animal in this image?", choice_index=0), FileReferenceContent(file_id="test_file_id"), ], choice_index=0, ), StreamingChatMessageContent( role=AuthorRole.USER, choice_index=0, ), ] for message in user_messages: assert hash(message) is not None