mirror of
https://github.com/nesquena/hermes-webui.git
synced 2026-05-25 11:10:18 +00:00
fix: honor configured max_turns in WebUI agents
Read agent.max_turns when constructing streaming WebUI AIAgent instances, pass it as max_iterations when supported, and include it in the per-session agent cache signature so budget changes take effect. Add regression coverage for the config read, constructor kwarg, and cache key.
This commit is contained in:
committed by
nesquena-hermes
parent
773857d159
commit
01b9c82dc9
@@ -2296,6 +2296,29 @@ def _run_agent_streaming(
|
||||
import inspect as _inspect
|
||||
_agent_params = set(_inspect.signature(_AIAgent.__init__).parameters)
|
||||
|
||||
# CLI-parity max-iteration budget: read config.yaml's
|
||||
# agent.max_turns and pass it to AIAgent when supported. Without
|
||||
# this WebUI-created agents silently use AIAgent's constructor
|
||||
# default (90), so long browser-originated tasks hit the
|
||||
# "maximum number of tool-calling iterations" summary path even
|
||||
# after the operator raises Hermes' global turn budget.
|
||||
_max_iterations_cfg = None
|
||||
try:
|
||||
_raw_max_iterations = None
|
||||
_agent_cfg_for_iterations = _cfg.get('agent', {}) if isinstance(_cfg, dict) else {}
|
||||
if isinstance(_agent_cfg_for_iterations, dict):
|
||||
_raw_max_iterations = _agent_cfg_for_iterations.get('max_turns')
|
||||
if _raw_max_iterations is None and isinstance(_cfg, dict):
|
||||
# Back-compat for older Hermes config files that used a
|
||||
# root-level max_turns key.
|
||||
_raw_max_iterations = _cfg.get('max_turns')
|
||||
if _raw_max_iterations is not None:
|
||||
_parsed_max_iterations = int(_raw_max_iterations)
|
||||
if _parsed_max_iterations > 0:
|
||||
_max_iterations_cfg = _parsed_max_iterations
|
||||
except Exception:
|
||||
_max_iterations_cfg = None
|
||||
|
||||
# CLI-parity max output cap: read config.yaml's max_tokens and pass
|
||||
# it to AIAgent when supported. Without this WebUI-created agents use
|
||||
# provider-native output ceilings (e.g. Claude via OpenRouter can
|
||||
@@ -2355,6 +2378,8 @@ def _run_agent_streaming(
|
||||
_agent_kwargs['reasoning_config'] = _reasoning_config
|
||||
if 'status_callback' in _agent_params:
|
||||
_agent_kwargs['status_callback'] = _agent_status_callback
|
||||
if 'max_iterations' in _agent_params and _max_iterations_cfg is not None:
|
||||
_agent_kwargs['max_iterations'] = _max_iterations_cfg
|
||||
if 'max_tokens' in _agent_params and _max_tokens_cfg is not None:
|
||||
_agent_kwargs['max_tokens'] = _max_tokens_cfg
|
||||
# Params added in newer hermes-agent — skip if not supported
|
||||
@@ -2388,6 +2413,7 @@ def _run_agent_streaming(
|
||||
_hashlib.sha256((resolved_api_key or '').encode()).hexdigest()[:16],
|
||||
resolved_base_url or '',
|
||||
resolved_provider or '',
|
||||
_max_iterations_cfg or '',
|
||||
_max_tokens_cfg or '',
|
||||
_fallback_resolved or {},
|
||||
sorted(_toolsets) if _toolsets else [],
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
"""Regression checks for WebUI AIAgent iteration-budget parity.
|
||||
|
||||
WebUI streaming agents must honor Hermes' configured agent.max_turns. Otherwise
|
||||
browser-originated long-running tasks silently fall back to AIAgent's constructor
|
||||
default and hit the "maximum number of tool-calling iterations" summary path even
|
||||
when the operator raised the global Hermes budget.
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
REPO = Path(__file__).resolve().parent.parent
|
||||
STREAMING_PY = (REPO / "api" / "streaming.py").read_text(encoding="utf-8")
|
||||
|
||||
|
||||
def test_streaming_agent_reads_agent_max_turns_from_config():
|
||||
assert "_agent_cfg_for_iterations" in STREAMING_PY
|
||||
assert "_agent_cfg_for_iterations.get('max_turns')" in STREAMING_PY
|
||||
assert "_cfg.get('max_turns')" in STREAMING_PY
|
||||
|
||||
|
||||
def test_streaming_agent_passes_max_iterations_to_aiagent():
|
||||
assert "if 'max_iterations' in _agent_params and _max_iterations_cfg is not None:" in STREAMING_PY
|
||||
assert "_agent_kwargs['max_iterations'] = _max_iterations_cfg" in STREAMING_PY
|
||||
|
||||
|
||||
def test_streaming_agent_cache_signature_includes_max_iterations():
|
||||
sig_start = STREAMING_PY.index("_sig_blob = _json.dumps")
|
||||
sig_block = STREAMING_PY[sig_start:STREAMING_PY.index("], sort_keys=True)", sig_start)]
|
||||
assert "_max_iterations_cfg or ''" in sig_block
|
||||
Reference in New Issue
Block a user