Skip to content

Python: Fix ChatHistoryTruncationReducer deleting system prompt#13610

Open
roli-lpci wants to merge 1 commit intomicrosoft:mainfrom
roli-lpci:fix/python-history-reducer-system-prompt-12612
Open

Python: Fix ChatHistoryTruncationReducer deleting system prompt#13610
roli-lpci wants to merge 1 commit intomicrosoft:mainfrom
roli-lpci:fix/python-history-reducer-system-prompt-12612

Conversation

@roli-lpci
Copy link

Summary

ChatHistoryTruncationReducer.reduce() silently deletes system/developer messages when truncating chat history. This is because it calls extract_range(), which unconditionally filters out system/developer messages — a function designed for the summarization use case, not truncation.

Fixes #12612.

Approach

Port the .NET SDK fix (PR #10344) to Python:

  1. Detect the first system/developer message before truncation
  2. Pass has_system_message=True to locate_safe_reduction_index so target_count accounts for the preserved message (matches .NET's targetCount -= hasSystemMessage ? 1 : 0)
  3. Use a simple history[truncation_index:] slice instead of extract_range (which strips system messages)
  4. Prepend the system message if it was truncated away

Also adds a guard for target_count <= 0 after the system message adjustment to prevent IndexError when target_count=1.

Changes

File Change
chat_history_reducer_utils.py Add has_system_message parameter, adjust target_count, guard against <= 0
chat_history_truncation_reducer.py Detect system message, bypass extract_range, prepend if truncated
test_chat_history_truncation_reducer.py Update 2 existing tests + 4 new tests (system, developer, no-system, target_count=1)

Note

The summarization reducer (ChatHistorySummarizationReducer) has the same bug — it also uses extract_range and loses system messages during summarization. The .NET summarization reducer preserves system messages via AssemblySummarizedHistory. This should be addressed in a follow-up PR to keep this change focused.

Test plan

…osoft#12612)

The truncation reducer used `extract_range()` which unconditionally
filters out system/developer messages — a function designed for the
summarization use case, not truncation. This caused system prompts to
be silently deleted when chat history was reduced.

Fix: detect the first system/developer message before truncation,
pass `has_system_message=True` to `locate_safe_reduction_index` so
target_count accounts for the preserved message, use a simple slice
instead of `extract_range`, and prepend the system message if it was
truncated away.

This matches the .NET SDK behavior (PR microsoft#10344, already merged).

Also adds a guard for `target_count <= 0` after the system message
adjustment to prevent IndexError when target_count=1.

Note: The summarization reducer (`ChatHistorySummarizationReducer`)
has the same bug — it also uses `extract_range` and loses system
messages. That should be addressed in a follow-up PR.

Closes microsoft#12612
@roli-lpci roli-lpci requested a review from a team as a code owner March 1, 2026 09:04
@moonbox3 moonbox3 added the python Pull requests for the Python Semantic Kernel label Mar 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

python Pull requests for the Python Semantic Kernel

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Python: Bug: Python 1.34.0 ChatHistoryTruncationReducer deletes the System Prompt

2 participants