Skip to content

Commit ea9407e

Browse files
committed
feat(ai): offer Claude Opus 5 on OpenRouter (BYOK) + 1M-context caps
Gateway (LevelCode Cloud) users get Opus 5 from the server roster with no editor release — that side is thin.ly. This is the BYOK half: the OpenRouter picker, the local caps table, and the sketch cost meter. - providers/index.js — a picker row for `anthropic/claude-opus-5`. Placed second, under the flagship, since it is the frontier pick most people reach for. - providers/catalog.js — an explicit CAPS row, for the same reason Kimi K3 has one: the `claude-` heuristic reports 200K, so a 1M window would drive the in-run context meter to cry "full" at a fifth of the real budget. - sketch/pricing.js — Opus 5 ties Opus 4.8's $5/$25, which the `claude-opus` family fallback already produced. The FAST variant does NOT: it is $10/$50, and the fallback was quietly pricing it at half. Both are pinned now, so the meter is honest whichever slug a BYOK user types. - sketch.js — MODEL_TIERS openrouter.powerful moves 4.8 → 5. Same price, newer model. It also fixes a latent wart: the old id was never in the OpenRouter registry, so a per-node model override naming it failed validation and silently fell back. Revert this one line if you'd rather pin the tier to 4.8. Prices/context confirmed against the OpenRouter models API (2026-07-24): $5/M in · $25/M out · $0.50/M cached · 1M ctx (fast variant: $10/$50). Verified at runtime, not just in tests: the row renders in the picker, caps resolve to 1,000,000, tools/vision on, and isAnthropicFamily matches so explicit prompt-cache breakpoints are sent. Full gate green (24 suites).
1 parent 7ba72f1 commit ea9407e

7 files changed

Lines changed: 24 additions & 3 deletions

File tree

extensions/levelcode-ai/providers/catalog.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,11 @@ const CAPS = {
5757
// warns "full" 5× too early (the roster feeds the PICKER's ctx, but the meter reads this local CAPS).
5858
// No `fast` (a 2.8T reasoning model is a poor ghost-text completer). tools:true matches its agentic
5959
// intent; flip to false ONLY if the reasoning-loop spike (docs/KIMI-K3.md §3) shows it breaks the loop.
60-
'moonshotai/kimi-k3': { context: 1048576, tools: true, reasoning: true }
60+
'moonshotai/kimi-k3': { context: 1048576, tools: true, reasoning: true },
61+
// Same reason as K3: a 1M window that the `claude-` heuristic would otherwise report as 200K,
62+
// making the in-run context meter cry "full" 5× too early. Listed under its OpenRouter slug
63+
// because that is how it is reached (BYOK here, and the gateway roster uses the same id).
64+
'anthropic/claude-opus-5': { context: 1000000, tools: true, vision: true, caching: true }
6165
};
6266

6367
/** The basename of a model id ('openai/gpt-4o' → 'gpt-4o', 'gpt-4o' → 'gpt-4o'). */

