hermes-agent/tests/hermes_cli/test_session_recap.py
kshitijk4poor 66827f8947 chore: prune unused imports and duplicate import redefinitions
Remove unused imports (F401) and duplicate/shadowed import
redefinitions (F811) across the codebase using ruff's safe
autofixes. No behavioral changes -- imports only.

- ~1400 safe autofixes applied across 644 files (net -1072 lines)
- __init__.py re-exports preserved (excluded from F401 removal so
  public re-export surfaces stay intact)
- Re-exports that are imported or monkeypatched by tests but look
  unused in their defining module are kept with explicit # noqa:
  F401 (gateway/run.py load_dotenv; run_agent re-exports from
  agent.message_sanitization, agent.context_compressor,
  agent.retry_utils, agent.prompt_builder, agent.process_bootstrap,
  agent.codex_responses_adapter)
- Unsafe F841 (unused-variable) fixes deliberately skipped -- those
  can change behavior when the RHS has side effects
- ruff lints remain disabled in pyproject.toml (only PLW1514 is
  selected); this is a one-time cleanup, not a config change

Verification:
- python -m compileall: clean
- pytest --collect-only: all 27161 tests collect (zero import errors)
- core entry points import clean (run_agent, model_tools, cli,
  toolsets, hermes_state, batch_runner, gateway)
- static scan: every name any test imports directly from an edited
  module still resolves
2026-05-28 22:26:25 -07:00

179 lines
4.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Unit tests for hermes_cli.session_recap."""
from __future__ import annotations
import json
from hermes_cli.session_recap import build_recap
def _user(text):
return {"role": "user", "content": text}
def _assistant(text=None, tool_calls=None):
msg = {"role": "assistant", "content": text}
if tool_calls:
msg["tool_calls"] = tool_calls
return msg
def _tool_call(name, args):
return {
"id": f"call_{name}",
"type": "function",
"function": {"name": name, "arguments": json.dumps(args)},
}
def _tool_result(content="ok"):
return {"role": "tool", "content": content}
def test_empty_history():
out = build_recap([])
assert "Session recap" in out
assert "nothing to recap" in out
def test_header_shows_title_when_provided():
out = build_recap([_user("hello")], session_title="Refactor the adapter")
assert "Refactor the adapter" in out.splitlines()[0]
def test_header_shows_short_id_when_no_title():
out = build_recap([_user("hello")], session_id="abcdef1234567890")
assert "abcdef12" in out.splitlines()[0]
def test_counts_recent_turns():
msgs = [
_user("one"),
_assistant("first reply"),
_user("two"),
_assistant("second reply"),
]
out = build_recap(msgs)
assert "2 user turn" in out
assert "assistant repl" in out
def test_last_ask_and_reply_are_surfaced():
msgs = [
_user("old question"),
_assistant("old answer"),
_user("summarise the docs"),
_assistant("here is the summary of the docs you asked for"),
]
out = build_recap(msgs)
assert "summarise the docs" in out
assert "summary of the docs" in out
def test_tool_counts_and_files():
msgs = [
_user("edit the readme and run tests"),
_assistant(
tool_calls=[
_tool_call("read_file", {"path": "README.md"}),
_tool_call("patch", {"path": "README.md"}),
]
),
_tool_result(),
_tool_result(),
_assistant(
tool_calls=[
_tool_call("terminal", {"command": "pytest"}),
]
),
_tool_result("tests ok"),
_assistant("All green."),
]
out = build_recap(msgs)
assert "patch×1" in out
assert "terminal×1" in out
assert "read_file×1" in out
# README.md should appear (may include cwd-relative prefix stripping).
assert "README.md" in out
def test_tool_preview_length_truncates_long_user_prompt():
long = "x " * 500
out = build_recap([_user(long)])
ask_line = [l for l in out.splitlines() if "Last ask" in l][0]
assert len(ask_line) < 300 # truncated with ellipsis
assert "" in ask_line
def test_respects_recent_window():
# 30 turns of user+assistant; only the most recent 20 should be summarised.
msgs = []
for i in range(30):
msgs.append(_user(f"question {i}"))
msgs.append(_assistant(f"answer {i}"))
out = build_recap(msgs)
# We scoped to the 20-turn window but show "of 30/30 total".
assert "of 30/30 total" in out
def test_multimodal_content_blocks_flattened():
msgs = [
{
"role": "user",
"content": [
{"type": "text", "text": "check this file"},
{"type": "image_url", "image_url": {"url": "..."}},
],
},
_assistant("Looked at your image."),
]
out = build_recap(msgs)
assert "check this file" in out
assert "Looked at your image" in out
def test_handles_arguments_as_dict_not_string():
# Some providers return arguments already as a dict.
msgs = [
_user("go"),
{
"role": "assistant",
"content": None,
"tool_calls": [
{
"type": "function",
"function": {
"name": "patch",
"arguments": {"path": "foo.py"},
},
}
],
},
]
out = build_recap(msgs)
assert "patch×1" in out
assert "foo.py" in out
def test_no_assistant_activity_hint():
out = build_recap([_user("just sent my first message")])
assert "no assistant activity" in out or "Last ask" in out
def test_tool_message_count_reported():
msgs = [
_user("go"),
_assistant(tool_calls=[_tool_call("read_file", {"path": "a"})]),
_tool_result(),
_tool_result(),
_assistant("done"),
]
out = build_recap(msgs)
assert "2 tool result" in out
def test_ignores_non_mapping_entries_gracefully():
msgs = [None, "stray", _user("hi"), _assistant("hello")]
# Should not raise.
out = build_recap(msgs)
assert "Session recap" in out