Summary
The "Add Custom Model" feature on the dashboard accepts max_input_tokens and max_output_tokens in the form payload, but the backend route discards both fields. Additionally, /v1/models does not expose context length metadata for any model (only returns id, object, owned_by), and passthrough kr/* aliases that aren't in the static registry fall back to a hardcoded 200K context in the internal resolver.
This means:
- Users cannot override context limits via the dashboard custom model feature (fields are silently dropped).
- OpenAI-compatible clients that read
/v1/models to determine context window (Cursor, Continue, etc.) have no metadata to work with and must hardcode or guess.
/v1/models/info returns 404 for passthrough aliases, with the internal resolver defaulting to 200K even for models that support 1M (e.g., Kiro Pro Claude Opus 4.7).
Tested on 9router 0.4.55 (latest at time of writing).
Reproduction
-
Login to dashboard, then:
curl -X POST http://<host>:20128/api/models/custom \
-H "Content-Type: application/json" \
-H "Cookie: auth_token=<...>" \
-d '{
"providerAlias": "kr",
"id": "claude-opus-4.7",
"name": "Claude Opus 4.7",
"category": "chat",
"max_input_tokens": 1000000,
"max_output_tokens": 64000
}'
Returns: {"success":true,"added":true}
-
Verify what was actually saved:
curl http://<host>:20128/api/models/custom \
-H "Cookie: auth_token=<...>"
Returns:
{"models":[{"providerAlias":"kr","id":"claude-opus-4.7","type":"llm","name":"Claude Opus 4.7"}]}
max_input_tokens and max_output_tokens are not persisted.
-
Check /v1/models response shape:
curl http://<host>:20128/v1/models -H "Authorization: Bearer <key>"
Every entry only contains {"id": "...", "object": "model", "owned_by": "..."} — no context_length, max_input_tokens, or similar field.
-
Check /v1/models/info for passthrough alias:
curl "http://<host>:20128/v1/models/info?id=kr/claude-opus-4.7" \
-H "Authorization: Bearer <key>"
Returns 404. The internal token-limit resolver falls back to 2e5 (200K).
Source evidence
app/api/models/custom/route.js (POST handler, deobfuscated):
let { providerAlias: b, id: c, type: d, name: e } = await a.json();
if (!b || !c) return v.NextResponse.json({error:"providerAlias and id required"}, {status:400});
let f = await S8({providerAlias:b, id:c, type:d||"llm", name:e});
Only 4 fields are destructured. max_input_tokens / max_output_tokens are never read.
Hardcoded fallback (chunks/8515.js, Kiro alias resolver):
d = Number(a?.tokenLimits?.maxInputTokens) || 2e5
Expected behavior
POST /api/models/custom should persist max_input_tokens / max_output_tokens when provided.
/v1/models should include context metadata (e.g., context_length or max_input_tokens) in each model entry, consistent with how OpenRouter, Together, and other OpenAI-compatible aggregators expose this.
- The Kiro alias resolver /
/v1/models/info should honor custom overrides when present, and ideally ship Opus 4.6/4.7 in the static kr: registry with their real 1M context limit (per https://kiro.dev/changelog/models/claude-opus-4-7-now-available/).
Workaround
Clients that support explicit context configuration (e.g., Hermes Agent's context_length field in config.yaml) can override on the client side. Clients that rely solely on /v1/models metadata have no workaround.
Environment
- 9router 0.4.55 (npm-global on Windows; also reproduced on Docker
decolua/9router:latest)
- Provider: Kiro (Builder ID OAuth, Pro tier accounts)
- Model:
kr/claude-opus-4.7 (passthrough alias)
cc related: #1244 (Kiro Pro account detected as Free) — same provider area, may be worth fixing together.
Summary
The "Add Custom Model" feature on the dashboard accepts
max_input_tokensandmax_output_tokensin the form payload, but the backend route discards both fields. Additionally,/v1/modelsdoes not expose context length metadata for any model (only returnsid,object,owned_by), and passthroughkr/*aliases that aren't in the static registry fall back to a hardcoded 200K context in the internal resolver.This means:
/v1/modelsto determine context window (Cursor, Continue, etc.) have no metadata to work with and must hardcode or guess./v1/models/inforeturns 404 for passthrough aliases, with the internal resolver defaulting to 200K even for models that support 1M (e.g., Kiro Pro Claude Opus 4.7).Tested on 9router 0.4.55 (latest at time of writing).
Reproduction
Login to dashboard, then:
Returns:
{"success":true,"added":true}Verify what was actually saved:
Returns:
{"models":[{"providerAlias":"kr","id":"claude-opus-4.7","type":"llm","name":"Claude Opus 4.7"}]}max_input_tokensandmax_output_tokensare not persisted.Check
/v1/modelsresponse shape:Every entry only contains
{"id": "...", "object": "model", "owned_by": "..."}— nocontext_length,max_input_tokens, or similar field.Check
/v1/models/infofor passthrough alias:Returns 404. The internal token-limit resolver falls back to
2e5(200K).Source evidence
app/api/models/custom/route.js(POST handler, deobfuscated):Only 4 fields are destructured.
max_input_tokens/max_output_tokensare never read.Hardcoded fallback (chunks/8515.js, Kiro alias resolver):
Expected behavior
POST /api/models/customshould persistmax_input_tokens/max_output_tokenswhen provided./v1/modelsshould include context metadata (e.g.,context_lengthormax_input_tokens) in each model entry, consistent with how OpenRouter, Together, and other OpenAI-compatible aggregators expose this./v1/models/infoshould honor custom overrides when present, and ideally ship Opus 4.6/4.7 in the statickr:registry with their real 1M context limit (per https://kiro.dev/changelog/models/claude-opus-4-7-now-available/).Workaround
Clients that support explicit context configuration (e.g., Hermes Agent's
context_lengthfield in config.yaml) can override on the client side. Clients that rely solely on/v1/modelsmetadata have no workaround.Environment
decolua/9router:latest)kr/claude-opus-4.7(passthrough alias)cc related: #1244 (Kiro Pro account detected as Free) — same provider area, may be worth fixing together.