Skip to content

feat: per-provider max_tokens via custom_providers models.<model>.max_tokens - #28786

Closed
pty819 wants to merge 1 commit into
NousResearch:mainfrom
pty819:feat/per-provider-max-tokens
Closed

feat: per-provider max_tokens via custom_providers models.<model>.max_tokens#28786
pty819 wants to merge 1 commit into
NousResearch:mainfrom
pty819:feat/per-provider-max-tokens

Conversation

@pty819

@pty819 pty819 commented May 19, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds per-provider max_tokens support to custom_providers, mirroring the existing context_length pattern. This allows users to set a provider-scoped output-token cap without affecting fallback providers.

Problem

model.max_tokens in config.yaml is global — it applies to all providers including fallbacks. There is no way to scope max_tokens to a specific provider, unlike context_length which already supports per-provider overrides.

Changes

hermes_cli/config.py

  • Adds get_custom_provider_max_tokens() — mirrors get_custom_provider_context_length() exactly. Matches by base_url + model name, returns models.<model>.max_tokens if present and valid.
  • Adds "max_tokens" to _KNOWN_KEYS in _normalize_custom_provider_entry so the top-level key is not flagged as unknown (defensive, for users who had it at top level).

agent/agent_init.py

  • After the global model.max_tokens fallback (and before context_length resolution), adds a second fallback that checks custom_providers for a per-provider max_tokens when agent.max_tokens is still None.

Usage

custom_providers:
  - name: My Provider
    base_url: https://example.com/v1
    api_key: ...
    model: my-model
    api_mode: chat_completions
    models:
      my-model:
        context_length: 1000000
        max_tokens: 131072       # ← new per-provider field

Related

Closes #28782

…model>.max_tokens

Adds a new lookup function get_custom_provider_max_tokens() parallel to
the existing get_custom_provider_context_length(). When model.max_tokens
is unset globally, the agent init path now falls back to checking
custom_providers for a per-provider max_tokens override, matched by
base_url + model name.

This allows users to set a provider-scoped output-token cap without
affecting fallback providers in the chain.

Closes NousResearch#28782
@alt-glitch alt-glitch added type/feature New feature or request P3 Low — cosmetic, nice to have 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 labels May 19, 2026

@teknium1 teknium1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for the focused patch. The underlying gap still exists on current main for custom_providers[].models.<model>.max_tokens, but this implementation needs rework before it is safe to salvage.

Problems

  • The patch resolves the nested cap only during agent initialization. Current main sends agent.max_tokens on later requests (agent/chat_completion_helpers.py:650, :750, :782), while fallback activation and model switching change provider/model without recomputing that cap (agent/chat_completion_helpers.py:1045, agent/agent_runtime_helpers.py:1446). That can leak the primary custom provider's cap onto fallbacks, which is the behavior this PR is trying to avoid.
  • The new assignment is placed after the init path records _session_init_model_config["max_tokens"]; on current main the analogous write is agent/agent_init.py:1343, so the session metadata would not reflect the effective cap.
  • There is no regression coverage. Current main only tests the adjacent top-level provider cap path in tests/gateway/test_max_tokens_propagation.py:77.

Suggested changes

  • Move the lookup into a shared resolution helper used by startup, /model switch, and fallback activation.
  • Keep current precedence: explicit/env override > global model.max_tokens > provider/model-specific cap > None.
  • Add targeted tests for nested per-model config, invalid values, global override precedence, and fallback/switch non-leakage.

Automated hermes-sweeper review.

Comment thread agent/agent_init.py
@@ -1165,6 +1165,23 @@ def init_agent(
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Resolving this only during init makes agent.max_tokens a sticky session-wide value; fallback activation and /model switching later change provider/model without recomputing this cap, so the primary provider's cap can still leak onto fallback providers.

Comment thread agent/agent_init.py
base_url=agent.base_url,
custom_providers=get_compatible_custom_providers(_agent_cfg),
)
if _cp_max_tokens is not None:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Because _session_init_model_config["max_tokens"] was written just above this block, the session metadata will still record None when this nested provider cap is applied. Either resolve before recording the metadata or update the metadata after assigning agent.max_tokens.

Comment thread hermes_cli/config.py
@@ -3229,6 +3229,66 @@ def get_custom_provider_context_length(
return None

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This helper mirrors context_length lookup, but max_tokens has a stronger non-leakage requirement across fallback/switch paths. It should probably be part of a shared max_tokens resolution path rather than only called from startup.

@teknium1

Copy link
Copy Markdown
Contributor

Thanks for the focused configuration work. This automated hermes-sweeper review is closing it under the standing policy against new user-facing max_tokens configuration knobs.

  • The proposed custom_providers[].models.<model>.max_tokens is a new per-model output-cap setting, which matches the max-tokens-knob policy.
  • Current main already supports the narrower provider-wide alternative: hermes_cli/runtime_provider.py:592 accepts custom_providers[].max_output_tokens or max_tokens, and gateway/run.py:1875 applies it only when global model.max_tokens is unset.
  • That provider-level path shipped in 14275d7baa1a48844e58671f63ab49c4823c7cc0 (fix(gateway): honor per-provider max_output_tokens in max_tokens chain).

For provider-wide caps, please use the existing max_output_tokens configuration rather than adding another max-token configuration surface.


Closed as not-planned per standing maintainer policy (max-tokens-knob). This is a design-direction decision, not a code-quality judgment — see the Contribution Rubric in AGENTS.md for what the project is looking for. If you believe this policy was misapplied to your change, comment here and a maintainer will take a look.

@teknium1 teknium1 closed this Jul 13, 2026
@teknium1 teknium1 added the sweeper:not-planned Sweeper: closed per standing maintainer policy (design direction) label Jul 13, 2026
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 P3 Low — cosmetic, nice to have sweeper:not-planned Sweeper: closed per standing maintainer policy (design direction) type/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: custom_providers should support per-provider max_tokens override

3 participants