Merge pull request #2125 into stage-344

docs: clarify compression anchor helpers (closes #2093)
This commit is contained in:
Hermes Agent
2026-05-12 16:12:53 +00:00
4 changed files with 41 additions and 10 deletions
+31
View File
@@ -1,5 +1,36 @@
"""
Shared helpers for session compression anchor metadata.
Manual compression anchoring versus automatic compression paths
===============================================================
When ``auto_compression=True`` is passed to ``visible_messages_for_anchor()``,
the function accepts a broader set of message content types (including
provider-style ``input_text`` / ``output_text`` parts) and metadata markers
(``reasoning``, ``thinking``, etc.) from any non-tool role. This enables the
streaming auto-compression path to determine which messages should anchor
compression UI metadata without being limited to the legacy manual-compression
rules.
When ``auto_compression=False`` (the default), the function applies the
historical manual-compression rules: only plain ``text`` content parts from
non-assistant roles are counted.
Why this module exists
======================
Compression anchoring needs to identify which messages in a session transcript
are semantically significant enough to seed the compression UI metadata (e.g.,
message count, token budget display). The original implementation hard-coded
these rules in multiple places. This module consolidates the logic so that:
1. Manual compression anchoring (CLI/legacy path) uses the stricter ruleset.
2. Automatic compression (streaming/agent path) can leverage the relaxed ruleset
when it knows it is handling provider-style messages.
Callers specify ``auto_compression=True`` when the messages may originate from
an automatic/compression-aware pipeline, and ``False`` (default) for manual
compression contexts.
"""
+2 -2
View File
@@ -41,7 +41,7 @@ _tls = threading.local()
_SKILL_HOME_MODULES = ("tools.skills_tool", "tools.skill_manager_tool")
def _patch_skill_home_modules(home: Path) -> None:
def patch_skill_home_modules(home: Path) -> None:
"""Patch imported skill modules that cache HERMES_HOME at import time."""
for module_name in _SKILL_HOME_MODULES:
module = sys.modules.get(module_name)
@@ -628,7 +628,7 @@ def _set_hermes_home(home: Path):
"""Set HERMES_HOME env var and monkey-patch cached module-level paths."""
os.environ['HERMES_HOME'] = str(home)
_patch_skill_home_modules(home)
patch_skill_home_modules(home)
# Patch cron/jobs module-level cache
try:
+4 -4
View File
@@ -2286,7 +2286,7 @@ def _run_agent_streaming(
# process-level active-profile global. Falls back gracefully.
try:
from api.profiles import (
_patch_skill_home_modules,
patch_skill_home_modules,
get_hermes_home_for_profile,
get_profile_runtime_env,
)
@@ -2296,7 +2296,7 @@ def _run_agent_streaming(
except ImportError:
_profile_home = os.environ.get('HERMES_HOME', '')
_profile_runtime_env = {}
_patch_skill_home_modules = None
patch_skill_home_modules = None
# Capture the resolved profile name now, while profile context is
# reliable. Used in the compression migration block to stamp s.profile
@@ -2349,8 +2349,8 @@ def _run_agent_streaming(
# above, so we only do lightweight sys.modules lookups and
# attribute assignments here — no first-time import under
# the lock (#2024).
if _patch_skill_home_modules is not None:
_patch_skill_home_modules(Path(_profile_home))
if patch_skill_home_modules is not None:
patch_skill_home_modules(Path(_profile_home))
# Lock released — agent runs without holding it
# ── MCP Server Discovery (lazy import, idempotent) ──
# MUST run AFTER the HERMES_HOME mutation above — `discover_mcp_tools()`
@@ -165,7 +165,7 @@ class TestSysModulesLookupInEnvLock:
lock_lines.append(line)
lock_source = "\n".join(lock_lines)
assert "_patch_skill_home_modules" in lock_source, (
assert "patch_skill_home_modules" in lock_source, (
"Inside `_ENV_LOCK`, streaming must use the shared skill module "
"cache patch helper instead of duplicating module-specific logic "
"(#2023/#2024)"
@@ -179,15 +179,15 @@ class TestSysModulesLookupInEnvLock:
node
for node in ast.walk(tree)
if isinstance(node, ast.FunctionDef)
and node.name == "_patch_skill_home_modules"
and node.name == "patch_skill_home_modules"
),
None,
)
assert helper is not None, "_patch_skill_home_modules() must be defined"
assert helper is not None, "patch_skill_home_modules() must be defined"
helper_source = ast.get_source_segment(source, helper) or ""
assert "sys.modules.get" in helper_source, (
"_patch_skill_home_modules() must use sys.modules.get(), not import, "
"patch_skill_home_modules() must use sys.modules.get(), not import, "
"so env-lock callers do not trigger first-time imports (#2024)"
)
assert "HERMES_HOME" in helper_source