mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-04-30 01:41:43 +00:00
fix(config): respect quoted false for session vacuum_after_prune
This commit is contained in:
parent
4fade39c90
commit
7da299ddb0
6 changed files with 94 additions and 15 deletions
4
cli.py
4
cli.py
|
|
@ -77,7 +77,7 @@ _COMMAND_SPINNER_FRAMES = ("⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧
|
||||||
# User-managed env files should override stale shell exports on restart.
|
# User-managed env files should override stale shell exports on restart.
|
||||||
from hermes_constants import get_hermes_home, display_hermes_home
|
from hermes_constants import get_hermes_home, display_hermes_home
|
||||||
from hermes_cli.env_loader import load_hermes_dotenv
|
from hermes_cli.env_loader import load_hermes_dotenv
|
||||||
from utils import base_url_host_matches
|
from utils import base_url_host_matches, coerce_bool
|
||||||
|
|
||||||
_hermes_home = get_hermes_home()
|
_hermes_home = get_hermes_home()
|
||||||
_project_env = Path(__file__).parent / '.env'
|
_project_env = Path(__file__).parent / '.env'
|
||||||
|
|
@ -974,7 +974,7 @@ def _run_state_db_auto_maintenance(session_db) -> None:
|
||||||
session_db.maybe_auto_prune_and_vacuum(
|
session_db.maybe_auto_prune_and_vacuum(
|
||||||
retention_days=int(cfg.get("retention_days", 90)),
|
retention_days=int(cfg.get("retention_days", 90)),
|
||||||
min_interval_hours=int(cfg.get("min_interval_hours", 24)),
|
min_interval_hours=int(cfg.get("min_interval_hours", 24)),
|
||||||
vacuum=bool(cfg.get("vacuum_after_prune", True)),
|
vacuum=coerce_bool(cfg.get("vacuum_after_prune", True), default=True),
|
||||||
)
|
)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logger.debug("state.db auto-maintenance skipped: %s", exc)
|
logger.debug("state.db auto-maintenance skipped: %s", exc)
|
||||||
|
|
|
||||||
|
|
@ -17,23 +17,14 @@ from typing import Dict, List, Optional, Any
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
|
||||||
from hermes_cli.config import get_hermes_home
|
from hermes_cli.config import get_hermes_home
|
||||||
from utils import is_truthy_value
|
from utils import coerce_bool
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def _coerce_bool(value: Any, default: bool = True) -> bool:
|
def _coerce_bool(value: Any, default: bool = True) -> bool:
|
||||||
"""Coerce bool-ish config values, preserving a caller-provided default."""
|
"""Coerce bool-ish config values, preserving a caller-provided default."""
|
||||||
if value is None:
|
return coerce_bool(value, default=default)
|
||||||
return default
|
|
||||||
if isinstance(value, str):
|
|
||||||
lowered = value.strip().lower()
|
|
||||||
if lowered in ("true", "1", "yes", "on"):
|
|
||||||
return True
|
|
||||||
if lowered in ("false", "0", "no", "off"):
|
|
||||||
return False
|
|
||||||
return default
|
|
||||||
return is_truthy_value(value, default=default)
|
|
||||||
|
|
||||||
|
|
||||||
def _normalize_unauthorized_dm_behavior(value: Any, default: str = "pair") -> str:
|
def _normalize_unauthorized_dm_behavior(value: Any, default: str = "pair") -> str:
|
||||||
|
|
|
||||||
|
|
@ -89,7 +89,7 @@ sys.path.insert(0, str(Path(__file__).parent.parent))
|
||||||
|
|
||||||
# Resolve Hermes home directory (respects HERMES_HOME override)
|
# Resolve Hermes home directory (respects HERMES_HOME override)
|
||||||
from hermes_constants import get_hermes_home
|
from hermes_constants import get_hermes_home
|
||||||
from utils import atomic_yaml_write, base_url_host_matches, is_truthy_value
|
from utils import atomic_yaml_write, base_url_host_matches, coerce_bool, is_truthy_value
|
||||||
_hermes_home = get_hermes_home()
|
_hermes_home = get_hermes_home()
|
||||||
|
|
||||||
# Load environment variables from ~/.hermes/.env first.
|
# Load environment variables from ~/.hermes/.env first.
|
||||||
|
|
@ -748,7 +748,7 @@ class GatewayRunner:
|
||||||
self._session_db.maybe_auto_prune_and_vacuum(
|
self._session_db.maybe_auto_prune_and_vacuum(
|
||||||
retention_days=int(_sess_cfg.get("retention_days", 90)),
|
retention_days=int(_sess_cfg.get("retention_days", 90)),
|
||||||
min_interval_hours=int(_sess_cfg.get("min_interval_hours", 24)),
|
min_interval_hours=int(_sess_cfg.get("min_interval_hours", 24)),
|
||||||
vacuum=bool(_sess_cfg.get("vacuum_after_prune", True)),
|
vacuum=coerce_bool(_sess_cfg.get("vacuum_after_prune", True), default=True),
|
||||||
)
|
)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logger.debug("state.db auto-maintenance skipped: %s", exc)
|
logger.debug("state.db auto-maintenance skipped: %s", exc)
|
||||||
|
|
|
||||||
31
tests/cli/test_state_db_auto_maintenance.py
Normal file
31
tests/cli/test_state_db_auto_maintenance.py
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
from unittest.mock import Mock
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("raw_value", ["false", False])
|
||||||
|
def test_run_state_db_auto_maintenance_respects_vacuum_flag(monkeypatch, tmp_path, raw_value):
|
||||||
|
monkeypatch.setenv("HERMES_HOME", str(tmp_path))
|
||||||
|
|
||||||
|
import cli as cli_mod
|
||||||
|
|
||||||
|
session_db = Mock()
|
||||||
|
monkeypatch.setattr(
|
||||||
|
"hermes_cli.config.load_config",
|
||||||
|
lambda: {
|
||||||
|
"sessions": {
|
||||||
|
"auto_prune": True,
|
||||||
|
"retention_days": 30,
|
||||||
|
"min_interval_hours": 12,
|
||||||
|
"vacuum_after_prune": raw_value,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
cli_mod._run_state_db_auto_maintenance(session_db)
|
||||||
|
|
||||||
|
session_db.maybe_auto_prune_and_vacuum.assert_called_once_with(
|
||||||
|
retention_days=30,
|
||||||
|
min_interval_hours=12,
|
||||||
|
vacuum=False,
|
||||||
|
)
|
||||||
34
tests/gateway/test_state_db_auto_maintenance.py
Normal file
34
tests/gateway/test_state_db_auto_maintenance.py
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
from unittest.mock import Mock
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from gateway.config import GatewayConfig
|
||||||
|
from gateway.run import GatewayRunner
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("raw_value", ["false", False])
|
||||||
|
def test_gateway_runner_respects_vacuum_after_prune_flag(monkeypatch, tmp_path, raw_value):
|
||||||
|
monkeypatch.setenv("HERMES_HOME", str(tmp_path))
|
||||||
|
|
||||||
|
fake_db = Mock()
|
||||||
|
monkeypatch.setattr("hermes_state.SessionDB", lambda: fake_db)
|
||||||
|
monkeypatch.setattr(
|
||||||
|
"hermes_cli.config.load_config",
|
||||||
|
lambda: {
|
||||||
|
"sessions": {
|
||||||
|
"auto_prune": True,
|
||||||
|
"retention_days": 45,
|
||||||
|
"min_interval_hours": 6,
|
||||||
|
"vacuum_after_prune": raw_value,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
runner = GatewayRunner(GatewayConfig(sessions_dir=tmp_path / "sessions"))
|
||||||
|
|
||||||
|
assert runner._session_db is fake_db
|
||||||
|
fake_db.maybe_auto_prune_and_vacuum.assert_called_once_with(
|
||||||
|
retention_days=45,
|
||||||
|
min_interval_hours=6,
|
||||||
|
vacuum=False,
|
||||||
|
)
|
||||||
23
utils.py
23
utils.py
|
|
@ -15,6 +15,7 @@ logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
TRUTHY_STRINGS = frozenset({"1", "true", "yes", "on"})
|
TRUTHY_STRINGS = frozenset({"1", "true", "yes", "on"})
|
||||||
|
FALSY_STRINGS = frozenset({"0", "false", "no", "off"})
|
||||||
|
|
||||||
|
|
||||||
def is_truthy_value(value: Any, default: bool = False) -> bool:
|
def is_truthy_value(value: Any, default: bool = False) -> bool:
|
||||||
|
|
@ -28,6 +29,28 @@ def is_truthy_value(value: Any, default: bool = False) -> bool:
|
||||||
return bool(value)
|
return bool(value)
|
||||||
|
|
||||||
|
|
||||||
|
def coerce_bool(value: Any, default: bool = False) -> bool:
|
||||||
|
"""Coerce bool-ish config values while preserving the caller's default.
|
||||||
|
|
||||||
|
Unlike ``bool(value)``, this treats quoted config strings like
|
||||||
|
``"false"`` and ``"0"`` as ``False`` instead of truthy non-empty
|
||||||
|
strings. Unrecognized strings fall back to ``default`` so malformed
|
||||||
|
YAML values don't silently flip behavior.
|
||||||
|
"""
|
||||||
|
if value is None:
|
||||||
|
return default
|
||||||
|
if isinstance(value, bool):
|
||||||
|
return value
|
||||||
|
if isinstance(value, str):
|
||||||
|
lowered = value.strip().lower()
|
||||||
|
if lowered in TRUTHY_STRINGS:
|
||||||
|
return True
|
||||||
|
if lowered in FALSY_STRINGS:
|
||||||
|
return False
|
||||||
|
return default
|
||||||
|
return bool(value)
|
||||||
|
|
||||||
|
|
||||||
def env_var_enabled(name: str, default: str = "") -> bool:
|
def env_var_enabled(name: str, default: str = "") -> bool:
|
||||||
"""Return True when an environment variable is set to a truthy value."""
|
"""Return True when an environment variable is set to a truthy value."""
|
||||||
return is_truthy_value(os.getenv(name, default), default=False)
|
return is_truthy_value(os.getenv(name, default), default=False)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue