fix(session_search): read default_mode from auxiliary.session_search

The previous fix wired _resolve_user_default_mode() to look up
tools.session_search.default_mode, but the config schema has no
top-level 'tools' section. The closest analogue is auxiliary.<tool>,
which already groups per-tool config by tool name (auxiliary.vision
has download_timeout, auxiliary.session_search has max_concurrency —
neither is strictly aux-LLM routing).

This moves the lookup to auxiliary.session_search.default_mode so the
knob lives next to max_concurrency and the existing session_search
config block. Adds default_mode to the default config scaffold so it
shows up in fresh installs.

Updates docstring, tool description string, warning messages, and all
7 mock-config tests to the new path. 88/88 tests passing.
This commit is contained in:
yoniebans
2026-05-13 16:54:56 +02:00
parent 2bed2124a4
commit 76f40e6449
3 changed files with 27 additions and 23 deletions
+1
View File
@@ -828,6 +828,7 @@ DEFAULT_CONFIG = {
"timeout": 30,
"extra_body": {},
"max_concurrency": 3, # Clamp parallel summaries to avoid request-burst 429s on small providers
"default_mode": "summary", # 'fast' | 'summary' — which mode session_search uses when caller passes none
},
"skills_hub": {
"provider": "auto",
+13 -13
View File
@@ -746,7 +746,7 @@ class TestSessionSearch:
If the handler substitutes "summary" when the LLM omits ``mode``, then
``_resolve_user_default_mode()`` is structurally unreachable from real
tool calls and the ``tools.session_search.default_mode`` config knob
tool calls and the ``auxiliary.session_search.default_mode`` config knob
becomes dead code. This is the registry-handler counterpart to
``test_run_agent_special_session_search_paths_forward_mode``.
"""
@@ -761,13 +761,13 @@ class TestSessionSearch:
)
assert 'mode=args.get("mode", "summary")' not in source, (
"registry handler must not hardcode \"summary\" as the mode default — "
"it shadows tools.session_search.default_mode in config.yaml"
"it shadows auxiliary.session_search.default_mode in config.yaml"
)
def test_unset_mode_via_registry_honours_configured_default(self, monkeypatch):
"""End-to-end: unset mode through the registry handler resolves to config.
With ``tools.session_search.default_mode: fast`` configured, an LLM tool
With ``auxiliary.session_search.default_mode: fast`` configured, an LLM tool
call that omits ``mode`` must run in fast mode (no aux LLM), not summary.
This is the regression test for the bug where three layers of hardcoded
``"summary"`` defaults made the config knob unreachable.
@@ -779,7 +779,7 @@ class TestSessionSearch:
self._clear_default_mode_cache()
monkeypatch.setattr(
"hermes_cli.config.load_config",
lambda: {"tools": {"session_search": {"default_mode": "fast"}}},
lambda: {"auxiliary": {"session_search": {"default_mode": "fast"}}},
)
# Sanity: the resolver itself sees fast.
assert _resolve_user_default_mode() == "fast"
@@ -798,7 +798,7 @@ class TestSessionSearch:
)
# -----------------------------------------------------------------
# User-configurable default mode (tools.session_search.default_mode
# User-configurable default mode (auxiliary.session_search.default_mode
# in ~/.hermes/config.yaml). Lets a user opt into fast-as-default
# without having to pass mode= on every call.
# -----------------------------------------------------------------
@@ -818,12 +818,12 @@ class TestSessionSearch:
assert _resolve_user_default_mode() == "summary"
def test_user_can_configure_fast_as_default(self, monkeypatch):
"""tools.session_search.default_mode: fast → unset mode resolves to 'fast'."""
"""auxiliary.session_search.default_mode: fast → unset mode resolves to 'fast'."""
from tools.session_search_tool import _resolve_user_default_mode
self._clear_default_mode_cache()
monkeypatch.setattr(
"hermes_cli.config.load_config",
lambda: {"tools": {"session_search": {"default_mode": "fast"}}},
lambda: {"auxiliary": {"session_search": {"default_mode": "fast"}}},
)
assert _resolve_user_default_mode() == "fast"
@@ -833,7 +833,7 @@ class TestSessionSearch:
self._clear_default_mode_cache()
monkeypatch.setattr(
"hermes_cli.config.load_config",
lambda: {"tools": {"session_search": {"default_mode": "summary"}}},
lambda: {"auxiliary": {"session_search": {"default_mode": "summary"}}},
)
assert _resolve_user_default_mode() == "summary"
@@ -844,7 +844,7 @@ class TestSessionSearch:
self._clear_default_mode_cache()
monkeypatch.setattr(
"hermes_cli.config.load_config",
lambda: {"tools": {"session_search": {"default_mode": "smary"}}},
lambda: {"auxiliary": {"session_search": {"default_mode": "smary"}}},
)
with caplog.at_level(logging.WARNING):
assert _resolve_user_default_mode() == "summary"
@@ -857,7 +857,7 @@ class TestSessionSearch:
self._clear_default_mode_cache()
monkeypatch.setattr(
"hermes_cli.config.load_config",
lambda: {"tools": {"session_search": {"default_mode": "guided"}}},
lambda: {"auxiliary": {"session_search": {"default_mode": "guided"}}},
)
assert _resolve_user_default_mode() == "summary"
@@ -868,7 +868,7 @@ class TestSessionSearch:
for bad in (42, ["fast"], {"mode": "fast"}, True):
monkeypatch.setattr(
"hermes_cli.config.load_config",
lambda b=bad: {"tools": {"session_search": {"default_mode": b}}},
lambda b=bad: {"auxiliary": {"session_search": {"default_mode": b}}},
)
self._clear_default_mode_cache()
assert _resolve_user_default_mode() == "summary", f"bad value {bad!r} should fall back"
@@ -881,7 +881,7 @@ class TestSessionSearch:
self._clear_default_mode_cache()
monkeypatch.setattr(
"hermes_cli.config.load_config",
lambda: {"tools": {"session_search": {"default_mode": "fast"}}},
lambda: {"auxiliary": {"session_search": {"default_mode": "fast"}}},
)
mock_db = MagicMock()
mock_db.search_messages.return_value = []
@@ -900,7 +900,7 @@ class TestSessionSearch:
self._clear_default_mode_cache()
monkeypatch.setattr(
"hermes_cli.config.load_config",
lambda: {"tools": {"session_search": {"default_mode": "fast"}}},
lambda: {"auxiliary": {"session_search": {"default_mode": "fast"}}},
)
async def fail_summarize(*_args, **_kwargs):
+13 -10
View File
@@ -26,16 +26,19 @@ MAX_SESSION_CHARS = 100_000
# Default mode is summary unless the user opts into a different one via
# ``tools.session_search.default_mode`` in ~/.hermes/config.yaml. Only ``fast``
# and ``summary`` are valid defaults — guided requires anchors and can't be
# used standalone. Wrapped in lru_cache so the YAML read happens at most once
# per process; the CLI / TUI is the typical caller and config changes need a
# restart anyway.
# ``auxiliary.session_search.default_mode`` in ~/.hermes/config.yaml. Lives
# alongside the other session_search-scoped knobs (provider, model,
# max_concurrency) — "auxiliary" started as aux-LLM routing but in practice
# groups per-tool config by tool name. Only ``fast`` and ``summary`` are
# valid defaults — guided requires anchors and can't be used standalone.
# Wrapped in lru_cache so the YAML read happens at most once per process;
# the CLI / TUI is the typical caller and config changes need a restart
# anyway.
_VALID_DEFAULT_MODES = ("fast", "summary")
def _resolve_user_default_mode() -> str:
"""Look up ``tools.session_search.default_mode`` from ~/.hermes/config.yaml.
"""Look up ``auxiliary.session_search.default_mode`` from ~/.hermes/config.yaml.
Returns "summary" if unset, invalid, or the config loader is unavailable
(e.g. tests, tools loaded outside the CLI). Logs a one-time warning on
@@ -52,7 +55,7 @@ def _resolve_user_default_mode() -> str:
return "summary"
raw = (
config.get("tools", {})
config.get("auxiliary", {})
.get("session_search", {})
.get("default_mode")
)
@@ -60,14 +63,14 @@ def _resolve_user_default_mode() -> str:
return "summary"
if not isinstance(raw, str):
logging.warning(
"tools.session_search.default_mode in config.yaml must be a string, got %r — falling back to 'summary'",
"auxiliary.session_search.default_mode in config.yaml must be a string, got %r — falling back to 'summary'",
raw,
)
return "summary"
normalised = raw.strip().lower()
if normalised not in _VALID_DEFAULT_MODES:
logging.warning(
"tools.session_search.default_mode=%r is not one of %s — falling back to 'summary'. "
"auxiliary.session_search.default_mode=%r is not one of %s — falling back to 'summary'. "
"(guided requires anchors and cannot be a default.)",
raw, _VALID_DEFAULT_MODES,
)
@@ -1124,7 +1127,7 @@ SESSION_SEARCH_SCHEMA = {
"Returns titles, previews, timestamps. Zero LLM cost, instant. Start here when the user "
"asks 'what were we working on' or 'what did we do recently'.\n\n"
"The default mode is configurable per-user via "
"``tools.session_search.default_mode: fast`` in ~/.hermes/config.yaml. When no mode is "
"``auxiliary.session_search.default_mode: fast`` in ~/.hermes/config.yaml. When no mode is "
"passed explicitly, that user-configured value applies (then 'summary' as the final "
"fallback).\n\n"
"USE THIS PROACTIVELY when:\n"