From 1c5e8eb8ab6a47a167030ca49ee4478f7673b9cd Mon Sep 17 00:00:00 2001 From: Khalil Gharbaoui Date: Thu, 20 Aug 2026 01:05:44 +0200 Subject: [PATCH] Bump Sonnet 5 to standard pricing (closes #22) Sonnet 5's introductory $2/M in, $10/M out pricing expires 2026-08-31. Point it at sonnetCost ($3/$15), multiplier 3x, and drop sonnet5Cost. Updates the README table and pricing paragraph, the test-config-models assertions, and the AGENTS.md reminder. Introductory pricing was originally added by @broskees in #18. --- AGENTS.md | 2 +- README.md | 4 ++-- src/models.ts | 10 +++------- test-config-models.ts | 10 +++++----- 4 files changed, 11 insertions(+), 15 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index ed16609..ee8c100 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -40,7 +40,7 @@ - Reasoning stream parts are only started after the first non-empty `thinking_delta`. This prevents empty Thinking rows when the CLI opens a thinking block but streams no text. - opencode's own reasoning features (e.g. v1.17.0 "Added Claude Fable reasoning support", vLLM interleaved `reasoning` field) live in opencode's **native** Anthropic/vLLM runtime, which this plugin deliberately bypasses by routing through the `claude` CLI. There is nothing to "switch to" — the plugin implements reasoning itself (reasoning variants → thinking keyword + `--thinking`/`--thinking-display` flags → `thinking_delta` forwarding), and any model defined with `reasoning: true` (including `claude-fable-5`) inherits the full path automatically. Do not re-investigate adopting opencode's native reasoning; it would mean abandoning the CLI wrapper. - Model display names carry a list-price multiplier as a `(N×)` suffix (`src/models.ts` `defineModel`, via the `multiplier` field): haiku 1×, sonnet 3×, opus 5×, fable 10×, mythos 10×. These are exact ratios of published per-token price vs Haiku (input and output ratios coincide), so Fable/Mythos = 2× Opus. opencode has no native multiplier field, so the suffix is the only way it surfaces in the picker; it's display-only and model resolution still keys off `id`. `test-config-models.ts` asserts the suffixed names — update both if the format changes. -- **ACTION DUE 2026-09-01: bump Sonnet 5 to standard pricing.** `claude-sonnet-5` currently ships introductory pricing ($2/M in, $10/M out, `sonnet5Cost`, multiplier 2×) which expires 2026-08-31. From September 1: switch it to `sonnetCost` ($3/$15), multiplier 3×, update the README model table + pricing paragraph and the `test-config-models.ts` assertions (name suffix becomes `(3×)`, cost fields change). The plan is to have an open PR staged with this change and merge it just before Sept 1. +- Sonnet 5 is on **standard pricing** ($3/M in, $15/M out, `sonnetCost`, multiplier 3×) as of 2026-09-01, when its introductory $2/$10 period ended. The `sonnet5Cost` constant is gone; do not reintroduce it, and do not "correct" the 3× suffix back to 2× from an older README or screenshot. - **Costs in `src/models.ts` are dollars per MILLION tokens**, the unit opencode and models.dev use (`~/.cache/opencode/models.json` has `claude-haiku-4-5 -> {"input": 1, ...}`); opencode divides by 1e6 itself. They were per-token until @CNQQC's PR #25 (merged 2026-08-19), which made every reported session cost 1,000,000x too low — do not "restore" the `1e-6` form. `opusCost` is the real Opus 4.5+ standard price ($5/M in, $25/M out — corrected from a stale legacy $15/$75; Opus 5 keeps it). Haiku ($1/$5), Sonnet ($3/$15), and Fable/Mythos ($10/$50) were already correct. If you add a model, set its cost from the published standard (not Fast Mode) pricing so the `(N×)` suffix stays consistent. **Every entry now carries its published `limit`**, audited against the Anthropic models + pricing docs on 2026-07-26 (the placeholder `output: 16_384` is gone; do not reintroduce it). Two classes of drift were corrected: `claude-sonnet-4-5` and `claude-opus-4-5` claimed a **1M context they never had** — the whole 4.5 generation (including Haiku 4.5) is **200k context / 64k output** — while every 4.6-and-later entry is **1M / 128k**. Release dates for the three dated IDs were also wrong and now match the snapshot suffix (haiku `2025-10-01`, sonnet-4-5 `2025-09-29`, opus-4-5 `2025-11-01`). `test-config-models.ts` pins all eleven limits, so a regression fails the suite rather than silently misreporting the context gauge. - **No long-context pricing tier exists — do not add one.** Investigated for issue #24 on 2026-07-26: Anthropic's pricing page has a "Long context pricing" section stating that Claude 4.6 and later include the full 1M window **at standard pricing** ("a 900k-token request is billed at the same per-token rate as a 9k-token request"), with caching and batch discounts unchanged across it. opencode 1.18.5's optional `cost.tiers` / `cost.experimentalOver200K` fields therefore stay unset — populating them would misreport the real price. The premiums that *do* exist are out of scope here: Fast Mode ($10/$50 on Opus 5/4.8, and this plugin never sends `speed: "fast"`), `inference_geo: "us"` (1.1×, not a CLI flag we pass), and partner-cloud regional endpoints (10%, not our path). Re-open only if Anthropic publishes an above-200K rate. A comment above the cost constants in `src/models.ts` records the same finding. - Billing context (researched 2026-06-10, documented in README "Billing change: June 15, 2026"): from 2026-06-15 Anthropic bills `claude -p` / Agent SDK usage (the plugin's default headless `--print` path) against a separate monthly Agent SDK credit on subscription plans (Pro $20 / Max 5x $100 / Max 20x $200), not normal plan limits; API-key auth is unaffected. Same day, `claude-sonnet-4-20250514` / `claude-opus-4-20250514` retire (not registered here, but pass-through overrides could hit them). Fable 5 is included free on plans only through 2026-06-22; after that it needs usage credits. Confirmed failure mechanism: the 400 `Third-party apps now draw from your extra usage...` corresponds to a `rate_limit_event` with `{rateLimitType:"five_hour", overageStatus:"rejected", overageDisabledReason:"org_level_disabled"}` under OAuth subscription auth, so org-level overage/extra usage being disabled can reject requests that do not fit the remaining rolling window. URL redaction was tested and reverted; the opencode repo URL is not the trigger. Interactive mitigation: live bisection showed this plugin's own CLI/AGENTS/continuation prompt succeeds, while opencode's forwarded system prompt payload can trip the usage gate on constrained subscription accounts. Interactive mode therefore intentionally omits the forwarded opencode system prompt by default. Real account-side fixes remain: enable overage/add extra usage, wait for the 5-hour window reset, switch account/org/plan, or use API-key auth. diff --git a/README.md b/README.md index 6540ee4..fcea544 100644 --- a/README.md +++ b/README.md @@ -73,7 +73,7 @@ The plugin auto-registers the following. They appear in the model picker without | `claude-haiku-4-5` | Claude Haiku 4.5 | 200k | 64,000 | – | 1× | | `claude-sonnet-4-5` | Claude Sonnet 4.5 | 200k | 64,000 | low/medium/high/xhigh/max | 3× | | `claude-sonnet-4-6` | Claude Sonnet 4.6 | 1M | 128,000 | low/medium/high/xhigh/max | 3× | -| `claude-sonnet-5` | Claude Sonnet 5 | 1M | 128,000 | low/medium/high/xhigh/max | 2×* | +| `claude-sonnet-5` | Claude Sonnet 5 | 1M | 128,000 | low/medium/high/xhigh/max | 3× | | `claude-opus-4-5` | Claude Opus 4.5 | 200k | 64,000 | low/medium/high/xhigh/max | 5× | | `claude-opus-4-6` | Claude Opus 4.6 | 1M | 128,000 | low/medium/high/xhigh/max | 5× | | `claude-opus-4-7` | Claude Opus 4.7 | 1M | 128,000 | low/medium/high/xhigh/max | 5× | @@ -86,7 +86,7 @@ The plugin auto-registers the following. They appear in the model picker without Capabilities for every model: text + image input, text output, tool use, attachments. No temperature control, no PDF/audio/video, no interleaved streaming. -**Price ×** is each model's per-token list price relative to Haiku, the cheapest model. It's derived exactly from Anthropic's published pricing — input and output ratios both come out the same (Haiku $1/$5 = 1×, Sonnet $3/$15 = 3×, Opus $5/$25 = 5×, Fable 5 / Mythos 5 $10/$50 = 10×), so **Fable 5 and Mythos 5 cost 2× Opus 5**. Sonnet 5's `2×` uses its introductory $2/$10 pricing through August 31, 2026; standard $3/$15 pricing begins September 1. The same multiplier is shown as a `(N×)` suffix on the display name in opencode's model picker, since opencode has no dedicated multiplier field. On a flat Max/Pro subscription it doubles as a rough guide to how fast each model drains your usage limit. +**Price ×** is each model's per-token list price relative to Haiku, the cheapest model. It's derived exactly from Anthropic's published pricing — input and output ratios both come out the same (Haiku $1/$5 = 1×, Sonnet $3/$15 = 3×, Opus $5/$25 = 5×, Fable 5 / Mythos 5 $10/$50 = 10×), so **Fable 5 and Mythos 5 cost 2× Opus 5**. The same multiplier is shown as a `(N×)` suffix on the display name in opencode's model picker, since opencode has no dedicated multiplier field. On a flat Max/Pro subscription it doubles as a rough guide to how fast each model drains your usage limit. The model ID is passed straight through to `claude --model`, so anything Claude Code accepts works. diff --git a/src/models.ts b/src/models.ts index 687cd9a..e9bdfd4 100644 --- a/src/models.ts +++ b/src/models.ts @@ -31,8 +31,7 @@ function defineModel(opts: { releaseDate: string // List-price multiplier relative to Haiku (the cheapest model). Derived // exactly from published per-token pricing: input AND output ratios both come - // out to haiku 1, sonnet 3, opus 5, fable/mythos 10. Sonnet 5 is temporarily - // 2x during its launch-price period through August 31, 2026. Rendered as an + // out to haiku 1, sonnet 3, opus 5, fable/mythos 10. Rendered as an // `(N×)` suffix so it surfaces in opencode's model picker, which has no // dedicated multiplier field. // Display-only: model resolution keys off `id`. @@ -78,9 +77,6 @@ function defineModel(opts: { // one. Verified against the pricing docs 2026-07-26. const haikuCost = { input: 1, output: 5, cacheRead: 0.1, cacheWrite: 1.25 } const sonnetCost = { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 } -// Introductory pricing through August 31, 2026. Standard pricing from September -// 1 is the same $3/M input and $15/M output as the other Sonnet models. -const sonnet5Cost = { input: 2, output: 10, cacheRead: 0.2, cacheWrite: 2.5 } // Opus 4.5+ standard pricing is $5/M in, $25/M out (the price cut at 4.5; held // through 4.6/4.7/4.8/5). Cache read 0.1x input, cache write 1.25x input. const opusCost = { input: 5, output: 25, cacheRead: 0.5, cacheWrite: 6.25 } @@ -172,8 +168,8 @@ export const defaultModels: Record = { reasoning: true, context: 1_000_000, output: 128_000, - cost: sonnet5Cost, - multiplier: 2, + cost: sonnetCost, + multiplier: 3, releaseDate: "2026-06-30", }), "claude-opus-4-5": defineModel({ diff --git a/test-config-models.ts b/test-config-models.ts index f80b171..a56746b 100644 --- a/test-config-models.ts +++ b/test-config-models.ts @@ -82,17 +82,17 @@ test("configModelsForProvider registers Sonnet 5 and Opus 5 metadata", () => { const models = configModelsForProvider({}, "claude-code") const sonnet = models["claude-sonnet-5"] as Record - assert.equal(sonnet.name, "Claude Sonnet 5 (2×)") + assert.equal(sonnet.name, "Claude Sonnet 5 (3×)") assert.equal(sonnet.family, "sonnet") assert.equal(sonnet.release_date, "2026-06-30") assert.equal(sonnet.reasoning, true) assert.deepEqual(sonnet.limit, { context: 1_000_000, output: 128_000 }) // Dollars per million tokens, the unit opencode/models.dev expect. assert.deepEqual(sonnet.cost, { - input: 2, - output: 10, - cache_read: 0.2, - cache_write: 2.5, + input: 3, + output: 15, + cache_read: 0.3, + cache_write: 3.75, }) const opus = models["claude-opus-5"] as Record