extensions/levelcode-ai/providers/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ const PROVIDERS = {
4545
caps: { tools: true, vision: true }, headers: { 'HTTP-Referer': 'https://levelcode.ai', 'X-Title': 'LevelCode' },
4646
models: [
4747
{ id: 'moonshotai/kimi-k2.7-code', label: 'Kimi K2.7 Code', detail: 'Moonshot · coding, 262K · via OpenRouter' },
48+
{ id: 'anthropic/claude-opus-5', label: 'Claude Opus 5', detail: 'Anthropic · frontier, 1M · via OpenRouter' },
4849
{ id: 'anthropic/claude-sonnet-4-6', label: 'Claude Sonnet 4.6', detail: 'via OpenRouter' },
4950
{ id: 'openai/gpt-4o', label: 'GPT-4o', detail: 'via OpenRouter' },
5051
{ id: 'google/gemini-2.0-flash-001', label: 'Gemini 2.0 Flash', detail: 'via OpenRouter' },

extensions/levelcode-ai/sketch.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,10 @@ const MODEL_TIERS = {
4141
openrouter: {
4242
fast: "google/gemini-2.0-flash-001",
4343
balanced: "anthropic/claude-sonnet-4-6",
44-
powerful: "anthropic/claude-opus-4-8",
44+
// Opus 5 costs exactly what 4.8 did ($5/$25), so "powerful" gets the newer model for the same
45+
// money. It is also in the OpenRouter registry now, which the old id never was — so a per-node
46+
// model override naming it now validates instead of silently falling back to the tier default.
47+
powerful: "anthropic/claude-opus-5",
4548
},
4649
groq: {
4750
fast: "llama-3.1-8b-instant",

extensions/levelcode-ai/sketch/pricing.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@ const { baseName } = require('../providers/catalog');
1919
const PRICING = {
2020
// Anthropic (published)
2121
'claude-opus-4-8': { inM: 5.00, outM: 25.00 },
22+
// Opus 5 ties 4.8's sheet, so the `claude-opus` family fallback below already gets it right — but
23+
// the FAST variant is double ($10/$50), and that fallback would silently under-report it by 2×.
24+
// Pinning both keeps the cost meter honest for whichever slug a BYOK user types.
25+
'claude-opus-5': { inM: 5.00, outM: 25.00 },
26+
'claude-opus-5-fast': { inM: 10.00, outM: 50.00 },
2227
'claude-sonnet-4-6': { inM: 3.00, outM: 15.00 },
2328
'claude-haiku-4-5': { inM: 1.00, outM: 5.00 },
2429
'claude-haiku-4-5-20251001': { inM: 1.00, outM: 5.00 },

extensions/levelcode-ai/test/catalog.test.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,10 @@ test('contextWindowFor: known model wins; fallback then 200000 when unknown', ()
6161
assert.strictEqual(C.contextWindowFor('openai', 'unknown-x'), 200000);
6262
// Gateway Kimi K3 — its 1M window must beat the 200000 fallback, or the meter warns 5× too early.
6363
assert.strictEqual(C.contextWindowFor('openai', 'moonshotai/kimi-k3', 200000), 1048576);
64+
// Same trap for Opus 5: the `claude-` heuristic would hand back 200K and shrink a 1M window.
65+
assert.strictEqual(C.contextWindowFor('openai', 'anthropic/claude-opus-5', 200000), 1000000);
66+
// …and the heuristic is still what covers Claude ids we DON'T list (200K is the right default).
67+
assert.strictEqual(C.contextWindowFor('openai', 'anthropic/claude-opus-4-8', 200000), 200000);
6468
});
6569

6670
// ---- fastCompletionModel ----

extensions/levelcode-ai/test/promptCaching.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ test('splitOutCachedTokens: zero cached_tokens does not subtract', () => {
9494
// ---- Anthropic-family gating for OpenRouter explicit caching ----
9595

9696
test('isAnthropicFamily: identifies Claude upstreams on OpenRouter, IDs only Anthropic routes', () => {
97-
for (const id of ['claude-opus-4-8', 'anthropic/claude-sonnet-4-6', 'claude-sonnet-4-6']) {
97+
for (const id of ['claude-opus-4-8', 'anthropic/claude-sonnet-4-6', 'claude-sonnet-4-6', 'anthropic/claude-opus-5']) {
9898
assert.strictEqual(O.isAnthropicFamily(id), true, id);
9999
}
100100
for (const id of ['openai/gpt-4o', 'deepseek/deepseek-chat', 'gpt-4o', 'moonshotai/kimi-k2.7-code', '']) {

extensions/levelcode-ai/test/sketch.test.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,10 @@ test('modelPricing: exact, basename (openrouter prefix), family fallback, unknow
7272
assert.strictEqual(P.modelPricing('claude-opus-4-8').inM, 5.0);
7373
assert.strictEqual(P.modelPricing('anthropic/claude-sonnet-4-6').outM, 15.0); // basename via vendor/model
7474
assert.strictEqual(P.modelPricing('claude-sonnet-4-6-preview').outM, 15.0); // family prefix
75+
// Opus 5 matches 4.8's sheet; its FAST variant is double. Without the explicit rows the
76+
// `claude-opus` family fallback prices the fast one at half what it costs.
77+
assert.deepStrictEqual(P.modelPricing('anthropic/claude-opus-5'), { inM: 5.0, outM: 25.0 });
78+
assert.deepStrictEqual(P.modelPricing('anthropic/claude-opus-5-fast'), { inM: 10.0, outM: 50.0 });
7579
assert.strictEqual(P.modelPricing('totally-unknown-model'), null);
7680
assert.strictEqual(P.modelPricing(''), null);
7781
});

0 commit comments

Comments
 (0)