refactor(moa): drop auxiliary-task reasoning knob in favor of per-slot preset config

The just-merged auxiliary.<task>.reasoning_effort shorthand applied
ensemble-wide to MoA (one value for every advisor) — wrong granularity.
Per-slot preset config supersedes it:

  moa:
    presets:
      deep_review:
        reference_models:
          - {provider: ..., model: ..., reasoning_effort: low}
          - {provider: ..., model: ..., reasoning_effort: xhigh}
        aggregator:
          {provider: ..., model: ..., reasoning_effort: high}

- Remove reasoning_effort from the moa_reference/moa_aggregator
  DEFAULT_CONFIG blocks; _get_task_extra_body now warns-and-ignores the
  key on MoA tasks, pointing at the preset config
- Guard tests: MoA aux blocks must not regrow the key; task-level value
  is rejected with the pointer warning
- Docs: configuration.md notes the MoA exception and links the MoA page
This commit is contained in:
Teknium 2026-07-14 14:38:41 -07:00
parent 5646dbdd5b
commit 0bb3a82c53
4 changed files with 45 additions and 3 deletions

View file

@ -6175,6 +6175,12 @@ def _get_task_extra_body(task: str) -> Dict[str, Any]:
chat.completions passes it through, the Codex Responses adapter maps it
to top-level ``reasoning``/``include``, and the Anthropic auxiliary
client maps it to ``build_anthropic_kwargs(reasoning_config=...)``.
MoA tasks are excluded by design: reasoning depth for MoA is a per-slot
setting in the MoA preset (``moa.presets.<name>.reference_models[].
reasoning_effort`` / ``aggregator.reasoning_effort``), not an
auxiliary-task knob an ensemble-wide value would override the
per-slot ones.
"""
task_config = _get_auxiliary_task_config(task)
raw = task_config.get("extra_body")
@ -6182,6 +6188,15 @@ def _get_task_extra_body(task: str) -> Dict[str, Any]:
if "reasoning" not in result:
effort = task_config.get("reasoning_effort")
if effort is not None and effort != "":
if task in ("moa_reference", "moa_aggregator"):
logger.warning(
"auxiliary.%s.reasoning_effort is not supported — MoA "
"reasoning depth is per-slot: set reasoning_effort on the "
"preset's reference_models entries / aggregator instead "
"(moa.presets.<name>...). Ignoring.",
task,
)
return result
from hermes_constants import parse_reasoning_effort
parsed = parse_reasoning_effort(effort)
if parsed is not None:

View file

@ -1756,7 +1756,10 @@ DEFAULT_CONFIG = {
"api_key": "",
"timeout": 900,
"extra_body": {},
"reasoning_effort": "", # per-task thinking level: none|minimal|low|medium|high|xhigh|max|ultra (empty = provider default)
# NOTE: no reasoning_effort here by design — MoA reasoning depth is
# configured PER SLOT in the MoA preset (moa.presets.<name>.
# reference_models[].reasoning_effort / aggregator.reasoning_effort),
# not at the auxiliary-task level.
},
"moa_aggregator": {
"provider": "auto",
@ -1765,7 +1768,7 @@ DEFAULT_CONFIG = {
"api_key": "",
"timeout": 900,
"extra_body": {},
"reasoning_effort": "", # per-task thinking level: none|minimal|low|medium|high|xhigh|max|ultra (empty = provider default)
# NOTE: no reasoning_effort here by design — see moa_reference above.
},
},

View file

@ -3331,6 +3331,28 @@ class TestAuxiliaryTaskExtraBody:
with patch("hermes_cli.config.load_config", return_value=config):
assert _get_task_extra_body("session_search") == {}
@pytest.mark.parametrize("moa_task", ["moa_reference", "moa_aggregator"])
def test_moa_tasks_reject_task_level_reasoning_effort(self, moa_task, caplog):
"""MoA reasoning is per-slot in the preset — the auxiliary-task
shorthand is ignored with a warning pointing at the preset config."""
from agent.auxiliary_client import _get_task_extra_body
config = {"auxiliary": {moa_task: {"reasoning_effort": "xhigh"}}}
with patch("hermes_cli.config.load_config", return_value=config), \
caplog.at_level(logging.WARNING, logger="agent.auxiliary_client"):
result = _get_task_extra_body(moa_task)
assert "reasoning" not in result
assert any("per-slot" in rec.message for rec in caplog.records)
@pytest.mark.parametrize("moa_task", ["moa_reference", "moa_aggregator"])
def test_moa_default_config_has_no_reasoning_effort(self, moa_task):
"""Invariant: the shipped MoA auxiliary blocks must not grow a
reasoning_effort key per-slot preset config is the only surface."""
from hermes_cli.config import DEFAULT_CONFIG
assert "reasoning_effort" not in DEFAULT_CONFIG["auxiliary"][moa_task]
def test_anthropic_aux_client_forwards_extra_body_reasoning(self):
"""_AnthropicCompletionsAdapter passes extra_body.reasoning into
build_anthropic_kwargs as reasoning_config."""

View file

@ -980,7 +980,9 @@ Auxiliary task blocks additionally accept a `reasoning_effort` knob:
|-----|-------------|---------|
| `reasoning_effort` | Thinking level for that task's LLM calls: `none`, `minimal`, `low`, `medium`, `high`, `xhigh`, `max`, `ultra` | not set (provider default) |
This is the per-task counterpart of the global `agent.reasoning_effort`: run compression at `low` or vision at `none` to cut side-task latency and cost when your main model is an expensive reasoning model, without touching your main chat behavior. It works on every auxiliary task block (`vision`, `web_extract`, `compression`, `title_generation`, `curator`, `background_review`, `moa_reference`, `moa_aggregator`, ...), across all three auxiliary wire formats (chat completions, Codex Responses, Anthropic Messages). An explicit `extra_body.reasoning` on the same task wins over the shorthand.
This is the per-task counterpart of the global `agent.reasoning_effort`: run compression at `low` or vision at `none` to cut side-task latency and cost when your main model is an expensive reasoning model, without touching your main chat behavior. It works on every auxiliary task block (`vision`, `web_extract`, `compression`, `title_generation`, `curator`, `background_review`, ...), across all three auxiliary wire formats (chat completions, Codex Responses, Anthropic Messages). An explicit `extra_body.reasoning` on the same task wins over the shorthand.
MoA is the one exception: reasoning depth for Mixture-of-Agents is configured **per slot** in the MoA preset (`moa.presets.<name>.reference_models[].reasoning_effort` / `aggregator.reasoning_effort`), not on the `moa_reference`/`moa_aggregator` auxiliary blocks — see [Mixture of Agents](/user-guide/features/mixture-of-agents).
```yaml
auxiliary: