Skip to content

provider, catalog: a prompt cap above a model's window is held to the window (#1286) - #1288

Merged
yetone merged 1 commit into
yetone:mainfrom
charan-rathore:fix/plugin-context-window
Oct 8, 2026
Merged

yetone merged 1 commit into
yetone:mainfrom
charan-rathore:fix/plugin-context-window

Conversation

@charan-rathore

@charan-rathore charan-rathore commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

Problem

A catalog row whose limit.input sits above its limit.context was served past its window: opencode/hy3-free (190000 window, 192000 input), Cloudflare AI Gateway's openai/gpt-5* (128000 / 272000), venice/openai-gpt-52 (256000 / 272000). Since catalog.Model.Context is what the gateway's /v1/models serves as context_window/context_length/max_input_tokens and what agents compact at, those rows advertise more than the model holds.

Solution

The input limit still wins wherever it sits under the window — it is the real prompt cap for the models that carry one (gpt-5's 272K of its 400K), and OpenCode's own client compacts at input too. The change only clamps a cap that exceeds the window, in both places the rule lives: pluginCatalog (plugin path) and mdModel.window() (models.dev path). A row naming only one number keeps it, as before.

This reframes the PR after review on #1286's premise: for space-bunny-free itself the catalog entry is the wrong part (the API accepts prompts well past 524288), so that model's fix belongs in the models.dev data, not in the rule.

Testing

Ran on Go 1.26.6, -tags nogui, under a temp HOME:

  • New TestPluginCatalogClampsInputAboveWindow (hy3-free clamps 192000 -> 190000; gpt-5-codex keeps its 272000 cap; input-only and context-only fallbacks unchanged) and TestWindowClampsInputAboveContext for the models.dev path: both fail on current main (hy3-free serves 192000; window() returns 272000 over a 128000 context), pass with the change.
  • go test -tags nogui ./internal/provider ./internal/catalog passes, including the restored TestPluginModelWindow, TestCursorPluginContext, and plugin parity tests. The diff is purely additive (+64 -0).
  • go vet and go build -tags nogui ./... are clean.

@yetone

yetone commented Oct 8, 2026

Copy link
Copy Markdown
Owner

Input for the review of this PR (from a parallel investigation of #1286; not a verdict).

1. limit.input is a real prompt cap for most of the models that carry it, including on the same OpenCode provider. https://models.opencode.ai/api.json and https://models.dev/api.json were byte-identical when I fetched them today (5,361,863 bytes, cmp clean). So this is not a separate catalog whose input means something different. In it, opencode (Zen) has 27 models with limit.input. 24 of them are OpenAI's GPT-5 family, for example:

model (provider opencode) context input output
gpt-5, gpt-5-codex, gpt-5.1-codex, gpt-5.2, gpt-5.4-mini 400000 272000 128000
gpt-5.4, gpt-5.5, gpt-5.6-sol, gpt-6-sol 1050000 922000 128000
space-bunny-free 1048576 524288 524288
big-pickle 200000 160000 32000
hy3-free 190000 192000 64000

The GPT-5 rows also satisfy input + output == context, so that test can't separate the cases. 272K/922K is the prompt cap OpenAI enforces, and Codex's own catalog uses it. github-copilot has the same pattern (claude-opus-4.8 200000/168000/64000, gpt-5-mini 264000/128000/64000), and Copilot's max_prompt_tokens is the real limit there. With this PR, any plugin that passes these through (an OpenCode Zen or Copilot plugin) is served at 400K/1.05M. Agents then compact past what the API accepts. This is the e950eb0 lesson (a rule from one vendor's report applied to every model) in reverse.

Review test I ran on this PR's head edc0b0f3 merged onto main 67329b8d (merge 677252ca, -tags nogui, temp HOME). It's a plugin opencode provider with gpt-5.5 {1050000, 922000, 128000} and gpt-5-codex {400000, 272000, 128000}:

review1288_test.go:17: gpt-5.5: 1050000, want 922000
review1288_test.go:17: gpt-5-codex: 400000, want 272000
--- FAIL: TestReview1288GPTCap

On main it passes. TestPluginCatalogPrefersContextWindow and TestCursorPluginContext pass on the merge.

2. OpenCode's own client uses input as the budget. It doesn't keep context as the window. In packages/opencode/src/session/overflow.ts usable(), when limit.input is set it returns limit.input - reserved (reserved = min(20000, max output)). So OpenCode compacts space-bunny-free at about 504K. magpie's current 524288 agrees with OpenCode.

3. For space-bunny-free alone, the catalog's number is wrong. I sent it directly to https://opencode.ai/zen/v1/chat/completions (Bearer public, max_tokens 16). Both prompts were accepted, with HTTP 200: usage.prompt_tokens 577812 and 866292. So the reporter is right about this model, but the fix belongs to that catalog entry (models.dev), or at most to that one model. It is not a reason to flip the rule for every plugin model.

4. One real bug in the current rule: input > context. Examples are opencode/hy3-free 190000/192000, Cloudflare AI Gateway's openai/gpt-5* 128000/272000, and venice/openai-gpt-52 256000/272000. These are served above their window. min(input, context) (when both are set) fixes it. The same applies to mdModel.window() in internal/catalog/catalog.go, the sibling of pluginCatalog.

5. The diff deletes an unrelated existing test. plugin_context_test.go is replaced whole. That removes clinePlugin, pluginWindow and TestPluginModelWindow (Cline's models taking models.dev windows by vendor prefix, and the user's SetContext override, ARNO / v0.1.815). The PR description doesn't mention this. The new test belongs in a file of its own, or appended.

What catalog.Model.Context drives. It is documented as "how many tokens a prompt may hold" (catalog.go, from 09c635c). The gateway's /v1/models serves it as context_window, context_length and max_input_tokens (gateway.go, the model list). It is also written into each agent's model list: dsh contextWindow, Pi, Crush, omp, Codex's catalog and others under internal/agent. Agents compact at it, so a number above the real prompt cap means requests that get rejected, not merely early compaction.

… window (#1286) — a catalog row with limit.input over limit.context (opencode/hy3-free's 192K over 190K, Cloudflare AI Gateway's gpt-5 at 272K over 128K, venice/openai-gpt-52) was served past its window, to the gateway's /v1/models and to every agent compacting at it. The input limit still wins wherever it sits under the window (gpt-5's 272K of its 400K, a real prompt cap; OpenCode's own client compacts at input too), and a row naming only one number keeps it. TestPluginCatalogClampsInputAboveWindow and TestWindowClampsInputAboveContext fail without the change. go vet, go build -tags nogui and go test -tags nogui ./internal/provider ./internal/catalog under a temp HOME pass.
@charan-rathore
charan-rathore force-pushed the fix/plugin-context-window branch from edc0b0f to 3820c8b Compare October 8, 2026 09:53
@charan-rathore charan-rathore changed the title provider: a plugin model carrying both a context window and an input … provider, catalog: a prompt cap above a model's window is held to the window (#1286) Oct 8, 2026
@charan-rathore

Copy link
Copy Markdown
Contributor Author

Thanks for the detailed pass - I reproduced each point before touching the code.

  1. Confirmed with your exact test (plugin opencode provider, gpt-5.5 {1050000, 922000, 128000}, gpt-5-codex {400000, 272000, 128000}): fails on the old head with your numbers (1050000 served), passes on main, and passes again on the revised head. The PR no longer flips the preference - input wins wherever it sits under the window. The byte-identical catalogs and the Copilot max_prompt_tokens pattern settle it: input is a real cap.
  2. Noted - overflow.ts usable() returning limit.input - reserved is the vendor behavior to match, so magpie's current 524288 for space-bunny-free agrees with OpenCode itself. The "halved" framing is out of the PR.
  3. Agreed - HTTP 200 at 577812 and 866292 prompt tokens proves the entry, not the rule, is wrong for that one model. The body now says the fix belongs in the models.dev data. Happy to file that correction upstream at models.dev if you want it tracked.
  4. Done in both places: pluginCatalog and mdModel.window() now clamp a cap that sits above the window (hy3-free 192000 -> 190000; Cloudflare AI Gateway's 272000 over 128000). TestPluginCatalogClampsInputAboveWindow and TestWindowClampsInputAboveContext fail on main and pass here; the GPT-5 caps are in the tests as controls (272000 kept).
  5. My mistake - I wrote the new test over plugin_context_test.go instead of adding to it. Restored: clinePlugin, pluginWindow and TestPluginModelWindow are back and passing, the new test is appended, and the diff is now purely additive (+64 -0).

Force-pushed as 3820c8b. go vet, go build -tags nogui, and go test -tags nogui ./internal/provider ./internal/catalog under a temp HOME all pass.

@yetone yetone left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @charan-rathore, and thanks to the #1286 reporter for the detailed write-up. Reviewed at head edc0b0f33c, merged onto main 4a8338cc in a sandbox HOME. I can't merge this as it is, for two reasons.

1. It deletes an existing test. The PR replaces the whole of internal/provider/plugin_context_test.go. That removes TestPluginModelWindow and its clinePlugin helper: a plugin model with no window takes models.dev's window under the vendor's prefix, and the user's own window set on the provider beats both (ARNO on Discord, v0.1.815). That test isn't about this change and must stay. Please add your test beside it, or in a file of its own. (I ran main's version of that test against this PR's plugins.go and it still passes, so only the guard is lost, not the behavior.)

2. input is the prompt budget, and OpenCode's own client uses it that way.

  • catalog.Model.Context is documented as "how many tokens a prompt may hold": models.dev's input limit, else its context window. Plugin models get input from the same models.dev data (fromModelsDev in internal/plugin/host.js). mdModel.window() prefers input on purpose: gpt-5's 272K of its 400K is OpenAI's real prompt cap.
  • With this PR, a plugin serving gpt-5 goes from 272000 to 400000 on the plugin path only, while the models.dev path still says 272000. I checked this with a review test: pluginCatalog with {Context: 400000, Input: 272000} gives 400000 at this head and 272000 on main. An agent told 400K would send prompts OpenAI refuses past 272K.
  • OpenCode's packages/opencode/src/session/overflow.ts usable(), the function the issue quotes, compacts at limit.input - reserved whenever input is set. Without input it compacts at context - maxOutput. So for space-bunny-free OpenCode itself compacts near 504K, not 1M. Today magpie matches OpenCode's own client. With input = context - output the two rules give the same budget, which is why the arithmetic in the issue lines up.

What I ran

  • TestPluginCatalogPrefersContextWindow fails on main's plugins.go ("space-bunny-free: 524288, want 1048576") and passes with the change. TestCursorPluginContext passes.
  • The gpt-5 review test above.
  • Main's TestPluginModelWindow against this PR's code: passes.

What would move this forward: evidence that a model's upstream accepts prompts above its limit.input, such as a real request to space-bunny-free with more than 524288 prompt tokens that succeeds. If some OpenCode entries really carry a wrong input (hy3-free's input > context looks like one), the fix belongs in that data, or in a narrowly scoped rule such as clamping input to context, with a test that gpt-5's 272K is unchanged. Not one that drops input for every plugin model.

@charan-rathore

Copy link
Copy Markdown
Contributor Author

Quick note since the formal review landed against the old head: current head 3820c8b implements exactly the narrow shape you described - input kept as the budget, clamped to context only when it sits above the window, gpt-5's 272K covered as a control test, and the deleted test file restored in full (diff is +64 -0 now). Details in my comment above. Ball's in your court, no rush.

@yetone

yetone commented Oct 8, 2026

Copy link
Copy Markdown
Owner

Re-reviewed at head 3820c8ba, merged onto current main 1f140046 (merge 780fc744), -tags nogui, temp HOME with Go caches pinned. This head is the narrow fix the earlier review asked for, so I'm merging it.

Ran

  • go test -tags nogui -count=1 ./internal/catalog ./internal/provider ./internal/gateway ./internal/agent on the merge with main 14ed370e: all ok. ./internal/catalog ./internal/provider again on the merge with 1f140046: ok.
  • go vet -tags nogui (darwin, GOOS=linux, GOOS=windows) on ./internal/catalog ./internal/provider: clean. go build -tags nogui . for darwin, linux and windows: ok.
  • Fails without the change: with main's plugins.go and catalog.go, TestPluginCatalogClampsInputAboveWindow fails ("hy3-free: 192000, want 190000") and TestWindowClampsInputAboveContext fails ("input above context: 272000, want 128000"). With the change both pass.
  • Broke it on purpose: I made pluginCatalog take context whenever it is set (the provider: plugin models keep limit.context as the window, not limit.input (#1286) #1296 rule). The PR's test fails ("gpt-5-codex: 400000, want 272000"), and so does a review test with real OpenCode rows (gpt-5.5 1050000 vs 922000, gpt-5-codex, space-bunny-free 1048576 vs 524288, big-pickle). With the PR as written that review test passes: gpt-5.5 922000, gpt-5-codex 272000, space-bunny-free 524288, big-pickle 160000, hy3-free 190000.
  • Real models.dev data (api.json fetched today): I dumped every Provider(pid) model's Context and every ContextOf(id) on main and on this head. Exactly the 10 rows with input above context change: opencode/hy3-free 192000→190000, venice/openai-gpt-52 272000→256000, six cloudflare-ai-gateway/openai/gpt-5* 272000→128000, and two qwen3.7-flash rows. Nothing else changes, and ContextOf("gpt-5") stays 272000.
  • TestPluginModelWindow and TestCursorPluginContext are kept and pass. The diff is additive.

Not verified: a live plugin model through the running gateway. Also, Cloudflare AI Gateway's 128000 is what models.dev says, not a value checked against Cloudflare. The clamp follows the data, and the smaller number is the safe side. space-bunny-free stays at 524288 (its input entry is the models.dev data problem noted in #1286).

@yetone
yetone merged commit 5f0490a into yetone:main Oct 8, 2026
8 checks passed
@charan-rathore
charan-rathore deleted the fix/plugin-context-window branch October 8, 2026 10:49
charan-rathore added a commit to charan-rathore/charan-rathore that referenced this pull request Oct 8, 2026
@yetone

yetone commented Oct 8, 2026

Copy link
Copy Markdown
Owner

Shipped in v0.1.1118. Thanks!

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants