mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-31 19:16:29 +00:00
test: pair load_config_readonly stubs at pruned-file sites (post-merge with main's readonly sweep)
Main's 243c9182b1/a16fd675df/7142dc4580 added load_config_readonly sibling stubs across 38 files; our pruned versions of 11 of those files kept only the load_config stubs. Re-applied the pairing at every surviving site (26 patch()/setattr sites) — same return_value/ side_effect as the adjacent load_config stub. 494 tests green across the 11 files.
This commit is contained in:
commit
3913ac2ba0
39 changed files with 221 additions and 82 deletions
|
|
@ -840,7 +840,7 @@ def init_agent(
|
|||
# sessions with >5-minute pauses between turns (#14971).
|
||||
agent._cache_ttl = "5m"
|
||||
try:
|
||||
from hermes_cli.config import load_config as _load_pc_cfg
|
||||
from hermes_cli.config import load_config_readonly as _load_pc_cfg
|
||||
|
||||
_pc_cfg = _load_pc_cfg().get("prompt_caching", {}) or {}
|
||||
_ttl = _pc_cfg.get("cache_ttl", "5m")
|
||||
|
|
@ -1090,7 +1090,7 @@ def init_agent(
|
|||
# Guardrail config — read from config.yaml at init time.
|
||||
agent._bedrock_guardrail_config = None
|
||||
try:
|
||||
from hermes_cli.config import load_config as _load_br_cfg
|
||||
from hermes_cli.config import load_config_readonly as _load_br_cfg
|
||||
_gr = _load_br_cfg().get("bedrock", {}).get("guardrail", {})
|
||||
if _gr.get("guardrail_identifier") and _gr.get("guardrail_version"):
|
||||
agent._bedrock_guardrail_config = {
|
||||
|
|
@ -1477,7 +1477,7 @@ def init_agent(
|
|||
# reads the JSON files directly. See run_agent._save_session_log.
|
||||
agent._session_json_enabled = False
|
||||
try:
|
||||
from hermes_cli.config import load_config as _load_sess_cfg
|
||||
from hermes_cli.config import load_config_readonly as _load_sess_cfg
|
||||
_sess_cfg = (_load_sess_cfg().get("sessions") or {})
|
||||
agent._session_json_enabled = bool(_sess_cfg.get("write_json_snapshots", False))
|
||||
except Exception:
|
||||
|
|
@ -1548,7 +1548,7 @@ def init_agent(
|
|||
|
||||
# Load config once for memory, skills, and compression sections
|
||||
try:
|
||||
from hermes_cli.config import load_config as _load_agent_config
|
||||
from hermes_cli.config import load_config_readonly as _load_agent_config
|
||||
_agent_cfg = _load_agent_config()
|
||||
except Exception:
|
||||
_agent_cfg = {}
|
||||
|
|
|
|||
|
|
@ -676,15 +676,15 @@ def build_or_headers(or_config: dict | None = None) -> dict:
|
|||
Overrides ``openrouter.response_cache_ttl`` in config.yaml.
|
||||
|
||||
*or_config* is the ``openrouter`` section from config.yaml. When *None*,
|
||||
falls back to reading config from disk via ``load_config()``.
|
||||
falls back to reading config from disk via ``load_config_readonly()``.
|
||||
"""
|
||||
headers = dict(_OR_HEADERS_BASE)
|
||||
|
||||
# Resolve config from disk if not provided.
|
||||
if or_config is None:
|
||||
try:
|
||||
from hermes_cli.config import load_config
|
||||
or_config = load_config().get("openrouter", {})
|
||||
from hermes_cli.config import load_config_readonly
|
||||
or_config = load_config_readonly().get("openrouter", {})
|
||||
except Exception:
|
||||
or_config = {}
|
||||
|
||||
|
|
@ -2317,8 +2317,8 @@ def _read_main_model() -> str:
|
|||
if isinstance(override, str) and override.strip():
|
||||
return override.strip()
|
||||
try:
|
||||
from hermes_cli.config import load_config
|
||||
cfg = load_config()
|
||||
from hermes_cli.config import load_config_readonly
|
||||
cfg = load_config_readonly()
|
||||
model_cfg = cfg.get("model", {})
|
||||
if isinstance(model_cfg, str) and model_cfg.strip():
|
||||
return model_cfg.strip()
|
||||
|
|
@ -2344,8 +2344,8 @@ def _read_main_provider() -> str:
|
|||
if isinstance(override, str) and override.strip():
|
||||
return override.strip().lower()
|
||||
try:
|
||||
from hermes_cli.config import load_config
|
||||
cfg = load_config()
|
||||
from hermes_cli.config import load_config_readonly
|
||||
cfg = load_config_readonly()
|
||||
model_cfg = cfg.get("model", {})
|
||||
if isinstance(model_cfg, dict):
|
||||
provider = model_cfg.get("provider", "")
|
||||
|
|
@ -3040,12 +3040,12 @@ def _try_azure_foundry(
|
|||
try:
|
||||
from hermes_cli.runtime_provider import _resolve_azure_foundry_runtime
|
||||
from hermes_cli.auth import AuthError
|
||||
from hermes_cli.config import load_config
|
||||
from hermes_cli.config import load_config_readonly
|
||||
except ImportError:
|
||||
return None, None
|
||||
|
||||
try:
|
||||
cfg = load_config()
|
||||
cfg = load_config_readonly()
|
||||
model_cfg = cfg.get("model") if isinstance(cfg, dict) else {}
|
||||
if not isinstance(model_cfg, dict):
|
||||
model_cfg = {}
|
||||
|
|
@ -3159,8 +3159,8 @@ def _try_anthropic(explicit_api_key: str = None) -> Tuple[Optional[Any], Optiona
|
|||
# see issue #52608.
|
||||
base_url = _pool_runtime_base_url(entry, _ANTHROPIC_DEFAULT_BASE_URL) if pool_present else _ANTHROPIC_DEFAULT_BASE_URL
|
||||
try:
|
||||
from hermes_cli.config import load_config
|
||||
cfg = load_config()
|
||||
from hermes_cli.config import load_config_readonly
|
||||
cfg = load_config_readonly()
|
||||
model_cfg = cfg.get("model")
|
||||
if isinstance(model_cfg, dict):
|
||||
cfg_provider = str(model_cfg.get("provider") or "").strip().lower()
|
||||
|
|
@ -4764,10 +4764,10 @@ def _try_main_fallback_chain(
|
|||
participate in the same order as the main agent.
|
||||
"""
|
||||
try:
|
||||
from hermes_cli.config import load_config
|
||||
from hermes_cli.config import load_config_readonly
|
||||
from hermes_cli.fallback_config import get_fallback_chain
|
||||
|
||||
chain = get_fallback_chain(load_config())
|
||||
chain = get_fallback_chain(load_config_readonly())
|
||||
except Exception as exc:
|
||||
logger.debug("Auxiliary %s: could not load main fallback chain: %s", task or "call", exc)
|
||||
return None, None, ""
|
||||
|
|
@ -5986,11 +5986,11 @@ def _main_model_supports_vision(provider: str, model: Optional[str]) -> bool:
|
|||
"""
|
||||
try:
|
||||
from agent.image_routing import _lookup_supports_vision
|
||||
from hermes_cli.config import load_config
|
||||
from hermes_cli.config import load_config_readonly
|
||||
except ImportError:
|
||||
return True
|
||||
try:
|
||||
supports = _lookup_supports_vision(provider, model, load_config())
|
||||
supports = _lookup_supports_vision(provider, model, load_config_readonly())
|
||||
except Exception: # pragma: no cover - defensive
|
||||
return True
|
||||
if supports is None:
|
||||
|
|
@ -6959,8 +6959,8 @@ def _get_auxiliary_task_config(task: str) -> Dict[str, Any]:
|
|||
if not task:
|
||||
return {}
|
||||
try:
|
||||
from hermes_cli.config import load_config
|
||||
config = load_config()
|
||||
from hermes_cli.config import load_config_readonly
|
||||
config = load_config_readonly()
|
||||
except ImportError:
|
||||
return {}
|
||||
aux = config.get("auxiliary", {}) if isinstance(config, dict) else {}
|
||||
|
|
|
|||
|
|
@ -70,8 +70,8 @@ def _resolve_review_runtime(agent: Any) -> Dict[str, Any]:
|
|||
"routed": False,
|
||||
}
|
||||
try:
|
||||
from hermes_cli.config import load_config
|
||||
cfg = load_config()
|
||||
from hermes_cli.config import load_config_readonly
|
||||
cfg = load_config_readonly()
|
||||
except Exception:
|
||||
return parent
|
||||
aux = cfg.get("auxiliary", {}) if isinstance(cfg.get("auxiliary"), dict) else {}
|
||||
|
|
|
|||
|
|
@ -337,9 +337,9 @@ def _coding_mode(config: Optional[dict[str, Any]]) -> str:
|
|||
"""Return the normalized ``agent.coding_context`` mode (auto/focus/on/off)."""
|
||||
if config is None:
|
||||
try:
|
||||
from hermes_cli.config import load_config
|
||||
from hermes_cli.config import load_config_readonly
|
||||
|
||||
config = load_config()
|
||||
config = load_config_readonly()
|
||||
except Exception:
|
||||
config = {}
|
||||
raw = ((config or {}).get("agent", {}) or {}).get("coding_context", "auto")
|
||||
|
|
|
|||
|
|
@ -138,8 +138,8 @@ def is_paused() -> bool:
|
|||
def _load_config() -> Dict[str, Any]:
|
||||
"""Read curator.* config from ~/.hermes/config.yaml. Tolerates missing file."""
|
||||
try:
|
||||
from hermes_cli.config import load_config
|
||||
cfg = load_config()
|
||||
from hermes_cli.config import load_config_readonly
|
||||
cfg = load_config_readonly()
|
||||
except Exception as e:
|
||||
logger.debug("Failed to load config for curator: %s", e)
|
||||
return {}
|
||||
|
|
@ -1875,9 +1875,9 @@ def _run_llm_review(prompt: str) -> Dict[str, Any]:
|
|||
_acp_args = None
|
||||
_model_name = ""
|
||||
try:
|
||||
from hermes_cli.config import load_config
|
||||
from hermes_cli.config import load_config_readonly
|
||||
from hermes_cli.runtime_provider import resolve_runtime_provider
|
||||
_cfg = load_config()
|
||||
_cfg = load_config_readonly()
|
||||
_binding = _resolve_review_runtime(_cfg)
|
||||
_provider, _model_name = _binding.provider, _binding.model
|
||||
_rp = resolve_runtime_provider(
|
||||
|
|
|
|||
|
|
@ -147,8 +147,8 @@ def _utc_id(now: Optional[datetime] = None) -> str:
|
|||
|
||||
def _load_config() -> Dict[str, Any]:
|
||||
try:
|
||||
from hermes_cli.config import load_config
|
||||
cfg = load_config()
|
||||
from hermes_cli.config import load_config_readonly
|
||||
cfg = load_config_readonly()
|
||||
except Exception as e:
|
||||
logger.debug("Failed to load config for curator backup: %s", e)
|
||||
return {}
|
||||
|
|
|
|||
|
|
@ -197,8 +197,8 @@ def _config_language_cached() -> str | None:
|
|||
(e.g. after the setup wizard).
|
||||
"""
|
||||
try:
|
||||
from hermes_cli.config import load_config
|
||||
cfg = load_config()
|
||||
from hermes_cli.config import load_config_readonly
|
||||
cfg = load_config_readonly()
|
||||
lang = (cfg.get("display") or {}).get("language")
|
||||
if lang:
|
||||
return _normalize_lang(lang)
|
||||
|
|
|
|||
|
|
@ -91,9 +91,9 @@ def get_active_provider() -> Optional[ImageGenProvider]:
|
|||
"""
|
||||
configured: Optional[str] = None
|
||||
try:
|
||||
from hermes_cli.config import load_config
|
||||
from hermes_cli.config import load_config_readonly
|
||||
|
||||
cfg = load_config()
|
||||
cfg = load_config_readonly()
|
||||
section = cfg.get("image_gen") if isinstance(cfg, dict) else None
|
||||
if isinstance(section, dict):
|
||||
raw = section.get("provider")
|
||||
|
|
|
|||
|
|
@ -196,8 +196,8 @@ class LSPService:
|
|||
itself returns ``is_active()`` False when LSP is disabled.
|
||||
"""
|
||||
try:
|
||||
from hermes_cli.config import load_config
|
||||
cfg = load_config()
|
||||
from hermes_cli.config import load_config_readonly
|
||||
cfg = load_config_readonly()
|
||||
except Exception as e: # noqa: BLE001
|
||||
logger.debug("LSP config load failed: %s", e)
|
||||
return None
|
||||
|
|
|
|||
|
|
@ -210,8 +210,8 @@ def _resolve_trust_policy(plugin_id: str) -> _TrustPolicy:
|
|||
return _TrustPolicy(plugin_id="")
|
||||
|
||||
try:
|
||||
from hermes_cli.config import load_config
|
||||
config = load_config() or {}
|
||||
from hermes_cli.config import load_config_readonly
|
||||
config = load_config_readonly() or {}
|
||||
except Exception: # pragma: no cover — config IO failure
|
||||
return _TrustPolicy(plugin_id=plugin_id)
|
||||
|
||||
|
|
|
|||
|
|
@ -1246,10 +1246,10 @@ def build_environment_hints() -> str:
|
|||
extra = (os.getenv("HERMES_ENVIRONMENT_HINT") or "").strip()
|
||||
if not extra:
|
||||
try:
|
||||
from hermes_cli.config import load_config
|
||||
from hermes_cli.config import load_config_readonly
|
||||
|
||||
extra = str(
|
||||
(load_config().get("agent", {}) or {}).get("environment_hint", "")
|
||||
(load_config_readonly().get("agent", {}) or {}).get("environment_hint", "")
|
||||
).strip()
|
||||
except Exception as e:
|
||||
logger.debug("Could not read agent.environment_hint from config: %s", e)
|
||||
|
|
@ -1300,9 +1300,9 @@ def _get_context_file_max_chars(context_length: Optional[int] = None) -> int:
|
|||
3. ``CONTEXT_FILE_MAX_CHARS`` (20K) as the upstream-compatible fallback.
|
||||
"""
|
||||
try:
|
||||
from hermes_cli.config import load_config
|
||||
from hermes_cli.config import load_config_readonly
|
||||
|
||||
val = load_config().get("context_file_max_chars")
|
||||
val = load_config_readonly().get("context_file_max_chars")
|
||||
if isinstance(val, (int, float)) and val > 0:
|
||||
return int(val)
|
||||
except Exception as e:
|
||||
|
|
|
|||
|
|
@ -25,9 +25,9 @@ _INLINE_SHELL_MAX_OUTPUT = 4000
|
|||
def load_skills_config() -> dict:
|
||||
"""Load the ``skills`` section of config.yaml (best-effort)."""
|
||||
try:
|
||||
from hermes_cli.config import load_config
|
||||
from hermes_cli.config import load_config_readonly
|
||||
|
||||
cfg = load_config() or {}
|
||||
cfg = load_config_readonly() or {}
|
||||
skills_cfg = cfg.get("skills")
|
||||
if isinstance(skills_cfg, dict):
|
||||
return skills_cfg
|
||||
|
|
|
|||
|
|
@ -43,10 +43,10 @@ _TITLE_PROMPT_PINNED_LANGUAGE = (
|
|||
def _title_language() -> str:
|
||||
"""Return configured title language, or empty string to match the user."""
|
||||
try:
|
||||
from hermes_cli.config import load_config
|
||||
from hermes_cli.config import load_config_readonly
|
||||
|
||||
return str(
|
||||
((load_config() or {}).get("auxiliary") or {})
|
||||
((load_config_readonly() or {}).get("auxiliary") or {})
|
||||
.get("title_generation", {})
|
||||
.get("language", "")
|
||||
).strip()
|
||||
|
|
|
|||
|
|
@ -149,9 +149,9 @@ def verify_on_stop_enabled(config: dict[str, Any] | None = None) -> bool:
|
|||
return env.strip().lower() not in {"0", "false", "no", "off"}
|
||||
if config is None:
|
||||
try:
|
||||
from hermes_cli.config import load_config
|
||||
from hermes_cli.config import load_config_readonly
|
||||
|
||||
config = load_config()
|
||||
config = load_config_readonly()
|
||||
except Exception:
|
||||
config = {}
|
||||
agent_cfg = (config or {}).get("agent") if isinstance(config, dict) else None
|
||||
|
|
|
|||
|
|
@ -84,9 +84,9 @@ def get_active_provider() -> Optional[VideoGenProvider]:
|
|||
"""
|
||||
configured: Optional[str] = None
|
||||
try:
|
||||
from hermes_cli.config import load_config
|
||||
from hermes_cli.config import load_config_readonly
|
||||
|
||||
cfg = load_config()
|
||||
cfg = load_config_readonly()
|
||||
section = cfg.get("video_gen") if isinstance(cfg, dict) else None
|
||||
if isinstance(section, dict):
|
||||
raw = section.get("provider")
|
||||
|
|
|
|||
|
|
@ -98,9 +98,9 @@ def get_provider(name: str) -> Optional[WebSearchProvider]:
|
|||
def _read_config_key(*path: str) -> Optional[str]:
|
||||
"""Resolve a dotted config key from ``config.yaml``. Returns None on miss."""
|
||||
try:
|
||||
from hermes_cli.config import load_config
|
||||
from hermes_cli.config import load_config_readonly
|
||||
|
||||
cfg = load_config()
|
||||
cfg = load_config_readonly()
|
||||
cur = cfg
|
||||
for segment in path:
|
||||
if not isinstance(cur, dict):
|
||||
|
|
|
|||
|
|
@ -1280,6 +1280,15 @@ def _normalize_custom_provider_entry(
|
|||
if not isinstance(entry, dict):
|
||||
return None
|
||||
|
||||
# Shallow-copy before the alias normalization below writes into the
|
||||
# entry: callers (get_compatible_custom_providers,
|
||||
# providers_dict_to_custom_providers) pass live sub-dicts from
|
||||
# load_config_readonly()'s shared cache, and mutating those both
|
||||
# violates the cache's no-mutation contract and leaks duplicated
|
||||
# alias keys back into config.yaml through any later
|
||||
# save_config(load_config()) round-trip.
|
||||
entry = dict(entry)
|
||||
|
||||
# Accept camelCase aliases commonly used in hand-written configs.
|
||||
_CAMEL_ALIASES: Dict[str, str] = {
|
||||
"apiKey": "api_key",
|
||||
|
|
@ -1390,7 +1399,10 @@ def _normalize_custom_provider_entry(
|
|||
|
||||
models = entry.get("models")
|
||||
if isinstance(models, dict) and models:
|
||||
normalized["models"] = models
|
||||
# Shallow-copy: `entry` may alias a cached config sub-dict, and the
|
||||
# normalized entry escapes into long-lived runtime state
|
||||
# (agent._custom_providers) — don't share the cached models mapping.
|
||||
normalized["models"] = dict(models)
|
||||
elif isinstance(models, list) and models:
|
||||
# Hand-edited configs (and older Hermes versions) may write
|
||||
# ``models`` as a plain list of ids or as ``[{id: ...}]`` rows.
|
||||
|
|
|
|||
|
|
@ -175,6 +175,7 @@ class TestResolveTaskProviderModel:
|
|||
lambda cfg, name: preset,
|
||||
)
|
||||
monkeypatch.setattr("hermes_cli.config.load_config", lambda: {"moa": {}})
|
||||
monkeypatch.setattr("hermes_cli.config.load_config_readonly", lambda: {"moa": {}})
|
||||
|
||||
resolved_provider, model, base_url, api_key, api_mode = _resolve_task_provider_model(
|
||||
task="title_generation",
|
||||
|
|
@ -210,6 +211,7 @@ class TestResolveTaskProviderModel:
|
|||
lambda cfg, name: preset,
|
||||
)
|
||||
monkeypatch.setattr("hermes_cli.config.load_config", lambda: {"moa": {}})
|
||||
monkeypatch.setattr("hermes_cli.config.load_config_readonly", lambda: {"moa": {}})
|
||||
|
||||
resolved_provider, model, base_url, api_key, api_mode = _resolve_task_provider_model(
|
||||
task="title_generation",
|
||||
|
|
@ -231,6 +233,7 @@ class TestResolveTaskProviderModel:
|
|||
lambda cfg, name: (_ for _ in ()).throw(KeyError("gone-preset")),
|
||||
)
|
||||
monkeypatch.setattr("hermes_cli.config.load_config", lambda: {"moa": {}})
|
||||
monkeypatch.setattr("hermes_cli.config.load_config_readonly", lambda: {"moa": {}})
|
||||
|
||||
resolved_provider, model, base_url, api_key, api_mode = _resolve_task_provider_model(
|
||||
task="title_generation",
|
||||
|
|
@ -2166,7 +2169,7 @@ class TestAuxiliaryTaskExtraBody:
|
|||
}
|
||||
}
|
||||
|
||||
with patch("hermes_cli.config.load_config", return_value=config), patch(
|
||||
with patch("hermes_cli.config.load_config", return_value=config), patch("hermes_cli.config.load_config_readonly", return_value=config), patch(
|
||||
"agent.auxiliary_client._get_cached_client",
|
||||
return_value=(client, "glm-4.5-air"),
|
||||
):
|
||||
|
|
@ -2197,7 +2200,7 @@ class TestAuxiliaryTaskExtraBody:
|
|||
}
|
||||
}
|
||||
|
||||
with patch("hermes_cli.config.load_config", return_value=config), patch(
|
||||
with patch("hermes_cli.config.load_config", return_value=config), patch("hermes_cli.config.load_config_readonly", return_value=config), patch(
|
||||
"agent.auxiliary_client._get_cached_client",
|
||||
return_value=(client, "glm-4.5-air"),
|
||||
):
|
||||
|
|
@ -2223,7 +2226,7 @@ class TestAuxiliaryTaskExtraBody:
|
|||
from agent.auxiliary_client import _get_task_extra_body
|
||||
|
||||
config = {"auxiliary": {moa_task: {"reasoning_effort": "xhigh"}}}
|
||||
with patch("hermes_cli.config.load_config", return_value=config), \
|
||||
with patch("hermes_cli.config.load_config", return_value=config), patch("hermes_cli.config.load_config_readonly", return_value=config), \
|
||||
caplog.at_level(logging.WARNING, logger="agent.auxiliary_client"):
|
||||
result = _get_task_extra_body(moa_task)
|
||||
|
||||
|
|
@ -3950,7 +3953,7 @@ class TestCustomEndpointApiKeyInheritance:
|
|||
captured.update(kwargs)
|
||||
return MagicMock()
|
||||
|
||||
with patch("hermes_cli.config.load_config", return_value=fake_config), \
|
||||
with patch("hermes_cli.config.load_config", return_value=fake_config), patch("hermes_cli.config.load_config_readonly", return_value=fake_config), \
|
||||
patch.object(ac, "_create_openai_client", side_effect=_capture_create):
|
||||
client, model = resolve_provider_client(
|
||||
"custom",
|
||||
|
|
@ -3978,7 +3981,7 @@ class TestCustomEndpointApiKeyInheritance:
|
|||
captured.update(kwargs)
|
||||
return MagicMock()
|
||||
|
||||
with patch("hermes_cli.config.load_config", return_value=fake_config), \
|
||||
with patch("hermes_cli.config.load_config", return_value=fake_config), patch("hermes_cli.config.load_config_readonly", return_value=fake_config), \
|
||||
patch.object(ac, "_create_openai_client", side_effect=_capture_create):
|
||||
client, model = resolve_provider_client(
|
||||
"custom",
|
||||
|
|
@ -4005,7 +4008,7 @@ class TestCustomEndpointApiKeyInheritance:
|
|||
|
||||
with patch.object(ac, "_RUNTIME_MAIN_API_KEY", "sk-runtime-key"), \
|
||||
patch.object(ac, "_RUNTIME_MAIN_BASE_URL", "https://gw.example.com/v1"), \
|
||||
patch("hermes_cli.config.load_config", return_value={"model": {}}), \
|
||||
patch("hermes_cli.config.load_config", return_value={"model": {}}), patch("hermes_cli.config.load_config_readonly", return_value={"model": {}}), \
|
||||
patch.object(ac, "_create_openai_client", side_effect=_capture_create):
|
||||
client, model = resolve_provider_client(
|
||||
"custom",
|
||||
|
|
@ -4037,7 +4040,7 @@ class TestCustomEndpointApiKeyInheritance:
|
|||
captured.update(kwargs)
|
||||
return MagicMock()
|
||||
|
||||
with patch("hermes_cli.config.load_config", return_value=fake_config), \
|
||||
with patch("hermes_cli.config.load_config", return_value=fake_config), patch("hermes_cli.config.load_config_readonly", return_value=fake_config), \
|
||||
patch.object(ac, "_create_openai_client", side_effect=_capture_create):
|
||||
client, model = resolve_provider_client(
|
||||
"custom",
|
||||
|
|
|
|||
|
|
@ -73,6 +73,10 @@ def patch_load_config(monkeypatch):
|
|||
"hermes_cli.config.load_config",
|
||||
lambda: {"model": model_cfg},
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
"hermes_cli.config.load_config_readonly",
|
||||
lambda: {"model": model_cfg},
|
||||
)
|
||||
return _apply
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -59,6 +59,8 @@ def _make_codex_agent(monkeypatch, tmp_path: Path, *, show_notice: bool):
|
|||
from hermes_cli import config as config_mod
|
||||
|
||||
monkeypatch.setattr(config_mod, "load_config", lambda: _config(show_notice=show_notice))
|
||||
|
||||
monkeypatch.setattr(config_mod, "load_config_readonly", lambda: _config(show_notice=show_notice))
|
||||
db = SessionDB(db_path=tmp_path / "state.db")
|
||||
stdout = io.StringIO()
|
||||
|
||||
|
|
|
|||
|
|
@ -46,6 +46,9 @@ def _make_agent(monkeypatch, tmp_path: Path, *, max_attempts=None):
|
|||
monkeypatch.setattr(
|
||||
config_mod, "load_config", lambda: _config(max_attempts=max_attempts)
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
config_mod, "load_config_readonly", lambda: _config(max_attempts=max_attempts)
|
||||
)
|
||||
db = SessionDB(db_path=tmp_path / "state.db")
|
||||
with contextlib.redirect_stdout(io.StringIO()):
|
||||
agent = AIAgent(
|
||||
|
|
|
|||
|
|
@ -637,6 +637,10 @@ def test_review_fork_forwards_runtime_pool_and_overrides(curator_env, monkeypatc
|
|||
"hermes_cli.config.load_config",
|
||||
lambda: {"model": {"provider": "custom:hyper-charm", "default": "glm-5.2"}},
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
"hermes_cli.config.load_config_readonly",
|
||||
lambda: {"model": {"provider": "custom:hyper-charm", "default": "glm-5.2"}},
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
"hermes_cli.runtime_provider.resolve_runtime_provider",
|
||||
_fake_resolve_runtime_provider,
|
||||
|
|
@ -660,6 +664,10 @@ def test_review_fork_uses_runtime_model_and_output_cap(curator_env, monkeypatch)
|
|||
"hermes_cli.config.load_config",
|
||||
lambda: {"model": {"provider": "custom:gateway", "default": "gateway"}},
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
"hermes_cli.config.load_config_readonly",
|
||||
lambda: {"model": {"provider": "custom:gateway", "default": "gateway"}},
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
"hermes_cli.runtime_provider.resolve_runtime_provider",
|
||||
lambda **_kwargs: {
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ class TestBuildOrHeaders:
|
|||
"""When load_config() fails, build_or_headers still returns base headers."""
|
||||
from agent.auxiliary_client import build_or_headers
|
||||
|
||||
with patch("hermes_cli.config.load_config", side_effect=RuntimeError("boom")):
|
||||
with patch("hermes_cli.config.load_config", side_effect=RuntimeError("boom")), patch("hermes_cli.config.load_config_readonly", side_effect=RuntimeError("boom")):
|
||||
headers = build_or_headers(or_config=None)
|
||||
# Should have base attribution but no cache headers
|
||||
assert "HTTP-Referer" in headers
|
||||
|
|
|
|||
|
|
@ -39,6 +39,8 @@ def _make_agent(monkeypatch, tmp_path: Path, **prune_keys):
|
|||
from hermes_cli import config as config_mod
|
||||
|
||||
monkeypatch.setattr(config_mod, "load_config", lambda: _config(**prune_keys))
|
||||
|
||||
monkeypatch.setattr(config_mod, "load_config_readonly", lambda: _config(**prune_keys))
|
||||
db = SessionDB(db_path=tmp_path / "state.db")
|
||||
with contextlib.redirect_stdout(io.StringIO()):
|
||||
agent = AIAgent(
|
||||
|
|
|
|||
|
|
@ -98,6 +98,7 @@ class TestTruncateContent:
|
|||
return {}
|
||||
|
||||
monkeypatch.setattr("hermes_cli.config.load_config", default_load_config)
|
||||
monkeypatch.setattr("hermes_cli.config.load_config_readonly", default_load_config)
|
||||
|
||||
|
||||
|
||||
|
|
@ -116,6 +117,7 @@ class TestTruncateContent:
|
|||
return {"context_file_max_chars": 120}
|
||||
|
||||
monkeypatch.setattr("hermes_cli.config.load_config", fake_load_config)
|
||||
monkeypatch.setattr("hermes_cli.config.load_config_readonly", fake_load_config)
|
||||
|
||||
_truncate_content("x" * 180, "warning.md")
|
||||
|
||||
|
|
@ -133,6 +135,7 @@ class TestTruncateContent:
|
|||
return {"context_file_max_chars": 120}
|
||||
|
||||
monkeypatch.setattr("hermes_cli.config.load_config", fake_load_config)
|
||||
monkeypatch.setattr("hermes_cli.config.load_config_readonly", fake_load_config)
|
||||
|
||||
# Generate a warning in a fresh child context, then assert it did NOT
|
||||
# leak into the parent context's accumulator.
|
||||
|
|
@ -161,6 +164,7 @@ class TestDynamicContextFileCap:
|
|||
def _no_explicit_config(self, monkeypatch):
|
||||
# No explicit context_file_max_chars → dynamic path is eligible.
|
||||
monkeypatch.setattr("hermes_cli.config.load_config", lambda: {})
|
||||
monkeypatch.setattr("hermes_cli.config.load_config_readonly", lambda: {})
|
||||
|
||||
|
||||
def test_dynamic_scales_above_floor_for_large_window(self):
|
||||
|
|
@ -179,6 +183,10 @@ class TestDynamicContextFileCap:
|
|||
"hermes_cli.config.load_config",
|
||||
lambda: {"context_file_max_chars": 1_000},
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
"hermes_cli.config.load_config_readonly",
|
||||
lambda: {"context_file_max_chars": 1_000},
|
||||
)
|
||||
assert _get_context_file_max_chars(200_000) == 1_000
|
||||
|
||||
def test_large_window_avoids_truncation_of_midsize_doc(self):
|
||||
|
|
|
|||
|
|
@ -22,11 +22,12 @@ class TestGenerateTitle:
|
|||
def test_title_language_reads_config(self):
|
||||
cfg = {"auxiliary": {"title_generation": {"language": " French "}}}
|
||||
|
||||
with patch("hermes_cli.config.load_config", return_value=cfg):
|
||||
with patch("hermes_cli.config.load_config", return_value=cfg), patch("hermes_cli.config.load_config_readonly", return_value=cfg):
|
||||
assert _title_language() == "French"
|
||||
with patch("hermes_cli.config.load_config", return_value={}):
|
||||
with patch("hermes_cli.config.load_config", return_value={}), patch("hermes_cli.config.load_config_readonly", return_value={}):
|
||||
assert _title_language() == ""
|
||||
with patch("hermes_cli.config.load_config", side_effect=RuntimeError("bad config")):
|
||||
with patch("hermes_cli.config.load_config", side_effect=RuntimeError("bad config")), \
|
||||
patch("hermes_cli.config.load_config_readonly", side_effect=RuntimeError("bad config")):
|
||||
assert _title_language() == ""
|
||||
|
||||
def test_default_timeout_delegates_to_auxiliary_config(self):
|
||||
|
|
|
|||
62
tests/hermes_cli/test_custom_provider_normalize_no_mutate.py
Normal file
62
tests/hermes_cli/test_custom_provider_normalize_no_mutate.py
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
"""Regression: _normalize_custom_provider_entry must never mutate its input.
|
||||
|
||||
Provider entries frequently alias cached config dicts (load_config_readonly()
|
||||
shares one dict process-wide). The normalizer's alias rewrites
|
||||
(api_key_env -> key_env, camelCase -> snake_case) used to write INTO the
|
||||
caller's entry, corrupting the shared cache for every subsequent reader.
|
||||
"""
|
||||
|
||||
import copy
|
||||
|
||||
from hermes_cli.config import (
|
||||
_normalize_custom_provider_entry,
|
||||
get_compatible_custom_providers,
|
||||
providers_dict_to_custom_providers,
|
||||
)
|
||||
|
||||
|
||||
def test_normalizer_does_not_mutate_entry_api_key_env():
|
||||
entry = {"base_url": "https://x.example/v1", "api_key_env": "MY_KEY"}
|
||||
snapshot = copy.deepcopy(entry)
|
||||
out = _normalize_custom_provider_entry(entry, provider_key="p")
|
||||
assert entry == snapshot, "input entry must not gain key_env"
|
||||
assert out is not None and out.get("key_env") == "MY_KEY"
|
||||
|
||||
|
||||
def test_normalizer_does_not_mutate_entry_camel_aliases():
|
||||
entry = {"baseUrl": "https://x.example/v1", "apiKey": "sk-test-not-real"}
|
||||
snapshot = copy.deepcopy(entry)
|
||||
_normalize_custom_provider_entry(entry, provider_key="p")
|
||||
assert entry == snapshot, "camelCase aliasing must not write into the input"
|
||||
|
||||
|
||||
def test_providers_dict_roundtrip_leaves_cached_config_untouched():
|
||||
config = {
|
||||
"providers": {
|
||||
"myprov": {
|
||||
"base_url": "https://x.example/v1",
|
||||
"api_key_env": "MY_KEY",
|
||||
"models": {"m1": {"context_length": 8192}},
|
||||
}
|
||||
}
|
||||
}
|
||||
snapshot = copy.deepcopy(config)
|
||||
providers_dict_to_custom_providers(config["providers"])
|
||||
assert config == snapshot
|
||||
|
||||
get_compatible_custom_providers(config)
|
||||
assert config == snapshot
|
||||
|
||||
|
||||
def test_normalized_models_mapping_is_not_shared_with_input():
|
||||
entry = {
|
||||
"base_url": "https://x.example/v1",
|
||||
"api_key": "sk-test-not-real",
|
||||
"models": {"m1": {}},
|
||||
}
|
||||
out = _normalize_custom_provider_entry(entry, provider_key="p")
|
||||
assert out is not None
|
||||
out["models"]["injected"] = {}
|
||||
assert "injected" not in entry["models"], (
|
||||
"normalized models mapping must not alias the caller's dict"
|
||||
)
|
||||
|
|
@ -17,7 +17,8 @@ def _make_agent(api_max_retries=None):
|
|||
cfg["agent"]["api_max_retries"] = api_max_retries
|
||||
|
||||
with patch("run_agent.OpenAI"), \
|
||||
patch("hermes_cli.config.load_config", return_value=cfg):
|
||||
patch("hermes_cli.config.load_config", return_value=cfg), \
|
||||
patch("hermes_cli.config.load_config_readonly", return_value=cfg):
|
||||
return AIAgent(
|
||||
api_key="test-key",
|
||||
base_url="https://openrouter.ai/api/v1",
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ class _FakeAgent:
|
|||
def test_routing_auto_inherits_parent_and_downgrades_codex_app_server():
|
||||
agent = _FakeAgent()
|
||||
cfg = {"auxiliary": {"background_review": {"provider": "auto", "model": ""}}}
|
||||
with patch("hermes_cli.config.load_config", return_value=cfg):
|
||||
with patch("hermes_cli.config.load_config", return_value=cfg), patch("hermes_cli.config.load_config_readonly", return_value=cfg):
|
||||
rt = br._resolve_review_runtime(agent)
|
||||
assert rt["routed"] is False
|
||||
assert rt["provider"] == "openai-codex"
|
||||
|
|
@ -64,7 +64,7 @@ def test_routing_to_different_model_marks_routed_and_resolves_credentials():
|
|||
"request_overrides": {"extra_body": {"store": False}},
|
||||
"max_output_tokens": 2048,
|
||||
}
|
||||
with patch("hermes_cli.config.load_config", return_value=cfg), \
|
||||
with patch("hermes_cli.config.load_config", return_value=cfg), patch("hermes_cli.config.load_config_readonly", return_value=cfg), \
|
||||
patch("hermes_cli.runtime_provider.resolve_runtime_provider", return_value=fake_rp):
|
||||
rt = br._resolve_review_runtime(agent)
|
||||
assert rt["routed"] is True
|
||||
|
|
@ -81,7 +81,7 @@ def test_unrouted_runtime_keeps_parent_pool_and_overrides():
|
|||
agent._credential_pool = "parent-pool"
|
||||
agent.request_overrides = {"service_tier": "priority"}
|
||||
agent.max_tokens = 4096
|
||||
with patch("hermes_cli.config.load_config", return_value={}):
|
||||
with patch("hermes_cli.config.load_config", return_value={}), patch("hermes_cli.config.load_config_readonly", return_value={}):
|
||||
rt = br._resolve_review_runtime(agent)
|
||||
assert rt["credential_pool"] == "parent-pool"
|
||||
assert rt["request_overrides"] == {"service_tier": "priority"}
|
||||
|
|
@ -93,7 +93,7 @@ def test_routing_same_model_as_parent_is_not_routed():
|
|||
cfg = {"auxiliary": {"background_review": {
|
||||
"provider": "openrouter", "model": "anthropic/claude-opus-4.8",
|
||||
}}}
|
||||
with patch("hermes_cli.config.load_config", return_value=cfg):
|
||||
with patch("hermes_cli.config.load_config", return_value=cfg), patch("hermes_cli.config.load_config_readonly", return_value=cfg):
|
||||
rt = br._resolve_review_runtime(agent)
|
||||
assert rt["routed"] is False # same model/provider → keep full-replay path
|
||||
|
||||
|
|
@ -103,7 +103,7 @@ def test_routing_resolution_failure_falls_back_to_parent():
|
|||
cfg = {"auxiliary": {"background_review": {
|
||||
"provider": "openrouter", "model": "google/gemini-3-flash-preview",
|
||||
}}}
|
||||
with patch("hermes_cli.config.load_config", return_value=cfg), \
|
||||
with patch("hermes_cli.config.load_config", return_value=cfg), patch("hermes_cli.config.load_config_readonly", return_value=cfg), \
|
||||
patch("hermes_cli.runtime_provider.resolve_runtime_provider",
|
||||
side_effect=RuntimeError("boom")):
|
||||
rt = br._resolve_review_runtime(agent)
|
||||
|
|
|
|||
|
|
@ -230,7 +230,7 @@ def test_init_feasibility_check_uses_aux_context_override_from_config():
|
|||
mock_client.api_key = "sk-custom"
|
||||
|
||||
with (
|
||||
patch("hermes_cli.config.load_config", return_value=cfg),
|
||||
patch("hermes_cli.config.load_config", return_value=cfg), patch("hermes_cli.config.load_config_readonly", return_value=cfg),
|
||||
patch("run_agent.get_tool_definitions", return_value=[]),
|
||||
patch("run_agent.check_toolset_requirements", return_value={}),
|
||||
patch("run_agent.OpenAI"),
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ def _build_agent(model_cfg, custom_providers=None, model=None):
|
|||
|
||||
with (
|
||||
patch("hermes_cli.config.load_config", return_value=cfg),
|
||||
patch("hermes_cli.config.load_config_readonly", return_value=cfg),
|
||||
patch("agent.model_metadata.get_model_context_length", return_value=128_000),
|
||||
patch("run_agent.get_tool_definitions", return_value=[]),
|
||||
patch("run_agent.check_toolset_requirements", return_value={}),
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ def test_blank_memory_provider_does_not_auto_enable_honcho():
|
|||
honcho_cfg = SimpleNamespace(enabled=True, api_key="stale-key", base_url=None)
|
||||
|
||||
with (
|
||||
patch("hermes_cli.config.load_config", return_value=cfg),
|
||||
patch("hermes_cli.config.load_config", return_value=cfg), patch("hermes_cli.config.load_config_readonly", return_value=cfg),
|
||||
patch("hermes_cli.config.save_config") as save_config,
|
||||
patch(
|
||||
"plugins.memory.honcho.client.HonchoClientConfig.from_global_config",
|
||||
|
|
@ -64,7 +64,7 @@ def test_aiagent_forwards_user_id_alt_to_memory_provider():
|
|||
cfg = {"memory": {"provider": "recording"}, "agent": {}}
|
||||
|
||||
with (
|
||||
patch("hermes_cli.config.load_config", return_value=cfg),
|
||||
patch("hermes_cli.config.load_config", return_value=cfg), patch("hermes_cli.config.load_config_readonly", return_value=cfg),
|
||||
patch("plugins.memory.load_memory_provider", return_value=provider),
|
||||
patch("agent.model_metadata.get_model_context_length", return_value=204_800),
|
||||
patch("run_agent.get_tool_definitions", return_value=[]),
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ def test_plugin_engine_gets_model_thresholds_before_initial_update_model():
|
|||
}
|
||||
|
||||
with (
|
||||
patch("hermes_cli.config.load_config", return_value=cfg),
|
||||
patch("hermes_cli.config.load_config", return_value=cfg), patch("hermes_cli.config.load_config_readonly", return_value=cfg),
|
||||
patch("plugins.context_engine.load_context_engine", return_value=engine),
|
||||
patch("agent.model_metadata.get_model_context_length", return_value=1_000_000),
|
||||
patch("run_agent.get_tool_definitions", return_value=[]),
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ def test_plugin_engine_gets_context_length_on_init():
|
|||
cfg = {"context": {"engine": "stub"}, "agent": {}}
|
||||
|
||||
with (
|
||||
patch("hermes_cli.config.load_config", return_value=cfg),
|
||||
patch("hermes_cli.config.load_config", return_value=cfg), patch("hermes_cli.config.load_config_readonly", return_value=cfg),
|
||||
patch("plugins.context_engine.load_context_engine", return_value=engine),
|
||||
patch("agent.model_metadata.get_model_context_length", return_value=204_800),
|
||||
patch("run_agent.get_tool_definitions", return_value=[]),
|
||||
|
|
@ -82,7 +82,7 @@ def test_active_context_engine_tools_survive_explicit_platform_toolsets():
|
|||
assert "context_engine" in enabled_toolsets
|
||||
|
||||
with (
|
||||
patch("hermes_cli.config.load_config", return_value=cfg),
|
||||
patch("hermes_cli.config.load_config", return_value=cfg), patch("hermes_cli.config.load_config_readonly", return_value=cfg),
|
||||
patch("plugins.context_engine.load_context_engine", return_value=engine),
|
||||
patch("agent.model_metadata.get_model_context_length", return_value=204_800),
|
||||
patch("run_agent.get_tool_definitions", return_value=[]),
|
||||
|
|
@ -115,7 +115,7 @@ def test_plugin_engine_update_model_args():
|
|||
cfg = {"context": {"engine": "stub"}, "agent": {}}
|
||||
|
||||
with (
|
||||
patch("hermes_cli.config.load_config", return_value=cfg),
|
||||
patch("hermes_cli.config.load_config", return_value=cfg), patch("hermes_cli.config.load_config_readonly", return_value=cfg),
|
||||
patch("plugins.context_engine.load_context_engine", return_value=engine),
|
||||
patch("agent.model_metadata.get_model_context_length", return_value=131_072),
|
||||
patch("run_agent.get_tool_definitions", return_value=[]),
|
||||
|
|
@ -168,7 +168,7 @@ def test_codex_gpt55_autoraise_suppressed_for_plugin_engine():
|
|||
}
|
||||
|
||||
with (
|
||||
patch("hermes_cli.config.load_config", return_value=cfg),
|
||||
patch("hermes_cli.config.load_config", return_value=cfg), patch("hermes_cli.config.load_config_readonly", return_value=cfg),
|
||||
patch("plugins.context_engine.load_context_engine", return_value=engine),
|
||||
patch("agent.model_metadata.get_model_context_length", return_value=272_000),
|
||||
patch("run_agent.get_tool_definitions", return_value=[]),
|
||||
|
|
@ -194,7 +194,7 @@ def test_codex_gpt55_autoraise_still_applies_to_builtin_compressor():
|
|||
}
|
||||
|
||||
with (
|
||||
patch("hermes_cli.config.load_config", return_value=cfg),
|
||||
patch("hermes_cli.config.load_config", return_value=cfg), patch("hermes_cli.config.load_config_readonly", return_value=cfg),
|
||||
patch("agent.context_compressor.get_model_context_length", return_value=272_000),
|
||||
patch("run_agent.get_tool_definitions", return_value=[]),
|
||||
patch("run_agent.check_toolset_requirements", return_value={}),
|
||||
|
|
|
|||
|
|
@ -60,6 +60,11 @@ def _make_agent(monkeypatch, tmp_path: Path, *, max_attempts) -> AIAgent:
|
|||
monkeypatch.setattr(
|
||||
config_mod, "load_config", lambda: _config(max_attempts)
|
||||
)
|
||||
|
||||
monkeypatch.setattr(
|
||||
config_mod, "load_config_readonly", lambda: _config(max_attempts)
|
||||
|
||||
)
|
||||
db = SessionDB(db_path=tmp_path / "state.db")
|
||||
with (
|
||||
contextlib.redirect_stdout(io.StringIO()),
|
||||
|
|
|
|||
|
|
@ -99,6 +99,8 @@ def test_openrouter_headers_include_response_cache_when_enabled(mock_openai):
|
|||
|
||||
with patch("hermes_cli.config.load_config", return_value={
|
||||
"openrouter": {"response_cache": True, "response_cache_ttl": 600},
|
||||
}), patch("hermes_cli.config.load_config_readonly", return_value={
|
||||
"openrouter": {"response_cache": True, "response_cache_ttl": 600},
|
||||
}):
|
||||
agent._apply_client_headers_for_base_url("https://openrouter.ai/api/v1")
|
||||
|
||||
|
|
@ -130,6 +132,8 @@ def test_user_default_headers_override_sdk_user_agent(mock_openai):
|
|||
|
||||
with patch("hermes_cli.config.load_config", return_value={
|
||||
"model": {"default_headers": {"User-Agent": "curl/8.7.1", "X-Extra": "1"}},
|
||||
}), patch("hermes_cli.config.load_config_readonly", return_value={
|
||||
"model": {"default_headers": {"User-Agent": "curl/8.7.1", "X-Extra": "1"}},
|
||||
}):
|
||||
agent._apply_client_headers_for_base_url("http://localhost:8080/v1")
|
||||
|
||||
|
|
@ -159,6 +163,8 @@ def test_openrouter_headers_no_cache_when_disabled(mock_openai):
|
|||
|
||||
with patch("hermes_cli.config.load_config", return_value={
|
||||
"openrouter": {"response_cache": False},
|
||||
}), patch("hermes_cli.config.load_config_readonly", return_value={
|
||||
"openrouter": {"response_cache": False},
|
||||
}):
|
||||
agent._apply_client_headers_for_base_url("https://openrouter.ai/api/v1")
|
||||
|
||||
|
|
|
|||
|
|
@ -704,7 +704,7 @@ class TestInit:
|
|||
patch("run_agent.get_tool_definitions", return_value=[]),
|
||||
patch("run_agent.check_toolset_requirements", return_value={}),
|
||||
patch("run_agent.OpenAI"),
|
||||
patch("hermes_cli.config.load_config", return_value={}),
|
||||
patch("hermes_cli.config.load_config", return_value={}), patch("hermes_cli.config.load_config_readonly", return_value={}),
|
||||
):
|
||||
a = AIAgent(
|
||||
api_key="test-k...7890",
|
||||
|
|
@ -727,6 +727,9 @@ class TestInit:
|
|||
patch(
|
||||
"hermes_cli.config.load_config",
|
||||
return_value={"model": {"max_tokens": 4096}},
|
||||
), patch(
|
||||
"hermes_cli.config.load_config_readonly",
|
||||
return_value={"model": {"max_tokens": 4096}},
|
||||
),
|
||||
):
|
||||
a = AIAgent(
|
||||
|
|
@ -898,6 +901,9 @@ class TestToolUseEnforcementConfig:
|
|||
patch(
|
||||
"hermes_cli.config.load_config",
|
||||
return_value={"agent": {"tool_use_enforcement": tool_use_enforcement}},
|
||||
), patch(
|
||||
"hermes_cli.config.load_config_readonly",
|
||||
return_value={"agent": {"tool_use_enforcement": tool_use_enforcement}},
|
||||
),
|
||||
):
|
||||
a = AIAgent(
|
||||
|
|
@ -943,6 +949,9 @@ class TestToolUseEnforcementConfig:
|
|||
patch(
|
||||
"hermes_cli.config.load_config",
|
||||
return_value={"agent": {"tool_use_enforcement": True}},
|
||||
), patch(
|
||||
"hermes_cli.config.load_config_readonly",
|
||||
return_value={"agent": {"tool_use_enforcement": True}},
|
||||
),
|
||||
):
|
||||
a = AIAgent(
|
||||
|
|
@ -980,6 +989,9 @@ class TestTaskCompletionGuidance:
|
|||
patch(
|
||||
"hermes_cli.config.load_config",
|
||||
return_value={"agent": agent_cfg},
|
||||
), patch(
|
||||
"hermes_cli.config.load_config_readonly",
|
||||
return_value={"agent": agent_cfg},
|
||||
),
|
||||
):
|
||||
a = AIAgent(
|
||||
|
|
@ -1016,6 +1028,9 @@ class TestTaskCompletionGuidance:
|
|||
patch(
|
||||
"hermes_cli.config.load_config",
|
||||
return_value={"agent": {"task_completion_guidance": True}},
|
||||
), patch(
|
||||
"hermes_cli.config.load_config_readonly",
|
||||
return_value={"agent": {"task_completion_guidance": True}},
|
||||
),
|
||||
):
|
||||
a = AIAgent(
|
||||
|
|
@ -1048,6 +1063,9 @@ class TestEnvironmentProbeIntegration:
|
|||
patch(
|
||||
"hermes_cli.config.load_config",
|
||||
return_value={"agent": {"environment_probe": environment_probe}},
|
||||
), patch(
|
||||
"hermes_cli.config.load_config_readonly",
|
||||
return_value={"agent": {"environment_probe": environment_probe}},
|
||||
),
|
||||
):
|
||||
a = AIAgent(
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ def _make_direct_start_agent(
|
|||
cfg: dict, *, model: str, provider: str, base_url: str
|
||||
) -> AIAgent:
|
||||
with (
|
||||
patch("hermes_cli.config.load_config", return_value=cfg),
|
||||
patch("hermes_cli.config.load_config", return_value=cfg), patch("hermes_cli.config.load_config_readonly", return_value=cfg),
|
||||
patch("run_agent.get_tool_definitions", return_value=[]),
|
||||
patch("run_agent.check_toolset_requirements", return_value={}),
|
||||
patch("run_agent.OpenAI"),
|
||||
|
|
@ -265,6 +265,8 @@ def test_lmstudio_switch_uses_destination_context_and_verified_runtime(monkeypat
|
|||
return LMStudioLoadResult(100_000)
|
||||
|
||||
monkeypatch.setattr("hermes_cli.config.load_config", fake_load_config)
|
||||
|
||||
monkeypatch.setattr("hermes_cli.config.load_config_readonly", fake_load_config)
|
||||
monkeypatch.setattr("hermes_cli.config.get_compatible_custom_providers", fake_compatible)
|
||||
monkeypatch.setattr("hermes_cli.config.get_custom_provider_context_length", fake_provider_context)
|
||||
monkeypatch.setattr(AIAgent, "_ensure_lmstudio_runtime_loaded", fake_lmstudio_load)
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ def _make_agent(*tool_names: str, max_iterations: int = 10, config: dict | None
|
|||
patch("run_agent.get_tool_definitions", return_value=_make_tool_defs(*tool_names)),
|
||||
patch("run_agent.check_toolset_requirements", return_value={}),
|
||||
patch("hermes_cli.config.load_config", return_value=config or {}),
|
||||
patch("hermes_cli.config.load_config_readonly", return_value=config or {}),
|
||||
patch("run_agent.OpenAI"),
|
||||
):
|
||||
agent = AIAgent(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue