Conversation
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>
teknium1
left a comment
There was a problem hiding this comment.
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 orcustom_providers: {}interaction.
Suggested changes
- Add focused tests for both cases above, ideally exercising the normalized
providers:path throughget_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_providersatcli.py:7934andcli.py:8243, via9d848cc60.
Automated hermes-sweeper review.
| 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 |
There was a problem hiding this comment.
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.
| else: | ||
| if ctx > 0: | ||
| return ctx | ||
| # 2. Provider-level fallback: when the entry's default model |
There was a problem hiding this comment.
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.
|
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. |
Problem
/modelswitch always showsContext: 256,000 tokensfor custom providers configured viaproviders:in config.yaml, regardless of the actualcontext_lengthvalue set on the provider entry.This affects all custom provider setups (vertex-proxy, LiteLLM, or any
providers:entry) using the common config shape:Root Cause
Three bugs in the resolution chain:
1.
get_compatible_custom_providers()early-returns on dict-shapedcustom_providersWhen
custom_providers: {}exists in config.yaml (an empty dict, not a list), thenot isinstance(custom_providers, list)guard returns[], skipping theproviders:dict loop below it. All user-configured providers become invisible to the context_length lookup.2.
get_custom_provider_context_length()only checks nestedmodels.<model>.context_lengthThe common config shape has
modelandcontext_lengthas siblings at the provider level, with no nestedmodels:dict. The lookup returnsNoneand the chain falls through toDEFAULT_FALLBACK_CONTEXT = 256_000.3.
resolve_display_context_length()never loadscustom_providersThe function accepts
custom_providersas a parameter, but neither call site incli.pypasses it. Whencustom_providersisNone, 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-shapedcustom_providersby normalizing it the same way as theproviders:dict, instead of early-returning.get_custom_provider_context_length(): after the existingmodels.<model>.context_lengthlookup, fall back to the provider-levelcontext_lengthwhen the entry's defaultmodelmatches the requested model.hermes_cli/model_switch.py:
resolve_display_context_length(): whencustom_providersisNone, load it from config viaget_compatible_custom_providers(load_config())so the resolver sees user-configured provider entries.agent/agent_runtime_helpers.py:
new_context_lengthback toagent._config_context_lengthafterswitch_modelcompletes, so the display path in cli.py reads the correct value on subsequent/modelcalls.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:
After fix:
Automated: All existing tests pass:
test_config.py-- 87 passedtest_config_validation.py-- 21 passed (dict-shaped custom_providers detection still works)test_model_switch_custom_providers.py-- 19 passedtest_custom_provider_context_length-- 12 passed, 2 skippedcustom_provider_context or model_switch_custom-- 31 passed, 2 skippedSupersedes #36199 and #37712.
Signed-off-by: Chris van Hoof vanhoof@ouwish.com