mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-04-25 00:51:20 +00:00
fix(cli): pass None as system_message in manual /compress to prevent duplication (#15281)
_manual_compress was passing _cached_system_prompt to _compress_context, but _build_system_prompt already assembles the identity block into prompt_parts. The redundant system_message caused the identity to appear twice in the compressed output. Pass None so _compress_context rebuilds the prompt from scratch. Supersedes #15304.
This commit is contained in:
parent
00c3d848d8
commit
5287575a9c
2 changed files with 97 additions and 1 deletions
8
cli.py
8
cli.py
|
|
@ -7023,9 +7023,15 @@ class HermesCLI:
|
|||
else:
|
||||
print(f"🗜️ Compressing {original_count} messages (~{approx_tokens:,} tokens)...")
|
||||
|
||||
# Pass None as system_message so _compress_context rebuilds
|
||||
# the system prompt from scratch via _build_system_prompt(None).
|
||||
# Passing _cached_system_prompt caused duplication because
|
||||
# _build_system_prompt appends system_message to prompt_parts
|
||||
# which already contain the agent identity — resulting in the
|
||||
# identity block appearing twice (issue #15281).
|
||||
compressed, _ = self.agent._compress_context(
|
||||
original_history,
|
||||
self.agent._cached_system_prompt or "",
|
||||
None,
|
||||
approx_tokens=approx_tokens,
|
||||
focus_topic=focus_topic or None,
|
||||
)
|
||||
|
|
|
|||
90
tests/cli/test_manual_compress_system_prompt.py
Normal file
90
tests/cli/test_manual_compress_system_prompt.py
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
"""Tests for manual /compress system prompt handling.
|
||||
|
||||
When _manual_compress invokes _compress_context, it must pass None
|
||||
as system_message to avoid duplicating the agent identity block.
|
||||
Issue #15281.
|
||||
"""
|
||||
import unittest
|
||||
from contextlib import contextmanager
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
|
||||
class TestManualCompressSystemMessage(unittest.TestCase):
|
||||
"""Verify _manual_compress avoids system prompt duplication."""
|
||||
|
||||
def _make_cli_with_agent(self):
|
||||
"""Create a minimal CLI mock with enough state for _manual_compress."""
|
||||
from cli import HermesCLI
|
||||
|
||||
cli = MagicMock(spec=HermesCLI)
|
||||
cli.conversation_history = [
|
||||
{"role": "user", "content": "msg1"},
|
||||
{"role": "assistant", "content": "resp1"},
|
||||
{"role": "user", "content": "msg2"},
|
||||
{"role": "assistant", "content": "resp2"},
|
||||
{"role": "user", "content": "msg3"},
|
||||
{"role": "assistant", "content": "resp3"},
|
||||
]
|
||||
cli.agent = MagicMock()
|
||||
cli.agent.compression_enabled = True
|
||||
cli.agent._cached_system_prompt = "You are Hermes, a helpful AI assistant."
|
||||
cli.agent.session_id = "test-session-id"
|
||||
cli.session_id = "test-session-id"
|
||||
cli.agent._compress_context.return_value = (
|
||||
[{"role": "assistant", "content": "[compressed summary]"}],
|
||||
50,
|
||||
)
|
||||
|
||||
@contextmanager
|
||||
def _busy_stub(msg):
|
||||
yield
|
||||
|
||||
cli._busy_command = _busy_stub
|
||||
return cli
|
||||
|
||||
@patch(
|
||||
"agent.manual_compression_feedback.summarize_manual_compression",
|
||||
return_value="Compressed 6 → 1",
|
||||
)
|
||||
@patch(
|
||||
"agent.model_metadata.estimate_messages_tokens_rough",
|
||||
return_value=5000,
|
||||
)
|
||||
def test_compress_context_receives_none_system_message(
|
||||
self, _mock_tokens, _mock_summary
|
||||
):
|
||||
"""_compress_context must receive None, not the cached system prompt."""
|
||||
from cli import HermesCLI
|
||||
|
||||
cli = self._make_cli_with_agent()
|
||||
HermesCLI._manual_compress(cli)
|
||||
|
||||
cli.agent._compress_context.assert_called_once()
|
||||
args, kwargs = cli.agent._compress_context.call_args
|
||||
# Second positional arg is system_message — must be None
|
||||
self.assertIsNone(
|
||||
args[1],
|
||||
"_compress_context should receive None as system_message, "
|
||||
"not the cached prompt, to avoid identity duplication",
|
||||
)
|
||||
|
||||
@patch(
|
||||
"agent.manual_compression_feedback.summarize_manual_compression",
|
||||
return_value="Compressed",
|
||||
)
|
||||
@patch(
|
||||
"agent.model_metadata.estimate_messages_tokens_rough",
|
||||
return_value=5000,
|
||||
)
|
||||
def test_compress_context_receives_focus_topic(
|
||||
self, _mock_tokens, _mock_summary
|
||||
):
|
||||
"""Focus topic from /compress <topic> should be forwarded."""
|
||||
from cli import HermesCLI
|
||||
|
||||
cli = self._make_cli_with_agent()
|
||||
HermesCLI._manual_compress(cli, cmd_original="/compress database schema")
|
||||
|
||||
cli.agent._compress_context.assert_called_once()
|
||||
_, kwargs = cli.agent._compress_context.call_args
|
||||
self.assertEqual(kwargs.get("focus_topic"), "database schema")
|
||||
Loading…
Add table
Add a link
Reference in a new issue