fix(custom_providers): resolve per-model context_length for publisher/slug model ids - #36852
Open
banditburai wants to merge 7 commits into
Open
fix(custom_providers): resolve per-model context_length for publisher/slug model ids#36852banditburai wants to merge 7 commits into
banditburai wants to merge 7 commits into
Conversation
LM Studio reports model ids as publisher/slug but users key custom_providers models: entries with the bare slug. Exact-match-first, then bare-slug fallback (mirrors _model_id_matches). Adds debug logging of resolved-vs-miss. Closes NousResearch#30178
Replaces the inline custom_providers models.get(model) loop (which missed on publisher/slug ids) with get_custom_provider_context_length, picking up the slug fallback. Refs NousResearch#30178
Replaces the inline per-model lookup in _format_session_info with get_custom_provider_context_length (slug fallback) and fixes a secondary bug: the old loop never matched on base_url. The displayed number was already correct via step-0b; this corrects the source label from (detected) to (config) and drops the non-standard bare-int models entry schema. Refs NousResearch#30178
The invalid context_length warning loop missed on publisher/slug ids, silently skipping the warning for slug-keyed models. Mirror the helper's slug fallback. Refs NousResearch#30178
The @context expansion ignored custom_providers per-model context_length. Thread it through get_model_context_length (step-0b -> slug-tolerant helper). Supersedes NousResearch#18844
Rename test_slug_collision_first_dict_key_wins → ..._and_exact_match_
precedence_with_prefixed_keys: the body only asserts deterministic
exact-match outcomes, never a genuine bare-slug collision (which would be
dict-order-dependent and brittle). Docstring now explains why the
collision case is deliberately not asserted.
Add test_multi_slash_id_strips_only_last_segment pinning 'org/team/model'
→ 'model' (rsplit('/',1)[1]). Refs NousResearch#30178
…bility seam
The existing feasibility tests stub get_model_context_length, so they
cannot observe a step-0b slug-resolution regression. Add a test that
leaves the resolver chain unmocked: an LM Studio publisher/slug aux model
id ('lmstudio/qwen3-coder-30b') against a bare-slug config key
('qwen3-coder-30b') must surface the full 1M window so the feasibility
check does not auto-lower the threshold.
Threshold is 500K — above DEFAULT_FALLBACK_CONTEXT (256K, the largest
probe tier) — so only the slug-resolved 1M can clear it; proven RED when
the slug branch is disabled (resolver→None→256K default<500K→warning).
Hermetic: step-0b short-circuits before any localhost probe.
Refs NousResearch#30178
This was referenced Jun 19, 2026
Closed
13 tasks
teknium1
reviewed
Jul 13, 2026
teknium1
left a comment
Contributor
There was a problem hiding this comment.
Thanks for tracing this through the shared resolver and its downstream gateway paths. The underlying defect remains on current main: hermes_cli/config.py:5150 only checks the exact models[model] key, while agent/model_metadata.py:2083-2096 relies on that helper before probing.
Problems
- The added gateway @-context test uses the pre-refactor
runner._model/runner._base_urlsetup. Current main resolves those values via_resolve_session_agent_runtime(gateway/run.py:10633-10645, introduced by265ac7d812192d3b6f11888811512604681d3a32), so that test needs to be ported as part of salvage.
Suggested changes
- Apply the exact-first, bare-slug fallback at the current helper seam (
hermes_cli/config.py:5150), then adapt the hygiene (gateway/run.py:11162-11188),/info(gateway/run.py:12363-12414), and invalid-value warning (agent/agent_init.py:1712-1745) changes to current main. - Base the gateway regression on
tests/gateway/test_context_ref_expansion_runtime.py:59-82so it exercises session-runtime resolution.
Automated hermes-sweeper review.
|
|
||
| captured = {} | ||
|
|
||
| async def fake_preprocess(message_text, *, cwd, context_length, allowed_root): |
Contributor
There was a problem hiding this comment.
Current main resolves the model and endpoint through _resolve_session_agent_runtime rather than GatewayRunner._model/_base_url (gateway/run.py:10633-10645). When salvaging this test, stub that resolver and assert the slug-resolved context reaches the current async preprocessor path.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
LM Studio reports model ids as
publisher/slug(e.g.nvidia/nemotron-3-nano-4b), butcustom_providers[].models:is keyed by the bare slug (nemotron-3-nano-4b).get_custom_provider_context_lengthmatched only the exact id, so per-modelcontext_lengthwas missed and the model fell back to the 256K default — mis-sizing compression and the@contextbudget (#30178, regressed in 0.14.0). Fix: try the exact key, then the bare slug, in that one resolver, and route the remaining inline lookups through it.Change
get_custom_provider_context_length(hermes_cli/config.py:3722-3748): after an exactmodels[model]miss on an id containing/, retrymodels[model.rsplit("/", 1)[1]]. Case-sensitive; exact match wins;ctx > 0guard; debug logs taggedexact/slugplus a miss log.gateway/run.py:8957-8961): replace the inline lookup loop (24 lines) with a call to the resolver./infoper-model (gateway/run.py:9766-9772): call the resolver; the match now requiresbase_url(the prior loop matched on model id alone)./infolegacy entry-level (gateway/run.py:9743-9750): retained for the top-levelcp.model+context_lengthschema.@contextbudget (gateway/run.py:8550-8568): passcustom_providerstoget_model_context_length.agent/agent_init.py:1362-1365): apply the same slug fallback so a non-integercontext_length(e.g."256K") on a slug-keyed model still warns.ContextCompressor.__init__, and theget_model_context_lengthstep-2 256K short-circuit.Resolution behavior
pub/mpub/m(exact)pub/mm(bare slug)pub/mPub/m/ wrong casem(no slash)pub/mpub/m@q4mTesting
uv run --extra dev pytest -p no:randomly tests/hermes_cli/test_custom_provider_context_length.py tests/gateway/test_session_hygiene.py tests/gateway/test_session_info.py tests/gateway/test_context_expansion_custom_provider.py tests/run_agent/test_invalid_context_length_warning.py tests/run_agent/test_compression_feasibility.py→ 77 passed.ruff checkclean (the tree is notruff format-managed).test_custom_provider_context_length.py(19): exact-wins, slug fallback, multi-slash, case sensitivity, quant-suffix and reverse-direction unsupported, trailing slash, non-positive, empty inputs.test_session_hygiene.py: slug-keyedcontext_lengthreachesget_model_context_lengththrough_handle_message.test_session_info.py:/infoshows1.0Mand(config).test_context_expansion_custom_provider.py: the@contextbudget uses the slug-resolved 1M through_prepare_inbound_message_text.test_invalid_context_length_warning.py:"256K"on a slug-keyed model warns through_build_agent.test_compression_feasibility.py::test_feasibility_resolves_slug_keyed_custom_provider_context: withget_model_context_lengthunmocked and a 500K threshold (above the 256K fallback tier), the feasibility check sees the slug-resolved 1M and does not lower the threshold; it fails if the slug fallback is removed.Verification limit: exercised through config and the resolver chain, not against a live LM Studio endpoint.
Note
The
/infoper-model lookup now requires abase_urlmatch. For a single provider the result is identical; for multiple providers sharing a model id, a non-matching provider'scontext_lengthis no longer shown. The displayed number was already correct viaget_model_context_lengthstep-0b; this affects the(detected)→(config)source label.Related
@contextthreading and the resolver-based hygiene lookup; both are implemented here (gateway/run.py:8550-8568,:8957-8961).auxiliary.*context_length ignorescustom_providers[].models[].context_length#26548 report bare-keyed (non-slug) configs; those paths threadcustom_providersintoget_model_context_lengthas of7becb19eaandb5bcffe16. [Bug]: Auxiliary compression model does not inherit context_length from custom_providers #21919 and [Bug]: Auxiliary compression model does not inherit context_length from custom_providers #21947 describe the same case.resolve_display_context_lengthhalf (hermes_cli/model_switch.py) is handled by0dd373ec4; theget_model_context_lengthstep-2 short-circuit is unchanged here.custom_providersthreading now in7becb19ea/b5bcffe16. fix(compression): pass custom providers to context resolver #26843 proposed passingcustom_providerstoContextCompressor.__init__; that constructor uses an already-resolvedconfig_context_length(step-0 returns before step-0b), so the window is resolved before construction.Closes #30178
Refs #18844