From a29a9700540d80c7e2de1fabc5583f0bf3f545b9 Mon Sep 17 00:00:00 2001 From: Teknium <127238744+teknium1@users.noreply.github.com> Date: Wed, 2 Sep 2026 22:28:11 -0700 Subject: [PATCH] refactor(web-profiles/status): drop dead sidebar cache_clear hook, compact literals and ladders --- hermes_cli/web_routers/profiles.py | 65 +++++++------------- hermes_cli/web_routers/status.py | 99 ++++++++++-------------------- hermes_cli/web_server_profiles.py | 18 +++--- 3 files changed, 64 insertions(+), 118 deletions(-) diff --git a/hermes_cli/web_routers/profiles.py b/hermes_cli/web_routers/profiles.py index df5ccc48bd..960b0bdb8a 100644 --- a/hermes_cli/web_routers/profiles.py +++ b/hermes_cli/web_routers/profiles.py @@ -177,10 +177,8 @@ def _profile_targets(log_label: str, *, lightweight: bool) -> List[Tuple[str, Pa config/meta and probes gateways/skills per profile — too heavy per sidebar refresh.""" from hermes_cli import profiles as profiles_mod try: - if lightweight: - targets = list(profiles_mod.profiles_to_serve(multiplex=True)) - else: - targets = [(info.name, info.path) for info in profiles_mod.list_profiles()] + targets = (list(profiles_mod.profiles_to_serve(multiplex=True)) if lightweight + else [(info.name, info.path) for info in profiles_mod.list_profiles()]) except Exception: _log.exception("%s: list_profiles failed", log_label) targets = [] @@ -309,11 +307,6 @@ def _sidebar_singleflight_cache(func): refresh_lock = threading.Lock() miss = object() - def _key(args, kwargs): - bound = signature.bind(*args, **kwargs) - bound.apply_defaults() - return tuple(bound.arguments.items()) - def _lookup(key): now = time.monotonic() with cache_lock: @@ -330,7 +323,9 @@ def _sidebar_singleflight_cache(func): if ttl <= 0: return func(*args, **kwargs) - key = _key(args, kwargs) + bound = signature.bind(*args, **kwargs) + bound.apply_defaults() + key = tuple(bound.arguments.items()) cached = _lookup(key) if cached is not miss: return cached @@ -359,11 +354,6 @@ def _sidebar_singleflight_cache(func): cache.popitem(last=False) return result - def cache_clear(): - with cache_lock: - cache.clear() - - wrapped.cache_clear = cache_clear return wrapped @@ -388,10 +378,8 @@ def get_profiles_sessions( if order not in ("created", "recent"): raise HTTPException(status_code=400, detail="order must be one of: created, recent") - if profile and profile != "all": - targets = [_cron_profile_home(profile)] - else: - targets = _profile_targets("GET /api/profiles/sessions", lightweight=True) + targets = ([_cron_profile_home(profile)] if profile and profile != "all" + else _profile_targets("GET /api/profiles/sessions", lightweight=True)) # Source scoping (see /api/sessions): recents pass exclude_sources=cron, the cron-jobs # section source=cron — two independent lists so cron sessions can't starve recents. @@ -594,11 +582,10 @@ def get_profiles_projects_tree(preview_limit: int = 3, session_limit: int = 2000 scoped_session_ids.extend(tree["scoped_session_ids"]) _read_profile_db(name, home, errors, _read) - return { - "projects": sorted(merged.values(), key=lambda p: p.get("lastActive") or 0, reverse=True), - # Ownership is per profile, so no project is "the active one" here; the desktop only - # reads active_id to bias its overview sort. - "active_id": None, "scoped_session_ids": scoped_session_ids, "errors": errors} + # active_id is None: ownership is per profile, so no project is "the active one" here. + projects = sorted(merged.values(), key=lambda p: p.get("lastActive") or 0, reverse=True) + return {"projects": projects, "active_id": None, "scoped_session_ids": scoped_session_ids, + "errors": errors} # `gh pr create` prints the PR url and nothing else, so a tool result whose whole output IS a @@ -690,14 +677,12 @@ async def create_profile_endpoint(body: ProfileCreate): "Setting model for new profile %s failed", body.name, fn=lambda: (_write_profile_model(path, provider, model), True)[1], default=False) mcp_written = _best_effort( - "Writing MCP servers for new profile %s failed", body.name, - fn=lambda: _write_profile_mcp_servers(path, body.mcp_servers), default=0 - ) if body.mcp_servers else 0 + "Writing MCP servers for new profile %s failed", body.name, default=0, + fn=lambda: _write_profile_mcp_servers(path, body.mcp_servers)) if body.mcp_servers else 0 # "keep" has replace semantics; skipped when empty (legacy: keep the bundle). skills_disabled = _best_effort( - "Applying skill selection for new profile %s failed", body.name, - fn=lambda: _disable_unselected_skills(path, body.keep_skills), default=0 - ) if body.keep_skills else 0 + "Applying skill selection for new profile %s failed", body.name, default=0, + fn=lambda: _disable_unselected_skills(path, body.keep_skills)) if body.keep_skills else 0 # Hub installs spawn async, scoped via `-p ` (a fresh subprocess re-binds # skills_hub.SKILLS_DIR at import). PIDs go back for the UI to poll. @@ -874,8 +859,8 @@ async def update_profile_description_endpoint(name: str, body: ProfileDescriptio @router.put("/api/profiles/{name}/model") async def update_profile_model_endpoint(name: str, body: ProfileModelUpdate): - """Set the main model for a specific profile's config.yaml without touching the - dashboard's own profile — ``POST /api/model/set`` (main scope) via the HERMES_HOME override.""" + """Set the main model for a specific profile's config.yaml without touching the dashboard's + own profile — ``POST /api/model/set`` (main scope) via the HERMES_HOME override.""" profile_dir = _resolve_profile_dir(name) provider = (body.provider or "").strip() model = (body.model or "").strip() @@ -903,12 +888,9 @@ async def describe_profile_auto_endpoint(name: str, body: ProfileDescribeAuto): not_found=(), bad_request=()): # A synchronous LLM round-trip with a 60 s ceiling; on the loop it stalls everything. outcome = await run_in_threadpool(_run) - return { - "ok": bool(outcome.ok), - "reason": outcome.reason, - "description": outcome.description, - # A failed sweep leaves any existing description untouched: not auto-authored. - "description_auto": bool(outcome.ok)} + # description_auto mirrors ok: a failed sweep leaves any existing description untouched. + return {"ok": bool(outcome.ok), "reason": outcome.reason, "description": outcome.description, + "description_auto": bool(outcome.ok)} # ── Export / Import ── wraps hermes_cli.profiles.export_profile / import_profile. Paths are @@ -954,10 +936,9 @@ async def import_profile_endpoint(body: ProfileImport): imported = profile_dir.name # Match the CLI import flow: create the wrapper alias when it's safe. - def _wrapper(): - if not profiles_mod.check_alias_collision(imported): - profiles_mod.create_wrapper_script(imported) - _best_effort("Creating wrapper for imported profile %s failed", imported, fn=_wrapper) + _best_effort("Creating wrapper for imported profile %s failed", imported, + fn=lambda: (profiles_mod.check_alias_collision(imported) + or profiles_mod.create_wrapper_script(imported))) # Bundled desktop appearance overlay, so the desktop needn't make another round-trip. desktop_overlay = None diff --git a/hermes_cli/web_routers/status.py b/hermes_cli/web_routers/status.py index 8703477072..7656cd84a6 100644 --- a/hermes_cli/web_routers/status.py +++ b/hermes_cli/web_routers/status.py @@ -58,6 +58,14 @@ _GATEWAY_HEALTH_ROUTE_TIMEOUT = 1.0 _HEALTHY_PLATFORM_STATES = {"connected", "running", "ok"} +def _safe_call(mod, fn_name: str, default): + try: + fn = getattr(mod, fn_name, None) + return fn() if callable(fn) else default + except Exception: + return default + + def _count_status_active_sessions() -> int: """Best-effort status garnish. Opens read-only (via the shared stale-schema heal) so /api/status never routinely writes to state.db while another Hermes process uses it.""" @@ -95,20 +103,15 @@ async def get_ssh_ownership(request: Request): _require_token(request) if not _SSH_OWNER_NONCE: raise HTTPException(status_code=404, detail="SSH ownership is not active") - return { - "ok": True, - "sshOwnerNonce": _SSH_OWNER_NONCE, - "protocolVersion": 1, - "runtimeIntact": _ssh_runtime_intact()} + return {"ok": True, "sshOwnerNonce": _SSH_OWNER_NONCE, "protocolVersion": 1, + "runtimeIntact": _ssh_runtime_intact()} @router.get("/api/health") async def get_health(): """Lightweight process liveness for desktop/backend readiness probes.""" - return { - "ok": True, - "version": __version__, - "auth_required": bool(getattr(app.state, "auth_required", False))} + return {"ok": True, "version": __version__, + "auth_required": bool(getattr(app.state, "auth_required", False))} # Profile segment mirrors hermes_cli.profiles._PROFILE_ID_RE. Platform segment mirrors the @@ -197,10 +200,8 @@ def _project_gateway_platforms(gateway_platforms: dict, configured: "set[str] | entries are the diagnosis (credential collisions, auth failures) that the single exit_reason string can't express, so they are kept — upstream writer-identity/freshness filtering already dropped other processes' entries.""" - platforms = { - key: _public_platform_entry(value) - for key, value in gateway_platforms.items() - if _status_platform_key_allowed(key, configured)} + platforms = {key: _public_platform_entry(value) for key, value in gateway_platforms.items() + if _status_platform_key_allowed(key, configured)} if gateway_running: return platforms if gateway_state == "startup_failed": @@ -326,15 +327,11 @@ async def _component_health(gateway: Dict[str, Any]) -> Dict[str, Any]: components["storage"] = {"status": storage_check.get("status", "degraded")} except Exception: components["storage"] = {"status": "degraded"} - platform_states = [ - str(value.get("state") or value.get("status") or "").lower() - for value in gateway_platforms.values() - if isinstance(value, dict)] + platform_states = [str(value.get("state") or value.get("status") or "").lower() + for value in gateway_platforms.values() if isinstance(value, dict)] connected = sum(1 for state in platform_states if state in _HEALTHY_PLATFORM_STATES) - components["platforms"] = { - "status": "ok" if connected == len(platform_states) else "degraded", - "configured": len(gateway_platforms), - "connected": connected} + components["platforms"] = {"status": "ok" if connected == len(platform_states) else "degraded", + "configured": len(gateway_platforms), "connected": connected} return components @@ -411,13 +408,10 @@ async def get_status(profile: Optional[str] = None): auth = _auth_gate_status() status = { - "version": __version__, - "release_date": __release_date__, - "config_version": current_ver, - "latest_config_version": latest_ver, + "version": __version__, "release_date": __release_date__, + "config_version": current_ver, "latest_config_version": latest_ver, "can_update_hermes": not _dashboard_local_update_managed_externally(), - "gateway_running": gateway_running, - "gateway_state": gateway_state, + "gateway_running": gateway_running, "gateway_state": gateway_state, "gateway_platforms": gateway["gateway_platforms"], "gateway_exit_reason": gateway["gateway_exit_reason"], "gateway_updated_at": gateway["gateway_updated_at"], @@ -427,10 +421,8 @@ async def get_status(profile: Optional[str] = None): active_agents=active_agents), "gateway_drainable": derive_gateway_drainable( gateway_running=gateway_running, gateway_state=gateway_state), - "restart_drain_timeout": restart_drain_timeout, - "active_sessions": active_sessions, - **auth, - "nous_session_valid": _nous_session_validity()} + "restart_drain_timeout": restart_drain_timeout, "active_sessions": active_sessions, + **auth, "nous_session_valid": _nous_session_validity()} # Stable per-install identity (first call may touch disk). Omitted (not null) when # unpersistable so older-client behavior and the no-identity fallback stay identical. @@ -455,12 +447,9 @@ async def get_status(profile: Optional[str] = None): # reaches this endpoint — surface them only on a loopback / ``--insecure`` bind. if not auth["auth_required"]: status.update({ - "hermes_home": str(get_hermes_home()), - "config_path": str(get_config_path()), - "env_path": str(get_env_path()), - "gateway_pid": gateway["gateway_pid"], - "gateway_health_url": _GATEWAY_HEALTH_URL, - "gateways": topology["gateways"]}) + "hermes_home": str(get_hermes_home()), "config_path": str(get_config_path()), + "env_path": str(get_env_path()), "gateway_pid": gateway["gateway_pid"], + "gateway_health_url": _GATEWAY_HEALTH_URL, "gateways": topology["gateways"]}) return status finally: @@ -478,12 +467,10 @@ async def get_system_stats(): **_display_system_platform( system=_platform.system(), release=_platform.release(), version=_platform.version(), platform_label=_platform.platform()), - "arch": _platform.machine(), - "hostname": _platform.node(), + "arch": _platform.machine(), "hostname": _platform.node(), "python_version": _platform.python_version(), "python_impl": _platform.python_implementation(), - "hermes_version": __version__, - "cpu_count": os.cpu_count()} + "hermes_version": __version__, "cpu_count": os.cpu_count()} def _disk(): du = psutil.disk_usage(str(get_hermes_home())) @@ -536,10 +523,7 @@ async def get_curator_status(): from agent import curator except Exception as exc: raise HTTPException(status_code=500, detail=f"Curator unavailable: {exc}") - try: - state = curator.load_state() - except Exception: - state = {} + state = _safe_call(curator, "load_state", {}) return { "enabled": _safe_call(curator, "is_enabled", True), "paused": _safe_call(curator, "is_paused", False), @@ -619,14 +603,6 @@ async def update_learning_node(body: LearningNodeEdit): body.profile, lambda: edit_node(body.id, body.content), 400, "edit failed") -def _safe_call(mod, fn_name: str, default): - try: - fn = getattr(mod, fn_name, None) - return fn() if callable(fn) else default - except Exception: - return default - - # Portal — Nous Portal auth + Tool Gateway routing status (read-only). @@ -667,8 +643,7 @@ def _get_portal_status_sync(): model_cfg = cfg.get("model") if isinstance(cfg.get("model"), dict) else {} return { - "logged_in": bool(auth.get("logged_in")), - "portal_url": auth.get("portal_base_url"), + "logged_in": bool(auth.get("logged_in")), "portal_url": auth.get("portal_base_url"), "inference_url": auth.get("inference_base_url"), "provider": str((model_cfg or {}).get("provider") or ""), "subscription_url": "https://portal.nousresearch.com/manage-subscription", @@ -710,12 +685,8 @@ async def run_debug_share_endpoint(body: DebugShareRequest | None = None): _log.exception("debug share failed") raise HTTPException(status_code=500, detail=f"Failed: {exc}") - return { - "ok": True, - "urls": result.urls, - "failures": result.failures, - "redacted": result.redacted, - "auto_delete_seconds": result.auto_delete_seconds} + return {"ok": True, "urls": result.urls, "failures": result.failures, + "redacted": result.redacted, "auto_delete_seconds": result.auto_delete_seconds} @logs_router.get("/api/logs") @@ -741,10 +712,8 @@ async def get_logs( if component and component.lower() != "all": comp_prefixes = COMPONENT_PREFIXES.get(component) if comp_prefixes is None: - raise HTTPException( - status_code=400, - detail=f"Unknown component: {component}. " - f"Available: {', '.join(sorted(COMPONENT_PREFIXES))}") + raise HTTPException(status_code=400, detail=f"Unknown component: {component}. " + f"Available: {', '.join(sorted(COMPONENT_PREFIXES))}") result = _read_tail( log_path, min(lines, 500) if not search else 2000, has_filters=bool(min_level or comp_prefixes or search), diff --git a/hermes_cli/web_server_profiles.py b/hermes_cli/web_server_profiles.py index b80cb4dcb7..7d3989edab 100644 --- a/hermes_cli/web_server_profiles.py +++ b/hermes_cli/web_server_profiles.py @@ -106,13 +106,11 @@ def _parse_model_ids(resp: "Any") -> List[str]: def _fallback_profile_entry(profiles_mod, name: str, home: Path, *, is_default: bool, has_env: bool, gateway_running: Callable[[], bool]) -> Dict[str, Any]: model, provider = _safe(lambda: profiles_mod._read_config_model(home), (None, None)) - - def meta(key, default): - return _safe(lambda: profiles_mod.read_profile_meta(home).get(key, default), default) - + meta = lambda key, default: _safe( # noqa: E731 + lambda: profiles_mod.read_profile_meta(home).get(key, default), default) return { - "name": name, "path": str(home), "is_default": is_default, - "model": model, "provider": provider, "has_env": has_env, + "name": name, "path": str(home), "is_default": is_default, "model": model, + "provider": provider, "has_env": has_env, "skill_count": _safe(lambda: profiles_mod._count_skills(home), 0), "gateway_running": _safe(gateway_running, False), "description": meta("description", ""), "description_auto": meta("description_auto", False), @@ -386,10 +384,8 @@ def _installed_hub_identifiers(profile: Optional[str] = None) -> dict: else: profile_dir = _resolve_profile_dir(profile.strip()) lock = HubLockFile(profile_dir / "skills" / ".hub" / "lock.json") - return { - entry["identifier"]: {"name": entry.get("name"), - "trust_level": entry.get("trust_level"), - "scan_verdict": entry.get("scan_verdict")} - for entry in lock.list_installed() if entry.get("identifier")} + keys = ("name", "trust_level", "scan_verdict") + return {entry["identifier"]: {k: entry.get(k) for k in keys} + for entry in lock.list_installed() if entry.get("identifier")} except Exception: return {}