mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-06 10:47:12 +08:00
fix(compaction): mark end of context summary in role=user fallback
When the head ends with assistant/tool and the tail starts with assistant, the summary is inserted as a standalone role="user" message. The body's verbatim "## Active Task" quote then gets read as fresh user input by weak/local models (#11475, #14521). The merge-into-tail path already appends an explicit end-of-summary marker for this reason. Mirror it on the standalone path so both insertion routes give the model the same "summary above, not new input" signal.
This commit is contained in:
@@ -664,6 +664,44 @@ class TestCompressWithClient:
|
||||
"call_123"
|
||||
]
|
||||
|
||||
def test_user_role_summary_carries_end_marker(self):
|
||||
"""When the summary lands as standalone role='user' (e.g. head ends
|
||||
with assistant/tool), the message body must include the explicit
|
||||
'--- END OF CONTEXT SUMMARY ---' marker. Without it, weak models
|
||||
read the verbatim past user request quoted in '## Active Task' as
|
||||
fresh input (#11475, #14521).
|
||||
"""
|
||||
mock_response = MagicMock()
|
||||
mock_response.choices = [MagicMock()]
|
||||
mock_response.choices[0].message.content = "summary text"
|
||||
|
||||
with patch("agent.context_compressor.get_model_context_length", return_value=100000):
|
||||
c = ContextCompressor(model="test", quiet_mode=True, protect_first_n=2, protect_last_n=2)
|
||||
|
||||
# head_last=assistant, tail_first=assistant (same shape as the
|
||||
# existing consecutive-user test) → role resolves to "user".
|
||||
msgs = [
|
||||
{"role": "user", "content": "msg 0"},
|
||||
{"role": "assistant", "content": "msg 1"},
|
||||
{"role": "user", "content": "msg 2"},
|
||||
{"role": "assistant", "content": "msg 3"},
|
||||
{"role": "user", "content": "msg 4"},
|
||||
{"role": "assistant", "content": "msg 5"},
|
||||
{"role": "user", "content": "msg 6"},
|
||||
{"role": "assistant", "content": "msg 7"},
|
||||
]
|
||||
with patch("agent.context_compressor.call_llm", return_value=mock_response):
|
||||
result = c.compress(msgs)
|
||||
|
||||
summary_msg = next(
|
||||
m for m in result if (m.get("content") or "").startswith(SUMMARY_PREFIX)
|
||||
)
|
||||
assert summary_msg["role"] == "user"
|
||||
assert "END OF CONTEXT SUMMARY" in summary_msg["content"]
|
||||
assert summary_msg["content"].rstrip().endswith(
|
||||
"respond to the message below, not the summary above ---"
|
||||
)
|
||||
|
||||
def test_summary_role_avoids_consecutive_user_messages(self):
|
||||
"""Summary role should alternate with the last head message to avoid consecutive same-role messages."""
|
||||
mock_client = MagicMock()
|
||||
|
||||
Reference in New Issue
Block a user