mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-31 19:16:29 +00:00
Third profiling round (after #66033 / #66347), targeting the composer model picker and dialog opens (worktree dialog etc.), measured over CDP on real 1000+-message sessions. Backend — model.options took 4.8s cold / 1.8s warm per call, and the desktop model pill/picker blocks on it every open: - agent/credential_pool: _load_config_safe uses load_config_readonly(). Every consumer only reads, and the per-call deepcopy was the dominant cost — list_authenticated_providers calls load_pool() per provider row, and each load_pool loaded (and deep-copied) the full config again via get_pool_strategy. - hermes_cli/config: memoize ensure_hermes_home() per home path. It runs inside the config lock on EVERY load_config(), paying ~14 mkdir/chmod syscalls per call. The fast path still re-checks that the home dir exists, so a deleted home is recreated as before; profile switches hit the new path and re-run. Tests cover both. - tui_gateway/server: add model.options to _LONG_HANDLERS. It measured seconds inline on the WS reader thread — while it ran, prompt.submit and session.interrupt sat unread (same class as #21123). Together: model.options RPC 4825/1842ms → 426/230ms (measured on the live desktop backend); build_models_payload in isolation 6.2s → 0.97s cold, 0.27s warm. Desktop — every Radix dialog/popover open forced a whole-document style recalc (Presence reads getComputedStyle on mount), which on a 1300-message transcript cost ~650-730ms per open (CPU profile: getAnimationName 483ms self). The worktree dialog (⌘⇧B) paid it on every single open: - thread/list: content-visibility:auto + contain-intrinsic-size on the per-turn group wrappers. Off-screen turns now skip style recalc, layout, and paint entirely; never-rendered turns hold a placeholder height (auto: remembered real size once rendered) so scrollbar and anchoring stay stable. Verified over CDP: worktree dialog open 656- 730ms → ~200ms on the same session; stick-to-bottom pin, scroll-to- top rendering, and sticky human bubbles all intact. Also: profile-session-switch harness accepts CDP_HTTP (Chrome tends to squat on 9222). Verification: - scripts/run_tests.sh: config, credential-pool, inventory, model-switch routing, tui_gateway protocol, profiles suites green (test_profiles has one pre-existing failure on main, unrelated); new tests for the ensure_hermes_home memo. - apps/desktop: tsc clean, eslint/prettier clean, thread + session suites green (326 tests). - E2E over CDP on the live app: numbers above, plus scroll/pin sanity.
40 lines
1.3 KiB
Python
40 lines
1.3 KiB
Python
"""ensure_hermes_home is memoized per home path (perf: it runs on every
|
|
load_config), but a deleted home must still be recreated on the next call."""
|
|
|
|
import shutil
|
|
|
|
from hermes_cli import config as cfg
|
|
|
|
|
|
def test_repeat_calls_are_memoized_but_deleted_home_is_recreated(tmp_path, monkeypatch):
|
|
home = tmp_path / ".hermes"
|
|
monkeypatch.setenv("HERMES_HOME", str(home))
|
|
|
|
cfg.ensure_hermes_home()
|
|
assert (home / "sessions").is_dir()
|
|
|
|
# Memoized: a second call must not recreate a removed SUBDIR (the fast
|
|
# path only re-checks the home root)…
|
|
shutil.rmtree(home / "sessions")
|
|
cfg.ensure_hermes_home()
|
|
assert not (home / "sessions").exists()
|
|
|
|
# …but a vanished HOME re-runs the full walk and restores the skeleton.
|
|
shutil.rmtree(home)
|
|
cfg.ensure_hermes_home()
|
|
assert (home / "sessions").is_dir()
|
|
|
|
|
|
def test_distinct_home_paths_each_get_the_skeleton(tmp_path, monkeypatch):
|
|
first = tmp_path / "a" / ".hermes"
|
|
second = tmp_path / "b" / ".hermes"
|
|
|
|
monkeypatch.setenv("HERMES_HOME", str(first))
|
|
cfg.ensure_hermes_home()
|
|
|
|
# Profile switch: HERMES_HOME moves → the new path is ensured too.
|
|
monkeypatch.setenv("HERMES_HOME", str(second))
|
|
cfg.ensure_hermes_home()
|
|
|
|
assert (first / "logs").is_dir()
|
|
assert (second / "logs").is_dir()
|