diff --git a/agent/title_generator.py b/agent/title_generator.py index aae407fbaa3e..f865aa02d022 100644 --- a/agent/title_generator.py +++ b/agent/title_generator.py @@ -9,8 +9,6 @@ import threading from typing import Callable, Optional from agent.auxiliary_client import call_llm -from hermes_cli.config import load_config_readonly -from utils import is_truthy_value logger = logging.getLogger(__name__) @@ -53,6 +51,12 @@ def _title_language() -> str: def _auto_title_enabled() -> bool: """Return whether automatic session title generation is enabled.""" try: + # Lazy imports, matching _title_language(): title_generator is imported + # from agent code paths where a module-level hermes_cli import risks + # circularity, and the read-only loader avoids config-migration writes. + from hermes_cli.config import load_config_readonly + from utils import is_truthy_value + config = load_config_readonly() title_config = (config.get("auxiliary") or {}).get("title_generation") or {} return is_truthy_value(title_config.get("enabled"), default=True) @@ -263,10 +267,6 @@ def maybe_auto_title( if not session_db or not session_id or not user_message or not assistant_response: return - if not _auto_title_enabled(): - logger.debug("Auto-title skipped: auxiliary.title_generation.enabled=false") - return - # Count user messages in history to detect first exchange. # conversation_history includes the exchange that just happened, # so for a first exchange we expect exactly 1 user message @@ -275,6 +275,12 @@ def maybe_auto_title( if user_msg_count > 2: return + # Config read comes after the cheap first-exchange guard so the file + # isn't touched on every subsequent turn of a long session. + if not _auto_title_enabled(): + logger.debug("Auto-title skipped: auxiliary.title_generation.enabled=false") + return + thread = threading.Thread( target=auto_title_session, args=(session_db, session_id, user_message, assistant_response), diff --git a/cli-config.yaml.example b/cli-config.yaml.example index a040c1a24935..2049b4426a54 100644 --- a/cli-config.yaml.example +++ b/cli-config.yaml.example @@ -526,6 +526,14 @@ prompt_caching: # model: "" # timeout: 30 # +# # Automatic session title generation after the first exchange +# title_generation: +# enabled: true # set false to disable auto-title generation +# provider: "auto" +# model: "" +# timeout: 30 +# language: "" # empty = match the user's language; or e.g. "English" +# # # Session search — summarizes matching past sessions # session_search: # provider: "auto" diff --git a/scripts/release.py b/scripts/release.py index d6ffb4d26026..4b96072c87d3 100755 --- a/scripts/release.py +++ b/scripts/release.py @@ -364,6 +364,7 @@ AUTHOR_MAP = { "290859878+synapsesx@users.noreply.github.com": "synapsesx", "157689911+itsflownium@users.noreply.github.com": "itsflownium", "dirtyren@users.noreply.github.com": "dirtyren", + "s96919@gmail.com": "s96919", "yakimenkoleksander228@gmail.com": "doxe0x", "a54983334@163.com": "Code-suphub", "78542984+Code-suphub@users.noreply.github.com": "Code-suphub", diff --git a/tests/agent/test_title_generator.py b/tests/agent/test_title_generator.py index fc63fbd56540..50aa01951abe 100644 --- a/tests/agent/test_title_generator.py +++ b/tests/agent/test_title_generator.py @@ -215,7 +215,7 @@ class TestGenerateTitle: config = {"auxiliary": {"title_generation": {"enabled": False}}} with ( - patch("agent.title_generator.load_config_readonly", return_value=config), + patch("hermes_cli.config.load_config_readonly", return_value=config), patch("agent.title_generator.call_llm") as mock_call_llm, ): assert generate_title("question", "answer") is None @@ -369,7 +369,7 @@ class TestMaybeAutoTitle: config = {"auxiliary": {"title_generation": {"enabled": False}}} with ( - patch("agent.title_generator.load_config_readonly", return_value=config), + patch("hermes_cli.config.load_config_readonly", return_value=config), patch("agent.title_generator.auto_title_session") as mock_auto, ): maybe_auto_title(db, "sess-1", "hello", "hi there", history)