Skip to content

fix(model-metadata): extend Kimi 32k guard to Nous OpenRouter suffix-match - #24066

Closed
briandevans wants to merge 1 commit into
NousResearch:mainfrom
briandevans:fix/nous-kimi-32k-guard-24000
Closed

fix(model-metadata): extend Kimi 32k guard to Nous OpenRouter suffix-match#24066
briandevans wants to merge 1 commit into
NousResearch:mainfrom
briandevans:fix/nous-kimi-32k-guard-24000

Conversation

@briandevans

Copy link
Copy Markdown
Contributor

Summary

provider: nous with a Kimi-family model (e.g. moonshotai/kimi-k2.6) cannot boot Hermes Agent: get_model_context_length() resolves the model to 32,768 tokens, which trips the 64K minimum-context guard at run_agent.py:2254. The user-facing message claims the model has a 32k window, but kimi-k2.6 actually supports 262,144.

Fixes #24000.

The bug

PR #23980 (Kimi-family 32k guard) and the related #23950 / e2b713c (skip OpenRouter for known providers, add kimi/moonshot to PROVIDER_TO_MODELS_DEV) landed a guard that rejects OpenRouter's stale moonshotai/kimi-k2.6 → 32768 metadata. But that guard lives inside the step-6 OpenRouter fallback in get_model_context_length, gated on if not effective_provider:.

