fix(config): kanban decompose and local-models status keep their fail-open config reads
Repointing the two _load_config copies at load_config_readonly() dropped the except-Exception guards the old copies had. load_config_readonly runs ensure_hermes_home(), which can raise FileNotFoundError / HomeInitializationError, so decompose_task (promises ok=False) and /api/local-models/status (garnish that must render degraded) would raise / 500 instead. The guard is restored at both sites with the same breadth the old code had.
This commit is contained in:
@@ -195,7 +195,10 @@ class _Routing:
|
||||
|
||||
def _load_routing() -> _Routing:
|
||||
from hermes_cli.config import load_config_readonly
|
||||
cfg = load_config_readonly()
|
||||
try:
|
||||
cfg = load_config_readonly()
|
||||
except Exception: # decompose_task promises ok=False, never a raise, on config trouble
|
||||
cfg = {}
|
||||
kanban_cfg = cfg.get("kanban", {}) if isinstance(cfg, dict) else {}
|
||||
roster, valid_names = _build_roster()
|
||||
return _Routing(
|
||||
|
||||
@@ -190,8 +190,13 @@ def _router_request(endpoint: Dict[str, Any], path: str, *, timeout: float, payl
|
||||
return None if payload is not None else json.loads(r.read())
|
||||
|
||||
|
||||
def _load_config() -> dict:
|
||||
"""Read-only config for status/garnish paths that must render degraded, never 500."""
|
||||
return _quiet(config_mod.load_config_readonly, {})
|
||||
|
||||
|
||||
def _runtime_section() -> dict:
|
||||
return (config_mod.load_config_readonly() or {}).get("local_runtime") or {}
|
||||
return (_load_config() or {}).get("local_runtime") or {}
|
||||
|
||||
|
||||
def _set_runtime_enabled(enabled: bool) -> dict:
|
||||
@@ -441,7 +446,7 @@ def _active_llamacpp_model_id() -> str | None:
|
||||
"""The active main model when it is one of ours (config authority: the model.provider + model.default
|
||||
that /api/model/set writes)."""
|
||||
def read() -> str | None:
|
||||
model_section = (config_mod.load_config_readonly() or {}).get("model") or {}
|
||||
model_section = (_load_config() or {}).get("model") or {}
|
||||
if str(model_section.get("provider", "")).strip().lower() in _LLAMACPP_PROVIDERS:
|
||||
return str(model_section.get("default") or model_section.get("name") or "").strip() or None
|
||||
return None
|
||||
@@ -631,7 +636,7 @@ def _restart_on_new_tag(job: Dict[str, Any], tag: str, previous: list) -> bool:
|
||||
return False
|
||||
_step(job, "restarting", "Switching the running server to the new build")
|
||||
bootstrap.shutdown_local_runtime()
|
||||
bootstrap.ensure_local_runtime(config_mod.load_config_readonly(), force=True)
|
||||
bootstrap.ensure_local_runtime(_load_config(), force=True)
|
||||
return True
|
||||
|
||||
|
||||
|
||||
@@ -146,6 +146,19 @@ def test_decompose_fanout_false_invalid_llm_assignee_uses_default(kanban_home):
|
||||
assert task.assignee == "fallback"
|
||||
|
||||
|
||||
def test_load_routing_falls_back_to_defaults_when_config_unreadable(kanban_home, monkeypatch):
|
||||
"""decompose_task promises ok=False on expected failures; a config read that raises (missing
|
||||
profile home, HomeInitializationError) must not escape _load_routing as an exception."""
|
||||
from hermes_cli import config as config_mod
|
||||
|
||||
def _boom():
|
||||
raise FileNotFoundError("profile home is gone")
|
||||
|
||||
monkeypatch.setattr(config_mod, "load_config_readonly", _boom)
|
||||
routing = decomp._load_routing()
|
||||
assert routing.default_assignee == "default" and routing.auto_promote is True
|
||||
|
||||
|
||||
def test_decompose_returns_false_when_task_not_triage(kanban_home):
|
||||
with kbc.connect() as conn:
|
||||
tid = kb.create_task(conn, title="x") # ready, not triage
|
||||
|
||||
@@ -54,6 +54,18 @@ def test_status_shape_and_defaults(client):
|
||||
assert isinstance(data["models"], list)
|
||||
|
||||
|
||||
def test_status_renders_degraded_when_config_cannot_be_read(client, monkeypatch):
|
||||
"""The status pane is garnish: an unreadable/uninitialized config renders defaults, never a 500."""
|
||||
from hermes_cli import config as config_mod
|
||||
|
||||
def _boom():
|
||||
raise FileNotFoundError("profile home is gone")
|
||||
|
||||
monkeypatch.setattr(config_mod, "load_config_readonly", _boom)
|
||||
r = client.get("/api/local-models/status")
|
||||
assert r.status_code == 200 and r.json()["enabled"] is False
|
||||
|
||||
|
||||
def test_status_lists_staged_models_with_labels(client, tmp_path):
|
||||
from hermes_cli.local_runtime.bootstrap import models_dir
|
||||
|
||||
|
||||
Reference in New Issue
Block a user