chore: import upstream snapshot with attribution
CodeQL / Analyze (csharp) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 13:21:23 +08:00
commit b957a53def
5423 changed files with 863745 additions and 0 deletions
@@ -0,0 +1,26 @@
# Copyright (c) Microsoft. All rights reserved.
from unittest.mock import AsyncMock, create_autospec
import pytest
from semantic_kernel.connectors.ai.chat_completion_client_base import ChatCompletionClientBase
from semantic_kernel.connectors.ai.prompt_execution_settings import PromptExecutionSettings
from semantic_kernel.contents.chat_message_content import ChatMessageContent
from semantic_kernel.contents.utils.author_role import AuthorRole
from semantic_kernel.kernel import Kernel
@pytest.fixture
def kernel_with_ai_service():
kernel = create_autospec(Kernel)
mock_ai_service_client = create_autospec(ChatCompletionClientBase)
mock_prompt_execution_settings = create_autospec(PromptExecutionSettings)
mock_prompt_execution_settings.function_choice_behavior = None
kernel.select_ai_service.return_value = (mock_ai_service_client, mock_prompt_execution_settings)
mock_ai_service_client.get_chat_message_contents = AsyncMock(
return_value=[ChatMessageContent(role=AuthorRole.SYSTEM, content="Processed Message")]
)
kernel.plugins = {} # Ensure plugins dict is initialized to avoid AttributeError during tests
return kernel, mock_ai_service_client
@@ -0,0 +1,471 @@
# Copyright (c) Microsoft. All rights reserved.
from collections.abc import AsyncGenerator, Callable
from types import MethodType
from unittest.mock import AsyncMock, create_autospec, patch
import pytest
from pydantic import ValidationError
from semantic_kernel.agents import ChatCompletionAgent
from semantic_kernel.agents.channels.chat_history_channel import ChatHistoryChannel
from semantic_kernel.agents.chat_completion.chat_completion_agent import ChatHistoryAgentThread
from semantic_kernel.connectors.ai.chat_completion_client_base import ChatCompletionClientBase
from semantic_kernel.connectors.ai.open_ai.services.azure_chat_completion import AzureChatCompletion
from semantic_kernel.connectors.ai.open_ai.services.open_ai_chat_completion import OpenAIChatCompletion
from semantic_kernel.connectors.ai.prompt_execution_settings import PromptExecutionSettings
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.history_reducer.chat_history_truncation_reducer import ChatHistoryTruncationReducer
from semantic_kernel.contents.streaming_chat_message_content import StreamingChatMessageContent
from semantic_kernel.contents.streaming_text_content import StreamingTextContent
from semantic_kernel.contents.utils.author_role import AuthorRole
from semantic_kernel.exceptions import KernelServiceNotFoundError
from semantic_kernel.exceptions.agent_exceptions import AgentInvokeException
from semantic_kernel.functions.kernel_arguments import KernelArguments
from semantic_kernel.kernel import Kernel
@pytest.fixture
def mock_streaming_chat_completion_response() -> Callable[..., AsyncGenerator[list[ChatMessageContent], None]]:
async def mock_response(
chat_history: ChatHistory,
settings: PromptExecutionSettings,
kernel: Kernel,
arguments: KernelArguments,
) -> AsyncGenerator[list[ChatMessageContent], None]:
content1 = ChatMessageContent(role=AuthorRole.SYSTEM, content="Processed Message 1")
content2 = ChatMessageContent(role=AuthorRole.TOOL, content="Processed Message 2")
chat_history.messages.append(content1)
chat_history.messages.append(content2)
yield [content1]
yield [content2]
return mock_response
async def test_initialization():
agent = ChatCompletionAgent(
name="TestAgent",
id="test_id",
description="Test Description",
instructions="Test Instructions",
)
assert agent.name == "TestAgent"
assert agent.id == "test_id"
assert agent.description == "Test Description"
assert agent.instructions == "Test Instructions"
async def test_initialization_invalid_name_throws():
with pytest.raises(ValidationError):
_ = ChatCompletionAgent(
name="Test Agent",
id="test_id",
description="Test Description",
instructions="Test Instructions",
)
def test_initialization_with_kernel(kernel: Kernel):
agent = ChatCompletionAgent(
kernel=kernel,
name="TestAgent",
id="test_id",
description="Test Description",
instructions="Test Instructions",
)
assert kernel == agent.kernel
assert agent.name == "TestAgent"
assert agent.id == "test_id"
assert agent.description == "Test Description"
assert agent.instructions == "Test Instructions"
def test_initialization_with_kernel_and_service(kernel: Kernel, azure_openai_unit_test_env, openai_unit_test_env):
kernel.add_service(AzureChatCompletion(service_id="test_azure"))
agent = ChatCompletionAgent(
service=OpenAIChatCompletion(),
kernel=kernel,
name="TestAgent",
id="test_id",
description="Test Description",
instructions="Test Instructions",
)
assert kernel == agent.kernel
assert len(kernel.services) == 2
assert agent.name == "TestAgent"
assert agent.id == "test_id"
assert agent.description == "Test Description"
assert agent.instructions == "Test Instructions"
def test_initialization_with_plugins_via_constructor(custom_plugin_class):
agent = ChatCompletionAgent(
name="TestAgent",
id="test_id",
description="Test Description",
instructions="Test Instructions",
plugins=[custom_plugin_class()],
)
assert agent.name == "TestAgent"
assert agent.id == "test_id"
assert agent.description == "Test Description"
assert agent.instructions == "Test Instructions"
assert agent.kernel.plugins is not None
assert len(agent.kernel.plugins) == 1
def test_initialization_with_service_via_constructor(openai_unit_test_env):
agent = ChatCompletionAgent(
name="TestAgent",
id="test_id",
description="Test Description",
instructions="Test Instructions",
service=OpenAIChatCompletion(),
)
assert agent.name == "TestAgent"
assert agent.id == "test_id"
assert agent.description == "Test Description"
assert agent.instructions == "Test Instructions"
assert agent.service is not None
assert agent.kernel.services["test_chat_model_id"] == agent.service
def test_initialize_chat_history_agent_thread_with_id():
thread = ChatHistoryAgentThread(thread_id="test_thread_id")
assert thread is not None
assert thread.id == "test_thread_id"
def test_initialize_with_base_chat_history():
base_history = ChatHistory()
thread = ChatHistoryAgentThread(chat_history=base_history, thread_id="base_test_thread")
assert thread is not None
assert thread.id == "base_test_thread"
assert isinstance(thread._chat_history, ChatHistory)
assert not isinstance(thread._chat_history, ChatHistoryTruncationReducer)
def test_initialize_with_reducer_chat_history():
reducer = ChatHistoryTruncationReducer(
service=AsyncMock(spec=ChatCompletionClientBase), target_count=10, threshold_count=2
)
thread = ChatHistoryAgentThread(chat_history=reducer, thread_id="reducer_test_thread")
assert thread is not None
assert thread.id == "reducer_test_thread"
assert isinstance(thread._chat_history, ChatHistoryTruncationReducer)
async def test_get_response(kernel_with_ai_service: tuple[Kernel, ChatCompletionClientBase]):
kernel, _ = kernel_with_ai_service
agent = ChatCompletionAgent(
kernel=kernel,
name="TestAgent",
instructions="Test Instructions",
)
thread = ChatHistoryAgentThread()
response = await agent.get_response(messages="test", thread=thread)
assert response.message.content == "Processed Message"
assert response.thread is not None
async def test_get_response_exception(kernel_with_ai_service: tuple[Kernel, ChatCompletionClientBase]):
kernel, mock_ai_service_client = kernel_with_ai_service
mock_ai_service_client.get_chat_message_contents = AsyncMock(return_value=[])
agent = ChatCompletionAgent(
kernel=kernel,
name="TestAgent",
instructions="Test Instructions",
)
thread = ChatHistoryAgentThread()
with pytest.raises(AgentInvokeException):
await agent.get_response(messages="test", thread=thread)
async def test_invoke(kernel_with_ai_service: tuple[Kernel, ChatCompletionClientBase]):
kernel, _ = kernel_with_ai_service
agent = ChatCompletionAgent(
kernel=kernel,
name="TestAgent",
instructions="Test Instructions",
)
thread = ChatHistoryAgentThread()
messages = [message async for message in agent.invoke(messages="test", thread=thread)]
assert len(messages) == 1
assert messages[0].message.content == "Processed Message"
async def test_invoke_emits_tool_call_then_result_then_text(kernel_with_ai_service):
kernel, chat_client = kernel_with_ai_service
agent = ChatCompletionAgent(kernel=kernel, name="TestAgent")
thread = ChatHistoryAgentThread()
call_msg = ChatMessageContent(
role=AuthorRole.ASSISTANT,
items=[FunctionCallContent(id="test-id", name="get_specials", arguments="{}")],
)
result_msg = ChatMessageContent(
role=AuthorRole.TOOL,
items=[FunctionResultContent(id="test-id", name="get_specials", result="Clam Chowder")],
)
final_msg = ChatMessageContent(
role=AuthorRole.ASSISTANT,
content="Clam Chowder is today's soup.",
)
chat_client.get_chat_message_contents = AsyncMock(return_value=[final_msg])
async def fake_drain(self, *_args, **_kwargs):
if not fake_drain.called:
fake_drain.called = True
return [call_msg, result_msg]
return []
fake_drain.called = False
with patch.object(ChatCompletionAgent, "_drain_mutated_messages", new=AsyncMock(side_effect=fake_drain)):
cb_messages: list[ChatMessageContent] = []
async def on_msg(m: ChatMessageContent):
cb_messages.append(m)
messages = [
m
async for m in agent.invoke(
messages="What's the special soup?", thread=thread, on_intermediate_message=on_msg
)
]
assert [type(m.items[0]) for m in cb_messages] == [
FunctionCallContent,
FunctionResultContent,
]
assert len(messages) == 1
assert isinstance(messages[0].message, ChatMessageContent)
assert messages[0].message.content.startswith("Clam Chowder")
assert messages[0].message.name == agent.name
async def test_invoke_tool_call_not_added(kernel_with_ai_service: tuple[Kernel, ChatCompletionClientBase]):
kernel, mock_ai_service_client = kernel_with_ai_service
agent = ChatCompletionAgent(
kernel=kernel,
name="TestAgent",
)
thread = ChatHistoryAgentThread()
async def mock_get_chat_message_contents(
chat_history: ChatHistory,
settings: PromptExecutionSettings,
kernel: Kernel,
arguments: KernelArguments,
):
responses = [
ChatMessageContent(
role=AuthorRole.TOOL,
items=[FunctionResultContent(result="Tool Call Result")],
),
]
chat_history.messages.extend(responses)
return responses
mock_ai_service_client.get_chat_message_contents = AsyncMock(side_effect=mock_get_chat_message_contents)
messages = [message async for message in agent.invoke(messages="test", thread=thread)]
assert len(messages) == 1
assert messages[0].message.items[0].result == "Tool Call Result"
assert messages[0].message.role == AuthorRole.TOOL
assert messages[0].message.name == "TestAgent"
thread: ChatHistoryAgentThread = messages[-1].thread
thread_messages = [message async for message in thread.get_messages()]
assert len(thread_messages) == 2
assert thread_messages[0].content == "test"
assert thread_messages[1].items[0].result == "Tool Call Result"
assert thread_messages[1].name == "TestAgent"
assert thread_messages[1].role == AuthorRole.TOOL
async def test_invoke_no_service_throws(kernel: Kernel):
agent = ChatCompletionAgent(kernel=kernel, name="TestAgent")
with pytest.raises(KernelServiceNotFoundError):
async for _ in agent.invoke(messages="test", thread=None):
pass
async def test_invoke_stream(kernel_with_ai_service: tuple[Kernel, ChatCompletionClientBase]):
kernel, _ = kernel_with_ai_service
agent = ChatCompletionAgent(kernel=kernel, name="TestAgent")
thread = ChatHistoryAgentThread()
with patch(
"semantic_kernel.connectors.ai.chat_completion_client_base.ChatCompletionClientBase.get_streaming_chat_message_contents",
return_value=AsyncMock(),
) as mock:
mock.return_value.__aiter__.return_value = [
[ChatMessageContent(role=AuthorRole.USER, content="Initial Message")]
]
async for response in agent.invoke_stream(messages="Initial Message", thread=thread):
assert response.message.role == AuthorRole.USER
assert response.message.content == "Initial Message"
async def test_invoke_stream_emits_tool_call_then_result_then_text(kernel_with_ai_service):
kernel, chat_client = kernel_with_ai_service
agent = ChatCompletionAgent(kernel=kernel, name="TestAgent")
thread = ChatHistoryAgentThread()
call_msg = ChatMessageContent(
role=AuthorRole.ASSISTANT,
items=[FunctionCallContent(id="test-id", name="get_specials", arguments="{}")],
)
result_msg = ChatMessageContent(
role=AuthorRole.TOOL,
items=[FunctionResultContent(id="test-id", name="get_specials", result="Clam Chowder")],
)
text_msg = StreamingChatMessageContent(
role=AuthorRole.ASSISTANT,
content="Clam Chowder is today's soup.",
items=[StreamingTextContent(text="Clam Chowder is today's soup.", choice_index=0)],
choice_index=0,
)
async def fake_stream(*_args, **_kwargs):
yield [StreamingChatMessageContent(role=AuthorRole.ASSISTANT, content="", items=[], choice_index=0)]
yield [text_msg]
chat_client.get_streaming_chat_message_contents = MethodType(fake_stream, chat_client)
async def fake_drain(self, *_args, **_kwargs):
if not fake_drain.called:
fake_drain.called = True
return [call_msg, result_msg]
return []
fake_drain.called = False
with patch.object(ChatCompletionAgent, "_drain_mutated_messages", new=AsyncMock(side_effect=fake_drain)):
cb_messages: list[ChatMessageContent] = []
async def on_msg(m: ChatMessageContent):
cb_messages.append(m)
yielded_text: list[StreamingChatMessageContent] = []
async for resp in agent.invoke_stream(
messages="What's the special soup?",
thread=thread,
on_intermediate_message=on_msg,
):
yielded_text.append(resp.message)
assert [type(m.items[0]) for m in cb_messages] == [
FunctionCallContent,
FunctionResultContent,
]
assert len(yielded_text) == 1
assert isinstance(yielded_text[0], StreamingChatMessageContent)
assert yielded_text[0].content.startswith("Clam Chowder")
assert yielded_text[0].name == agent.name
async def test_invoke_stream_tool_call_added(
kernel_with_ai_service: tuple[Kernel, ChatCompletionClientBase],
mock_streaming_chat_completion_response,
):
kernel, mock_ai_service_client = kernel_with_ai_service
agent = ChatCompletionAgent(kernel=kernel, name="TestAgent")
thread = ChatHistoryAgentThread()
mock_ai_service_client.get_streaming_chat_message_contents = mock_streaming_chat_completion_response
async for response in agent.invoke_stream(messages="Initial Message", thread=thread):
assert response.message.role in [AuthorRole.SYSTEM, AuthorRole.TOOL]
assert response.message.content in ["Processed Message 1", "Processed Message 2"]
async def test_invoke_stream_no_service_throws(kernel: Kernel):
agent = ChatCompletionAgent(kernel=kernel, name="TestAgent")
thread = ChatHistoryAgentThread()
with pytest.raises(KernelServiceNotFoundError):
async for _ in agent.invoke_stream(messages="test", thread=thread):
pass
def test_get_channel_keys():
agent = ChatCompletionAgent()
keys = agent.get_channel_keys()
for key in keys:
assert isinstance(key, str)
async def test_create_channel():
agent = ChatCompletionAgent()
channel = await agent.create_channel()
assert isinstance(channel, ChatHistoryChannel)
async def test_prepare_agent_chat_history_with_formatted_instructions():
agent = ChatCompletionAgent(
name="TestAgent", id="test_id", description="Test Description", instructions="Test Instructions"
)
with patch.object(
ChatCompletionAgent, "format_instructions", new=AsyncMock(return_value="Formatted instructions for testing")
) as mock_format_instructions:
dummy_kernel = create_autospec(Kernel)
dummy_args = KernelArguments(param="value")
user_message = ChatMessageContent(role=AuthorRole.USER, content="User message")
history = ChatHistory(messages=[user_message])
result_history = await agent._prepare_agent_chat_history(history, dummy_kernel, dummy_args)
mock_format_instructions.assert_awaited_once_with(dummy_kernel, dummy_args)
assert len(result_history.messages) == 2
system_message = result_history.messages[0]
assert system_message.role == AuthorRole.SYSTEM
assert system_message.content == "Formatted instructions for testing"
assert system_message.name == agent.name
assert result_history.messages[1] == user_message
async def test_prepare_agent_chat_history_without_formatted_instructions():
agent = ChatCompletionAgent(
name="TestAgent", id="test_id", description="Test Description", instructions="Test Instructions"
)
with patch.object(
ChatCompletionAgent, "format_instructions", new=AsyncMock(return_value=None)
) as mock_format_instructions:
dummy_kernel = create_autospec(Kernel)
dummy_args = KernelArguments(param="value")
user_message = ChatMessageContent(role=AuthorRole.USER, content="User message")
history = ChatHistory(messages=[user_message])
result_history = await agent._prepare_agent_chat_history(history, dummy_kernel, dummy_args)
mock_format_instructions.assert_awaited_once_with(dummy_kernel, dummy_args)
assert len(result_history.messages) == 1
assert result_history.messages[0] == user_message
@@ -0,0 +1,252 @@
# Copyright (c) Microsoft. All rights reserved.
from collections.abc import AsyncIterable
from unittest.mock import AsyncMock
import pytest
from semantic_kernel.agents.agent import AgentResponseItem
from semantic_kernel.agents.channels.chat_history_channel import ChatHistoryChannel
from semantic_kernel.contents.chat_message_content import ChatMessageContent
from semantic_kernel.contents.file_reference_content import FileReferenceContent
from semantic_kernel.contents.function_result_content import FunctionResultContent
from semantic_kernel.contents.streaming_file_reference_content import StreamingFileReferenceContent
from semantic_kernel.contents.utils.author_role import AuthorRole
@pytest.fixture
def chat_history_channel() -> ChatHistoryChannel:
from semantic_kernel.agents.chat_completion.chat_completion_agent import ChatHistoryAgentThread
ChatHistoryChannel.model_rebuild()
thread = ChatHistoryAgentThread()
return ChatHistoryChannel(thread=thread)
class MockChatHistoryHandler:
"""Mock agent to test chat history handling"""
async def invoke(self, history: list[ChatMessageContent]) -> AsyncIterable[ChatMessageContent]:
for message in history:
yield ChatMessageContent(role=AuthorRole.SYSTEM, content=f"Processed: {message.content}")
async def invoke_stream(self, history: list[ChatMessageContent]) -> AsyncIterable[ChatMessageContent]:
for message in history:
yield ChatMessageContent(role=AuthorRole.SYSTEM, content=f"Processed: {message.content}")
async def reduce_history(self, history: list[ChatMessageContent]) -> list[ChatMessageContent]:
return history
class MockNonChatHistoryHandler:
"""Mock agent to test incorrect instance handling."""
id: str = "mock_non_chat_history_handler"
class AsyncIterableMock:
def __init__(self, async_gen):
self.async_gen = async_gen
def __aiter__(self):
return self.async_gen()
async def test_invoke(chat_history_channel):
channel = chat_history_channel
agent = AsyncMock(spec=MockChatHistoryHandler)
async def mock_invoke(history: list[ChatMessageContent]):
for message in history:
yield AgentResponseItem(
message=ChatMessageContent(role=AuthorRole.SYSTEM, content=f"Processed: {message.content}"),
thread=channel.thread,
)
agent.invoke.return_value = AsyncIterableMock(
lambda: mock_invoke([ChatMessageContent(role=AuthorRole.USER, content="Initial message")])
)
initial_message = ChatMessageContent(role=AuthorRole.USER, content="Initial message")
channel.messages.append(initial_message)
received_messages = []
async for is_visible, message in channel.invoke(agent, thread=channel.thread):
received_messages.append(message)
assert is_visible
assert len(received_messages) == 1
assert "Processed: Initial message" in received_messages[0].content
async def test_invoke_stream(chat_history_channel):
channel = chat_history_channel
agent = AsyncMock(spec=MockChatHistoryHandler)
async def mock_invoke(history: list[ChatMessageContent]):
for message in history:
msg = AgentResponseItem(
message=ChatMessageContent(role=AuthorRole.SYSTEM, content=f"Processed: {message.content}"),
thread=channel.thread,
)
yield msg
channel.add_message(msg.message)
agent.invoke_stream.return_value = AsyncIterableMock(
lambda: mock_invoke([ChatMessageContent(role=AuthorRole.USER, content="Initial message")])
)
initial_message = ChatMessageContent(role=AuthorRole.USER, content="Initial message")
channel.messages.append(initial_message)
received_messages = []
async for message in channel.invoke_stream(agent, thread=channel.thread, messages=received_messages):
assert message is not None
assert len(received_messages) == 1
assert "Processed: Initial message" in received_messages[0].content
async def test_invoke_leftover_in_queue(chat_history_channel):
channel = chat_history_channel
agent = AsyncMock(spec=MockChatHistoryHandler)
async def mock_invoke(history: list[ChatMessageContent]):
for message in history:
yield AgentResponseItem(
message=ChatMessageContent(role=AuthorRole.SYSTEM, content=f"Processed: {message.content}"),
thread=channel.thread,
)
yield AgentResponseItem(
message=ChatMessageContent(
role=AuthorRole.SYSTEM,
content="Final message",
items=[FunctionResultContent(id="test_id", result="test")],
),
thread=channel.thread,
)
agent.invoke.return_value = AsyncIterableMock(
lambda: mock_invoke([
ChatMessageContent(
role=AuthorRole.USER,
content="Initial message",
items=[FunctionResultContent(id="test_id", result="test")],
)
])
)
initial_message = ChatMessageContent(role=AuthorRole.USER, content="Initial message")
channel.messages.append(initial_message)
received_messages = []
async for is_visible, message in channel.invoke(agent, thread=channel.thread):
received_messages.append(message)
assert is_visible
if len(received_messages) >= 3:
break
assert len(received_messages) == 3
assert "Processed: Initial message" in received_messages[0].content
assert "Final message" in received_messages[2].content
assert received_messages[2].items[0].id == "test_id"
async def test_receive(chat_history_channel):
channel = chat_history_channel
history = [
ChatMessageContent(role=AuthorRole.SYSTEM, content="test message 1"),
ChatMessageContent(role=AuthorRole.USER, content="test message 2"),
]
await channel.receive(history)
assert len(channel.messages) == 2
assert channel.messages[0].content == "test message 1"
assert channel.messages[0].role == AuthorRole.SYSTEM
assert channel.messages[1].content == "test message 2"
assert channel.messages[1].role == AuthorRole.USER
async def test_get_history(chat_history_channel):
channel = chat_history_channel
history = [
ChatMessageContent(role=AuthorRole.SYSTEM, content="test message 1"),
ChatMessageContent(role=AuthorRole.USER, content="test message 2"),
]
channel.messages.extend(history)
messages = [message async for message in channel.get_history()]
assert len(messages) == 2
assert messages[0].content == "test message 2"
assert messages[0].role == AuthorRole.USER
assert messages[1].content == "test message 1"
assert messages[1].role == AuthorRole.SYSTEM
async def test_reset_history(chat_history_channel):
channel = chat_history_channel
history = [
ChatMessageContent(role=AuthorRole.SYSTEM, content="test message 1"),
ChatMessageContent(role=AuthorRole.USER, content="test message 2"),
]
channel.messages.extend(history)
messages = [message async for message in channel.get_history()]
assert len(messages) == 2
assert messages[0].content == "test message 2"
assert messages[0].role == AuthorRole.USER
assert messages[1].content == "test message 1"
assert messages[1].role == AuthorRole.SYSTEM
await channel.reset()
assert len(channel.messages) == 0
async def test_receive_skips_file_references(chat_history_channel):
channel = chat_history_channel
file_ref_item = FileReferenceContent()
streaming_file_ref_item = StreamingFileReferenceContent()
normal_item_1 = FunctionResultContent(id="test_id", result="normal content 1")
normal_item_2 = FunctionResultContent(id="test_id_2", result="normal content 2")
msg_with_file_only = ChatMessageContent(
role=AuthorRole.USER,
content="Normal message set as TextContent",
items=[file_ref_item],
)
msg_with_mixed = ChatMessageContent(
role=AuthorRole.USER,
content="Mixed content message",
items=[streaming_file_ref_item, normal_item_1],
)
msg_with_normal = ChatMessageContent(
role=AuthorRole.USER,
content="Normal message",
items=[normal_item_2],
)
history = [msg_with_file_only, msg_with_mixed, msg_with_normal]
await channel.receive(history)
assert len(channel.messages) == 3
assert channel.messages[0].content == "Normal message set as TextContent"
assert len(channel.messages[0].items) == 1
assert channel.messages[1].content == "Mixed content message"
assert len(channel.messages[0].items) == 1
assert channel.messages[1].items[0].result == "normal content 1"
assert channel.messages[2].content == "Normal message"
assert len(channel.messages[2].items) == 2
assert channel.messages[2].items[0].result == "normal content 2"
assert channel.messages[2].items[1].text == "Normal message"