fix(gateway): preserve max turns after env reload

This commit is contained in:
altmazza0-star 2026-05-03 18:53:57 +08:00 committed by Teknium
parent 2c14d3b9b0
commit 8308d18339
2 changed files with 86 additions and 7 deletions

View file

@ -299,6 +299,36 @@ _env_path = _hermes_home / '.env'
load_hermes_dotenv(hermes_home=_hermes_home, project_env=Path(__file__).resolve().parents[1] / '.env')
def _reload_runtime_env_preserving_config_authority() -> None:
"""Reload .env for fresh credentials without letting stale .env override config.
Gateway processes are long-lived, so per-turn code reloads ~/.hermes/.env to
pick up rotated API keys. config.yaml remains authoritative for agent budget
settings such as agent.max_turns; otherwise a stale HERMES_MAX_ITERATIONS in
.env can replace the startup bridge on later turns.
"""
load_hermes_dotenv(
hermes_home=_hermes_home,
project_env=Path(__file__).resolve().parents[1] / '.env',
)
config_path = _hermes_home / 'config.yaml'
if not config_path.exists():
return
try:
import yaml as _yaml
with open(config_path, encoding="utf-8") as f:
cfg = _yaml.safe_load(f) or {}
from hermes_cli.config import _expand_env_vars
cfg = _expand_env_vars(cfg)
except Exception:
return
agent_cfg = cfg.get("agent", {})
if isinstance(agent_cfg, dict) and "max_turns" in agent_cfg:
os.environ["HERMES_MAX_ITERATIONS"] = str(agent_cfg["max_turns"])
_DOCKER_VOLUME_SPEC_RE = re.compile(r"^(?P<host>.+):(?P<container>/[^:]+?)(?::(?P<options>[^:]+))?$")
_DOCKER_MEDIA_OUTPUT_CONTAINER_PATHS = {"/output", "/outputs"}
@ -13524,13 +13554,9 @@ class GatewayRunner:
combined_ephemeral = (combined_ephemeral + "\n\n" + self._ephemeral_system_prompt).strip()
# Re-read .env and config for fresh credentials (gateway is long-lived,
# keys may change without restart).
try:
load_dotenv(_env_path, override=True, encoding="utf-8")
except UnicodeDecodeError:
load_dotenv(_env_path, override=True, encoding="latin-1")
except Exception:
pass
# keys may change without restart). Keep config.yaml authoritative for
# runtime budget settings bridged into env vars.
_reload_runtime_env_preserving_config_authority()
try:
model, runtime_kwargs = self._resolve_session_agent_runtime(

View file

@ -0,0 +1,53 @@
"""Regression tests for gateway per-turn env reload preserving config authority.
Issue #19158: startup bridges config.yaml agent.max_turns into
HERMES_MAX_ITERATIONS, but a later per-turn load_dotenv(..., override=True)
can restore a stale .env HERMES_MAX_ITERATIONS value before the next turn.
"""
from __future__ import annotations
import os
from pathlib import Path
import yaml
from gateway import run as gateway_run
def test_reload_runtime_env_preserves_config_max_turns(tmp_path: Path, monkeypatch) -> None:
hermes_home = tmp_path / ".hermes"
hermes_home.mkdir()
(hermes_home / "config.yaml").write_text(
yaml.safe_dump({"agent": {"max_turns": 9000}}),
encoding="utf-8",
)
(hermes_home / ".env").write_text(
"HERMES_MAX_ITERATIONS=90\nOPENROUTER_API_KEY=fresh-key\n",
encoding="utf-8",
)
monkeypatch.setattr(gateway_run, "_hermes_home", hermes_home)
monkeypatch.setenv("HERMES_MAX_ITERATIONS", "9000")
monkeypatch.delenv("OPENROUTER_API_KEY", raising=False)
gateway_run._reload_runtime_env_preserving_config_authority()
assert os.environ["OPENROUTER_API_KEY"] == "fresh-key"
assert os.environ["HERMES_MAX_ITERATIONS"] == "9000"
def test_reload_runtime_env_keeps_env_max_iterations_when_config_omits_key(
tmp_path: Path, monkeypatch
) -> None:
hermes_home = tmp_path / ".hermes"
hermes_home.mkdir()
(hermes_home / "config.yaml").write_text(yaml.safe_dump({"agent": {}}), encoding="utf-8")
(hermes_home / ".env").write_text("HERMES_MAX_ITERATIONS=123\n", encoding="utf-8")
monkeypatch.setattr(gateway_run, "_hermes_home", hermes_home)
monkeypatch.delenv("HERMES_MAX_ITERATIONS", raising=False)
gateway_run._reload_runtime_env_preserving_config_authority()
assert os.environ["HERMES_MAX_ITERATIONS"] == "123"