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:
@@ -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",
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user