Skip to content

fix(config): resolve provider-level context_length on /model switch - #37716

Closed
vanhoof wants to merge 1 commit into
NousResearch:mainfrom
vanhoof:fix/config-provider-context-length-v3
Closed

vanhoof wants to merge 1 commit into
NousResearch:mainfrom
vanhoof:fix/config-provider-context-length-v3

Conversation

@vanhoof

@vanhoof vanhoof commented Jun 2, 2026

Copy link
Copy Markdown

Problem

/model switch always shows Context: 256,000 tokens for custom providers configured via providers: in config.yaml, regardless of the actual context_length value set on the provider entry.

/model vertex-opus46
  ✓ Model switched: claude-opus-4.6
    Provider: vertex-opus46
    Context: 256,000 tokens    ← wrong, should be 1,000,000

This affects all custom provider setups (vertex-proxy, LiteLLM, or any providers: entry) using the common config shape:

providers:
  vertex-opus46:
    base_url: http://127.0.0.1:8788/anthropic
    model: claude-opus-4.6
    context_length: 1000000
    api_mode: anthropic_messages

Root Cause

Three bugs in the resolution chain:

1. get_compatible_custom_providers() early-returns on dict-shaped custom_providers

When custom_providers: {} exists in config.yaml (an empty dict, not a list), the not isinstance(custom_providers, list) guard returns [], skipping the providers: dict loop below it. All user-configured providers become invisible to the context_length lookup.

2. get_custom_provider_context_length() only checks nested models.<model>.context_length

The common config shape has model and context_length as siblings at the provider level, with no nested models: dict. The lookup returns None and the chain falls through to DEFAULT_FALLBACK_CONTEXT = 256_000.

3. resolve_display_context_length() never loads custom_providers

The function accepts custom_providers as a parameter, but neither call site in cli.py passes it. When custom_providers is None, the resolver has nothing to check against and falls through to the default.

Fix

hermes_cli/config.py:

  • get_compatible_custom_providers(): gracefully handle dict-shaped custom_providers by normalizing it the same way as the providers: dict, instead of early-returning.
  • get_custom_provider_context_length(): after the existing models.<model>.context_length lookup, fall back to the provider-level context_length when the entry's default model matches the requested model.

hermes_cli/model_switch.py:

  • resolve_display_context_length(): when custom_providers is None, load it from config via get_compatible_custom_providers(load_config()) so the resolver sees user-configured provider entries.

agent/agent_runtime_helpers.py:

  • Persist the resolved new_context_length back to agent._config_context_length after switch_model completes, so the display path in cli.py reads the correct value on subsequent /model calls.

Testing

Manual: Tested with providers: config entries pointing at a local proxy (vertex-proxy on port 8788, Anthropic wire protocol). Gateway restarted between tests.

Before fix:

/model vertex-opus46   → Context: 256,000 tokens
/model vertex-haiku    → Context: 256,000 tokens

After fix:

/model vertex-opus46   → Context: 1,000,000 tokens
/model vertex-haiku    → Context: 200,000 tokens

Automated: All existing tests pass:

  • test_config.py -- 87 passed
  • test_config_validation.py -- 21 passed (dict-shaped custom_providers detection still works)
  • test_model_switch_custom_providers.py -- 19 passed
  • test_custom_provider_context_length -- 12 passed, 2 skipped
  • Full custom_provider_context or model_switch_custom -- 31 passed, 2 skipped

Supersedes #36199 and #37712.

Signed-off-by: Chris van Hoof vanhoof@ouwish.com

Three bugs prevented `providers.<name>.context_length` from being
used when switching models via `/model`:

1. `get_compatible_custom_providers()` returned `[]` when
   `custom_providers` was an empty dict (`{}`) instead of a list.
   The early return skipped the `providers:` dict loop entirely, so
   user-configured providers were invisible to the context_length
   lookup chain.

2. `get_custom_provider_context_length()` only checked the nested
   `models.<model>.context_length` path. The common config shape
   (`providers.<name>` with `model` + `context_length` as siblings,
   no nested `models` dict) was never matched, so the lookup
   returned `None` and fell through to the 256K default.

3. `resolve_display_context_length()` accepted `custom_providers`
   as a parameter but neither call site in `cli.py` passed it. When
   `custom_providers` is `None`, load it from config so the resolver
   sees user-configured provider entries.

Also persist the resolved context_length back to
`agent._config_context_length` after `switch_model` so the display
path reads the correct value on subsequent `/model` calls.

Supersedes NousResearch#36199 and NousResearch#37712.

Signed-off-by: Chris van Hoof <vanhoof@ouwish.com>
@alt-glitch alt-glitch added type/bug Something isn't working comp/cli CLI entry point, hermes_cli/, setup wizard area/config Config system, migrations, profiles P2 Medium — degraded but workaround exists labels Jun 2, 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 provider-resolution chain. The remaining config behavior is still needed on current main: hermes_cli/config.py:4947-4955 returns early when custom_providers is a dict, and hermes_cli/config.py:5147-5161 ignores a provider-level model plus context_length.

Problems

  • The PR changes three production files but adds no regression coverage for either config-schema case. Existing tests exercise nested models.<id>.context_length (tests/hermes_cli/test_custom_provider_context_length.py:15-143), not the provider-level fallback or custom_providers: {} interaction.

Suggested changes

  • Add focused tests for both cases above, ideally exercising the normalized providers: path through get_compatible_custom_providers() and then context resolution.
  • The resolver's implicit config reload should be reconsidered when salvaging: current CLI callers already pass agent._custom_providers at cli.py:7934 and cli.py:8243, via 9d848cc60.

Automated hermes-sweeper review.

Comment thread hermes_cli/config.py
entry_url = (entry.get("base_url") or "").rstrip("/")
if not entry_url or entry_url != target_url:
continue
# 1. Per-model override: models.<model_name>.context_length

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.

Please add regression coverage for this compatibility branch: custom_providers: {} plus a valid providers: entry must still return the normalized provider, rather than silently hiding it.

Comment thread hermes_cli/config.py
else:
if ctx > 0:
return ctx
# 2. Provider-level fallback: when the entry's default model

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.

Please add a paired regression test for the provider-level fallback, including a non-matching requested model so a provider's default context length cannot leak to another model at the same endpoint.

@teknium1 teknium1 added sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform labels Jul 13, 2026
@vanhoof

vanhoof commented Aug 7, 2026

Copy link
Copy Markdown
Author

Closing as superseded upstream. The provider-level context_length gap on /model switch is now handled on main by #69712 (fix(cli): honor custom_providers in preflight shrink warning), which threads agent._custom_providers into the resolver at the call site — the same fix this PR proposed. Verified against current main during a runtime rebase.

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/cli CLI entry point, hermes_cli/, setup wizard P2 Medium — degraded but workaround exists sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants