mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-24 16:54:43 +00:00
fix(gateway): show lock-hold reason when /compress no-ops
This commit is contained in:
parent
65145d9c5a
commit
eed6bb14bc
2 changed files with 51 additions and 0 deletions
|
|
@ -3563,6 +3563,19 @@ class GatewaySlashCommandsMixin:
|
|||
defer_context_engine_notification=True,
|
||||
)
|
||||
)
|
||||
|
||||
# If _compress_context returned unchanged because a
|
||||
# concurrent compression lock is held, tell the user
|
||||
# clearly instead of showing the misleading
|
||||
# "No changes from compression" no-op text.
|
||||
_lock_skipped = getattr(tmp_agent, "_compression_skipped_due_to_lock", None)
|
||||
if _lock_skipped:
|
||||
holder = _lock_skipped if isinstance(_lock_skipped, str) else "unknown"
|
||||
return (
|
||||
f"⏳ Compression already in progress for this session "
|
||||
f"(holder: {holder}). Please wait for it to finish."
|
||||
)
|
||||
|
||||
if partial and tail:
|
||||
compressed = rejoin_compressed_head_and_tail(compressed, tail)
|
||||
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@ async def test_compress_command_reports_noop_without_success_banner():
|
|||
agent_instance.context_compressor.has_content_to_compress.return_value = True
|
||||
agent_instance.session_id = "sess-1"
|
||||
agent_instance._compress_context.return_value = (list(history), "")
|
||||
agent_instance._compression_skipped_due_to_lock = False
|
||||
|
||||
def _estimate(messages, **_kwargs):
|
||||
assert messages == history
|
||||
|
|
@ -149,6 +150,7 @@ async def test_compress_command_explains_when_token_estimate_rises():
|
|||
agent_instance.context_compressor.has_content_to_compress.return_value = True
|
||||
agent_instance.session_id = "sess-1"
|
||||
agent_instance._compress_context.return_value = (compressed, "")
|
||||
agent_instance._compression_skipped_due_to_lock = False
|
||||
|
||||
def _estimate(messages, **_kwargs):
|
||||
if messages == history:
|
||||
|
|
@ -199,6 +201,7 @@ async def test_compress_command_appends_warning_when_compression_aborts():
|
|||
)
|
||||
agent_instance.session_id = "sess-1"
|
||||
agent_instance._compress_context.return_value = (compressed, "")
|
||||
agent_instance._compression_skipped_due_to_lock = False
|
||||
|
||||
def _estimate(messages, **_kwargs):
|
||||
if messages == history:
|
||||
|
|
@ -261,6 +264,7 @@ async def test_compress_command_surfaces_aux_model_failure_even_when_recovered()
|
|||
)
|
||||
agent_instance.session_id = "sess-1"
|
||||
agent_instance._compress_context.return_value = (compressed, "")
|
||||
agent_instance._compression_skipped_due_to_lock = False
|
||||
|
||||
def _estimate(messages, **_kwargs):
|
||||
if messages == history:
|
||||
|
|
@ -319,6 +323,7 @@ async def test_compress_command_passes_session_db_and_persists_rotated_session()
|
|||
return compressed, ""
|
||||
|
||||
agent_instance._compress_context.side_effect = _compress
|
||||
agent_instance._compression_skipped_due_to_lock = False
|
||||
|
||||
def _estimate(messages, **_kwargs):
|
||||
if messages == history:
|
||||
|
|
@ -385,6 +390,7 @@ async def test_compress_command_does_not_repoint_session_when_transcript_write_f
|
|||
return compressed, ""
|
||||
|
||||
agent_instance._compress_context.side_effect = _compress
|
||||
agent_instance._compression_skipped_due_to_lock = False
|
||||
|
||||
def _estimate(messages, **_kwargs):
|
||||
return 100
|
||||
|
|
@ -440,6 +446,7 @@ async def test_compress_command_in_place_skips_destructive_rewrite():
|
|||
agent_instance._last_compaction_in_place = True
|
||||
agent_instance.session_id = "sess-1"
|
||||
agent_instance._compress_context.return_value = (compressed, "")
|
||||
agent_instance._compression_skipped_due_to_lock = False
|
||||
|
||||
def _estimate(messages, **_kwargs):
|
||||
if messages == history:
|
||||
|
|
@ -482,6 +489,7 @@ async def test_compress_command_preserves_platform_and_gateway_session_key():
|
|||
agent_instance.context_compressor.has_content_to_compress.return_value = True
|
||||
agent_instance.session_id = "sess-1"
|
||||
agent_instance._compress_context.return_value = (list(history), "")
|
||||
agent_instance._compression_skipped_due_to_lock = False
|
||||
|
||||
with (
|
||||
patch("gateway.run._resolve_runtime_agent_kwargs", return_value={"api_key": "test-key"}),
|
||||
|
|
@ -517,6 +525,7 @@ async def test_compress_command_overrides_stale_resolver_identity():
|
|||
agent_instance.context_compressor.has_content_to_compress.return_value = True
|
||||
agent_instance.session_id = "sess-1"
|
||||
agent_instance._compress_context.return_value = (list(history), "")
|
||||
agent_instance._compression_skipped_due_to_lock = False
|
||||
|
||||
# Resolver injects a WRONG platform and a stale session key.
|
||||
runtime = {"api_key": "test-key", "platform": "discord", "gateway_session_key": "stale-key"}
|
||||
|
|
@ -581,3 +590,32 @@ async def test_compress_command_passes_tool_messages_to_compressor():
|
|||
# Assistant tool_calls stubs (content=None) must survive too, or the
|
||||
# tool message would dangle without its call.
|
||||
assert any(m.get("tool_calls") for m in passed), "assistant tool_calls stub dropped"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_compress_command_surfaces_lock_skip():
|
||||
"""When _compress_context skips due to a concurrent lock, the gateway
|
||||
handler must surface a clear message, not the misleading no-op text."""
|
||||
history = _make_history()
|
||||
runner = _make_runner(history)
|
||||
agent_instance = MagicMock()
|
||||
agent_instance.shutdown_memory_provider = MagicMock()
|
||||
agent_instance.close = MagicMock()
|
||||
agent_instance._cached_system_prompt = ""
|
||||
agent_instance.tools = None
|
||||
agent_instance.context_compressor.has_content_to_compress.return_value = True
|
||||
agent_instance.session_id = "sess-1"
|
||||
agent_instance._compress_context.return_value = (list(history), "")
|
||||
agent_instance._compression_skipped_due_to_lock = "pid=99999"
|
||||
|
||||
with (
|
||||
patch("gateway.run._resolve_runtime_agent_kwargs", return_value={"api_key": "***"}),
|
||||
patch("gateway.run._resolve_gateway_model", return_value="test-model"),
|
||||
patch("run_agent.AIAgent", return_value=agent_instance),
|
||||
patch("agent.model_metadata.estimate_request_tokens_rough", return_value=100),
|
||||
):
|
||||
result = await runner._handle_compress_command(_make_event())
|
||||
|
||||
assert "Compression already in progress" in result
|
||||
assert "pid=99999" in result
|
||||
assert "No changes from compression" not in result
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue