Files
semantic-kernel/python/semantic_kernel
3c1b688dcf Python: Fix ChatHistoryTruncationReducer deleting system prompt (#13610)
## 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

- [x] 11/11 truncation reducer tests pass (was 7, now 11)
- [x] 9/9 utils tests pass (no regressions from new parameter)
- [x] 11/11 summarization reducer tests pass (unaffected)
- [x] Reproduces and fixes the exact scenario from #12612
- [x] Edge case: `target_count=1` with system message does not crash

---------

Co-authored-by: Claude Opus 4.6 <[email protected]>
Co-authored-by: Evan Mattson <[email protected]>
2026-03-19 12:28:52 +00:00
..
2024-10-10 22:33:42 +00:00
2024-03-28 15:10:28 +00:00