provider: nous is a known provider that intentionally piggybacks on OpenRouter via suffix-match (Nous Portal doesn't appear in models.dev, and adding it would be wrong). For nous, the resolver hits step 5b's _resolve_nous_context_length first — which queries the same OpenRouter cache and returned the 32,768 value directly. The step-6 guard never gets a chance to run because step 5b's return is non-None.

Trace for provider=nous, model=moonshotai/kimi-k2.6:

get_model_context_length(model="moonshotai/kimi-k2.6", provider="nous", ...)
  -> step 5b _resolve_nous_context_length(model)
       metadata["moonshotai/kimi-k2.6"] -> {"context_length": 32768}
       return 32768                                            <-- bug
  -> step 6 (if not effective_provider:)                       <-- skipped
  -> step 7 DEFAULT_CONTEXT_LENGTHS "kimi" -> 262144           <-- unreached

The fix

Apply the same narrow Kimi guard inside _resolve_nous_context_length: when a suffix-matched entry yields exactly 32768 AND _model_name_suggests_kimi(model) matches, treat the entry as not found and let later resolution paths fire. The resolver then falls through to DEFAULT_CONTEXT_LENGTHS["kimi"] = 262144 via the existing longest-substring match.

The filter is intentionally narrow:

  • It only rejects 32768, not other context values OpenRouter might report.
  • It only fires for model names starting with kimi or containing moonshot (existing helper).
  • It does not change behavior for any non-Kimi nous-routed model. A real 32k non-Kimi model still resolves to 32k correctly (covered by the second regression test).

If OpenRouter ever updates its metadata for kimi-k2.6, the filter becomes dead code with no impact, matching the existing comment on the step-6 guard.

Why this is the right scope

Adding nous to PROVIDER_TO_MODELS_DEV would not help: Nous Portal is documented as suffix-matching against OpenRouter, not as a first-class models.dev entry. The suffix-match path is the correct flow; the fix is to make it as robust as the step-6 fallback that #23980 already hardened.

Test plan

  • Focused regression test: test_nous_kimi_32k_guard_falls_through_to_default — mocks fetch_model_metadata to return the stale {"moonshotai/kimi-k2.6": 32768} entry, asserts result is 262144.
  • Negative case: test_nous_non_kimi_32k_is_preserved — verifies a legitimate 32k non-Kimi model still resolves to 32k (the guard is narrow).
  • Adjacent suite: tests/agent/test_model_metadata.py (96 passed), tests/agent/test_nous_rate_guard.py (32 passed), tests/hermes_cli/test_runtime_provider_resolution.py (109 passed).
  • Regression guard: with the fix reverted, test_nous_kimi_32k_guard_falls_through_to_default fails with assert 32768 == 262144 — confirms the test actually exercises the new code path.
uv run --with pytest --with pytest-xdist --with pytest-asyncio python3 \
  -m pytest tests/agent/test_model_metadata.py -v
# 96 passed in 1.99s

Related

Sibling code paths that may need the same fix: none I'm aware of in the current tree — the step-6 OR fallback already has its own Kimi guard, and _resolve_nous_context_length is the only other place that reads OR's context_length field directly without going through lookup_models_dev_context. Happy to widen if I missed one.

…match

OpenRouter reports 32768 for moonshotai/kimi-k2.6, even though the model
supports 262144. PR NousResearch#23980 added a Kimi-family guard to the step-6
OpenRouter fallback in get_model_context_length, but that guard is gated
on `not effective_provider`. When provider='nous', the resolver hits
step 5b's `_resolve_nous_context_length` first — which suffix-matches
the same OpenRouter cache and returned 32768 directly, tripping the
64k minimum-context guard at run_agent.py and blocking boot for any
Nous Portal config using a Kimi model.

This applies the same narrow guard inside the nous resolver: if the
suffix-matched entry returns exactly 32768 and the model name suggests
Kimi, reject it and let the resolver fall through to the curated
DEFAULT_CONTEXT_LENGTHS table (where "kimi" → 262144 wins via
longest-substring match). All non-Kimi paths are unchanged: a real
32k non-Kimi model still resolves correctly.

Fixes NousResearch#24000

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 12, 2026 00:14

Copilot AI 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.

Pull request overview

Extends the existing “Kimi-family 32K underreport” guard so that provider: nous (which resolves context length via OpenRouter suffix-match) no longer returns OpenRouter’s stale 32768 value for Kimi models and instead falls through to the curated defaults (e.g. 262,144 for kimi).

Changes:

  • Add a narrow ctx == 32768 && _model_name_suggests_kimi(model) filter inside _resolve_nous_context_length() to treat that OpenRouter value as “not found” for Nous-routed Kimi models.
  • Add regression tests covering the Nous+Kimi fallthrough to defaults and a negative case ensuring legitimate non-Kimi 32K models are preserved.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
agent/model_metadata.py Applies the Kimi 32K-underreport guard to the Nous/OpenRouter suffix-match resolver so later resolution paths (defaults) can run.
tests/agent/test_model_metadata.py Adds targeted tests validating the new fallthrough behavior and guarding against overbroad filtering.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@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 provider/nous Nous Research API (OAuth) P2 Medium — degraded but workaround exists labels May 12, 2026
@briandevans

Copy link
Copy Markdown
Contributor Author

CI audit — all 3 failures on this PR are pre-existing baselines on clean origin/main (e855925 and the prior commit ced1990 directly before this PR's run). Zero failures touch the changed code (agent/model_metadata.py + tests/agent/test_model_metadata.py).

Job Symptom Root cause on main
Tests / e2e test_platform_commands.pyExpected 'reset_session' to have been called once. Called 0 times Reproduces on ced1990c1 (run 25704964023) with identical failure list; runner doesn't invoke session_store.reset_session for slash-command paths. Unrelated to model_metadata.
Tests / test 19 failures across auxiliary_client, tts_media_routing, gateway/test_config, update_streaming, verbose_command, async_httpx_del_neuter, update_gateway_restart, provider_parity, web_server, ctx_halving_fix, vision_native_fast_path Same 19-failure set on ced1990c1 baseline (run 25704964023).
Lint / Windows footguns tools/process_registry.py:588: [bare os.killpg / bare signal.SIGKILL] Same failure on current origin/main (e855925, run 25706932794). The call is platform-gated under if not _IS_WINDOWS: — false positive in the footgun checker. Already being addressed in #23816.

PR is otherwise green and isolated to the Kimi 32k Nous-suffix path.

@briandevans

Copy link
Copy Markdown
Contributor Author

Closing — superseded on main.

  • 528bba673 (fix kimi, @rob-maron, 2026-05-12) added the same _safe_ctx guard inside _resolve_nous_context_length that this PR was adding, plus extra coverage I didn't have: it also invalidates stale 32k cache entries for Kimi-family models in get_model_context_length, so an already-poisoned on-disk cache recovers on the next call.
  • #24502 (Use nous portal as model metadata authority, merged 2026-05-12) then refactored the resolver to be portal-first with a (ctx, source) return so callers can decide whether to persist OpenRouter values — closing the structural loophole this PR was guarding against.

Net effect on main is strictly broader than this PR. Thanks @rob-maron.

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

Labels

comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint P2 Medium — degraded but workaround exists provider/nous Nous Research API (OAuth) type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

provider: nous falls back to 32,768-token context, blocking boot with model.context_length workaround required

3 participants