From ac06f7fd09f178f37a342c1c09ec0cb95076a753 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E8=80=80=E9=91=AB=28Huang=20Yaoxin=29?= <174019195+YaoxinHuang@users.noreply.github.com> Date: Sun, 20 Sep 2026 23:12:32 +0800 Subject: [PATCH 1/2] Preserve aliased messages when replacing chat history --- python/semantic_kernel/contents/chat_history.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/python/semantic_kernel/contents/chat_history.py b/python/semantic_kernel/contents/chat_history.py index a773c232c7bc..58600ba4e911 100644 --- a/python/semantic_kernel/contents/chat_history.py +++ b/python/semantic_kernel/contents/chat_history.py @@ -300,12 +300,13 @@ def extend(self, messages: Iterable[ChatMessageContent]) -> None: def replace(self, messages: Iterable[ChatMessageContent]) -> None: """Replace the chat history with a list of messages. - This calls clear() and then extend(messages=messages). + The iterable is consumed before clearing the history so it can refer to the current messages. Args: messages: The messages to add to the history. Can be a list of ChatMessageContent instances or a ChatHistory itself. """ + messages = list(messages) self.clear() self.extend(messages=messages) From 570ffe2199c31f608a9e20ae449bc7bb32dabca7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E8=80=80=E9=91=AB=28Huang=20Yaoxin=29?= <174019195+YaoxinHuang@users.noreply.github.com> Date: Sun, 20 Sep 2026 23:14:28 +0800 Subject: [PATCH 2/2] Test chat history replacement from aliased iterables --- .../tests/unit/contents/test_chat_history.py | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/python/tests/unit/contents/test_chat_history.py b/python/tests/unit/contents/test_chat_history.py index 3c1b92945296..ef05a45f1282 100644 --- a/python/tests/unit/contents/test_chat_history.py +++ b/python/tests/unit/contents/test_chat_history.py @@ -232,6 +232,34 @@ def test_iter(chat_history: ChatHistory): assert message.content == messages[i] +@pytest.mark.parametrize( + "get_messages", + [lambda history: history, lambda history: history.messages, lambda history: iter(history)], + ids=["history", "messages", "iterator"], +) +def test_replace_from_same_history(chat_history: ChatHistory, get_messages): + chat_history.add_user_message("Question") + chat_history.add_assistant_message("Answer") + expected_messages = list(chat_history) + messages = chat_history.messages + + chat_history.replace(get_messages(chat_history)) + + assert chat_history.messages == expected_messages + assert chat_history.messages is messages + + +def test_replace_from_filtered_history(chat_history: ChatHistory): + chat_history.add_system_message("Instructions") + chat_history.add_user_message("Question") + chat_history.add_assistant_message("Answer") + expected_messages = chat_history.messages[:2] + + chat_history.replace(message for message in chat_history if message.role != AuthorRole.ASSISTANT) + + assert chat_history.messages == expected_messages + + def test_eq(): # Create two instances of ChatHistory chat_history1 = ChatHistory()