perf(config): route the config and manifest loaders through fast_safe_load

utils.fast_safe_load already exists, is pinned by tests/test_fast_safe_load.py, and
its comment names exactly these payers: 'startup parses config.yaml and every plugin
manifest, so the slow path cost ~0.9 s of cold start'. The migration was started —
hermes_cli/config.py uses it eight times, hermes_cli/main.py and hermes_cli/plugins.py
too — but the file-level loaders it was written for were never converted.

The cost is config SIZE, and the size is the installer's doing: it seeds config.yaml
by copying cli-config.yaml.example, 120,897 bytes of mostly comments. Nothing caches
load_gateway_config() and it has 238 production call sites.

Profiled before assuming a cause — reader.forward 34 ms, scanner.scan_to_next_token
28 ms, reader.peek 13 ms: the pure-Python PyYAML scanner, nothing else.

Measured A/B on one realistic pass (gateway config + every bundled plugin
description), medians of five runs, __pycache__ cleared between arms:
48.8 ms -> 2.46 ms. Per path: load_gateway_config 48.3 -> 1.84 ms, managed
config.yaml 44.4 -> 1.03 ms, 105 plugin.yaml manifests 59.3 -> 5.9 ms.

Same parse, same restricted tag set, same result — only the loader changes. Drops the
three 'import yaml' statements the swap orphaned.

(cherry picked from commit a4efd6a4506043159310eba02c32b673c455b88b)
This commit is contained in:
John Paul Soliva
2026-09-22 11:31:22 +09:00
committed by kshitij
parent 8646ddc19a
commit 16db511ed5
6 changed files with 17 additions and 12 deletions

View File

@@ -385,13 +385,15 @@ def read_yaml_layers(home: Path) -> dict:
(``gateway.relay.relay_explicitly_disabled``) reads through here so it cannot disagree with
``load_gateway_config()`` on which files count.
"""
import yaml
config_yaml_path = home / "config.yaml"
yaml_cfg: dict = {}
if config_yaml_path.exists():
# The installer seeds config.yaml by copying cli-config.yaml.example (~120 KB, almost all
# comments), and nothing caches this loader — so the pure-Python parser dominates the load.
from utils import fast_safe_load
with open(config_yaml_path, encoding="utf-8") as f:
yaml_cfg = yaml.safe_load(f) or {}
yaml_cfg = fast_safe_load(f) or {}
from hermes_cli.config import _expand_env_vars

View File

@@ -15,7 +15,6 @@ import threading
from pathlib import Path
from typing import Dict, Optional
import yaml
# Stale-module bridge: this module binds ``utils.file_signature`` at import time, so a fresh
# import in a post-pull updater process (pre-handoff purge keeps root modules cached) dies
@@ -107,7 +106,9 @@ def _load_managed_file(name: str, cache: Dict[str, tuple], parse) -> dict:
def load_managed_config() -> dict:
"""Parsed managed config.yaml, or {} when absent/malformed (fail-open)."""
return _load_managed_file("config.yaml", _CONFIG_CACHE, lambda p: yaml.safe_load(p.read_text(encoding="utf-8")) or {})
from utils import fast_safe_load
return _load_managed_file("config.yaml", _CONFIG_CACHE, lambda p: fast_safe_load(p.read_text(encoding="utf-8")) or {})
def load_managed_env() -> Dict[str, str]:

View File

@@ -280,9 +280,10 @@ def _has_portable_manifest(plugin_dir: Path) -> bool:
def _load_yaml_manifest(manifest_file: Path):
"""``yaml.safe_load`` of *manifest_file* (``{}`` when empty); raises on any read/parse error."""
import yaml
from utils import fast_safe_load
with open(manifest_file, encoding="utf-8") as f:
return yaml.safe_load(f) or {}
return fast_safe_load(f) or {}
def _read_manifest(plugin_dir: Path) -> dict:

View File

@@ -56,9 +56,10 @@ def iter_plugin_dirs(root: Path) -> List[Path]:
def read_plugin_description(plugin_dir: Path) -> str:
"""Return ``description`` from ``plugin.yaml`` (empty string if absent/unreadable)."""
try:
import yaml
from utils import fast_safe_load
with open(plugin_dir / "plugin.yaml", encoding="utf-8-sig") as f:
meta = yaml.safe_load(f) or {}
meta = fast_safe_load(f) or {}
return meta.get("description", "")
except Exception:
return ""

View File

@@ -288,9 +288,9 @@ def _declares_model_provider_kind(plugin_dir: Path) -> bool:
except Exception:
return False
try:
import yaml
from utils import fast_safe_load
data = yaml.safe_load(text)
data = fast_safe_load(text)
if isinstance(data, dict):
return str(data.get("kind", "")).strip() == "model-provider"
except Exception:

View File

@@ -18,7 +18,7 @@ def _load_with_yaml_dict(yaml_dict: dict):
patch("builtins.open", create=True) as mock_file:
mock_file.return_value.__enter__ = lambda s: s
mock_file.return_value.__exit__ = MagicMock(return_value=False)
with patch("yaml.safe_load", return_value=yaml_dict):
with patch("utils.fast_safe_load", return_value=yaml_dict):
return load_gateway_config()