Skip to content

Fix/moa per slot max tokens - #60391

Closed
matarbot wants to merge 2 commits into
NousResearch:mainfrom
matarbot:fix/moa-per-slot-max-tokens
Closed

matarbot wants to merge 2 commits into
NousResearch:mainfrom
matarbot:fix/moa-per-slot-max-tokens

Conversation

@matarbot

@matarbot matarbot commented Jul 7, 2026

Copy link
Copy Markdown

What does this PR do?

This branch improves how the harness handles max_token settings in config.yaml, including fixing some outstanding bugs which caused documented max_tokens-like settings to be silently dropped.

Related Issue

Fixes #60388

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)
  • ✨ New feature (non-breaking change that adds functionality)
  • 🔒 Security fix
  • 📝 Documentation update
  • ✅ Tests (adding or improving test coverage)
  • ♻️ Refactor (no behavior change)
  • 🎯 New skill (bundled or hub)

Changes Made

How to Test

Checklist

Code

  • I've read the Contributing Guide
  • My commit messages follow Conventional Commits (fix(scope):, feat(scope):, etc.)
  • I searched for existing PRs to make sure this isn't a duplicate
  • My PR contains only changes related to this fix/feature (no unrelated commits)
  • I've run pytest tests/ -q and all tests pass
  • I've added tests for my changes (required for bug fixes, strongly encouraged for features)
  • I've tested on my platform:

Documentation & Housekeeping

  • I've updated relevant documentation (README, docs/, docstrings) — or N/A
  • I've updated cli-config.yaml.example if I added/changed config keys — or N/A
  • I've updated CONTRIBUTING.md or AGENTS.md if I changed architecture or workflows — or N/A
  • I've considered cross-platform impact (Windows, macOS) per the compatibility guide — or N/A
  • I've updated tool descriptions/schemas if I changed tool behavior — or N/A

For New Skills

  • This skill is broadly useful to most users (if bundled) — see Contributing Guide
  • SKILL.md follows the standard format (frontmatter, trigger conditions, steps, pitfalls)
  • No external dependencies that aren't already available (prefer stdlib, curl, existing Hermes tools)
  • I've tested the skill end-to-end: hermes --toolsets skills -q "Use the X skill to do Y"

Screenshots / Logs

rain-sk added 2 commits July 7, 2026 18:18
…opping it

_build_call_kwargs only added max_tokens to the request kwargs for
Anthropic-compat and NVIDIA NIM endpoints. For every other provider
(OpenRouter, custom/local, OpenAI direct) it computed max_tokens but
never put it in kwargs — so an explicit cap from any auxiliary caller
was silently ignored.

This affected MoA reference_max_tokens (did nothing unless the reference
model was on an Anthropic-compat endpoint) and title_generation
(max_tokens=500 was dropped on the floor).

Now: when max_tokens is explicitly non-None, forward it via
auxiliary_max_tokens_param() which picks the correct wire field
(max_tokens vs max_completion_tokens) per model. Anthropic-compat and
NIM still get max_tokens directly (mandatory field on that wire).
The existing retry logic strips the param on 400 if a provider rejects
it, so forwarding is safe.

Tests updated from asserting "max_tokens is always omitted" to asserting
the correct forwarding behavior per provider/model.
MoA reference_max_tokens is preset-level — one cap for all reference
models. When mixing a verbose model with a terse one, a single cap is
either too tight for the terse model or too loose for the verbose one.

Now each reference slot can optionally carry its own max_tokens:

  reference_models:
    - provider: openrouter
      model: deepseek/deepseek-v4-pro
      max_tokens: ***        # per-slot cap, overrides preset-level
    - provider: openai-codex
      model: gpt-5.5
      # no max_tokens → falls back to preset-level reference_max_tokens

_clean_slot (moa_config.py) preserves an optional max_tokens field on
the slot dict, coerced via _coerce_int_or_none. _run_reference
(moa_loop.py) reads slot-level max_tokens first, falling back to the
preset-level cap passed by the caller. Slots without the field are
unaffected — backward compatible.

Type hints on slot-handling functions updated from dict[str, str] to
dict[str, Any] to reflect the now-heterogeneous slot shape.
@alt-glitch alt-glitch added type/bug Something isn't working comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint comp/cli CLI entry point, hermes_cli/, setup wizard area/config Config system, migrations, profiles P2 Medium — degraded but workaround exists labels Jul 7, 2026

@teknium1 teknium1 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for tracing the dropped preset-level MoA cap. Current main does have a real contract mismatch: reference_max_tokens is documented at website/docs/user-guide/features/mixture-of-agents.md:112, reaches _run_references_parallel() at agent/moa_loop.py:1002, and is then omitted by _build_call_kwargs() for non-Anthropic/NIM routes at agent/auxiliary_client.py:6382.

Problems

  • The blanket forwarding at agent/auxiliary_client.py:6147 reverses the deliberate omission introduced by 2062a840; current regression coverage at tests/agent/test_auxiliary_client.py:278 requires omission for OpenAI-compatible routes, including ZAI vision. Retrying after a provider rejection does not preserve that behavior.
  • hermes_cli/moa_config.py:96 adds a new per-slot user-facing max_tokens configuration surface. Please keep that separate from repairing the existing documented reference_max_tokens behavior.

Suggested changes

  • Scope the salvage to the existing preset-level MoA cap and preserve the current safe omission behavior for unrelated auxiliary tasks.
  • Add a call_llm-level regression test for the actual wire kwargs and provider-rejection path, rather than testing only _build_call_kwargs.

This is an automated hermes-sweeper review.

Comment thread agent/auxiliary_client.py
# Use the model-aware helper to pick max_tokens vs
# max_completion_tokens. The retry logic below handles any
# provider that rejects the param by stripping and retrying.
kwargs.update(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This broadens explicit output-cap forwarding to every non-Anthropic/NIM auxiliary route, but current main intentionally omits those caps to avoid OpenAI-compatible and ZAI failures (2062a840; tests/agent/test_auxiliary_client.py:278). Please scope the repair to the documented MoA reference-cap path, or preserve the existing omission behavior for unrelated auxiliary tasks.

Comment thread hermes_cli/moa_config.py
# default) = no cap, so existing slots are unaffected. Allows tuning
# each advisor's output length independently — useful when one model
# is verbose and another is terse.
slot_mt = _coerce_int_or_none(slot.get("max_tokens"))

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a new user-facing per-slot max_tokens configuration surface. Please split it from the repair for the already documented preset-level reference_max_tokens setting; the two changes need separate maintainer direction.

@teknium1 teknium1 added sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:blast-broad Sweeper blast radius: broad — a core path most sessions hit labels Jul 15, 2026
@teknium1

Copy link
Copy Markdown
Collaborator

The per-slot max_tokens slice of this draft was merged via cluster PR #70279 (commit bc7212c) with your authorship preserved on that commit — _clean_slot whitelist, slot-cap-overrides-preset precedence, and your tests. The blanket _build_call_kwargs forwarding was NOT taken: it reverses the deliberate #34845 omission-by-default design, which is a maintainer decision that stays parked on your issue #60388 (thanks for filing that too). Closing the draft with the shipped half credited.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/config Config system, migrations, profiles comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint comp/cli CLI entry point, hermes_cli/, setup wizard P2 Medium — degraded but workaround exists sweeper:blast-broad Sweeper blast radius: broad — a core path most sessions hit sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: max_tokens settings across config.yaml are silently dropped

4 participants