Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions @omniroute/opencode-provider/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ Exported for completeness. Strips trailing `/`, deduplicates a trailing `/v1`, a
- `OMNIROUTE_PROVIDER_KEY` — `"omniroute"` (the key used under `provider.*`).
- `OMNIROUTE_PROVIDER_NPM` — `"@ai-sdk/openai-compatible"` (the runtime delegate).
- `OPENCODE_CONFIG_SCHEMA` — `"https://opencode.ai/config.json"`.
- `OMNIROUTE_DEFAULT_OPENCODE_MODELS` — readonly list of 4 default model ids.
- `OMNIROUTE_DEFAULT_OPENCODE_MODELS` — readonly list of default model ids.

## Custom model catalog

Expand All @@ -110,10 +110,10 @@ import { createOmniRouteProvider } from "@omniroute/opencode-provider";
createOmniRouteProvider({
baseURL: "http://localhost:20128",
apiKey: "sk_omniroute",
models: ["auto", "claude-opus-4-7", "gpt-5.5"],
models: ["auto", "claude-opus-4-8", "gpt-5.5"],
modelLabels: {
auto: "Auto-Combo (recommended)",
"claude-opus-4-7": "Claude Opus 4.7",
"claude-opus-4-8": "Claude Opus 4.8",
"gpt-5.5": "GPT-5.5",
},
});
Expand Down
5 changes: 4 additions & 1 deletion @omniroute/opencode-provider/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export const OPENCODE_CONFIG_SCHEMA = "https://opencode.ai/config.json" as const
* with Claude Code passthrough models (`cc/` prefix).
*/
export const OMNIROUTE_DEFAULT_OPENCODE_MODELS = [
"cc/claude-opus-4-8",
"cc/claude-opus-4-7",
"cc/claude-sonnet-4-6",
"cc/claude-haiku-4-5-20251001",
Expand Down Expand Up @@ -83,7 +84,8 @@ export interface ModelCapabilities {
* Matches the context lengths used by OmniRoute's provider registry.
*/
export const OMNIROUTE_DEFAULT_MODEL_CONTEXT_LENGTHS: Record<string, number> = {
"cc/claude-opus-4-7": 200_000,
"cc/claude-opus-4-8": 1_000_000,
"cc/claude-opus-4-7": 1_000_000,
"cc/claude-sonnet-4-6": 200_000,
"cc/claude-haiku-4-5-20251001": 200_000,
"claude-opus-4-5-thinking": 200_000,
Expand All @@ -100,6 +102,7 @@ export const OMNIROUTE_DEFAULT_MODEL_CONTEXT_LENGTHS: Record<string, number> = {
* model via `OmniRouteProviderOptions.modelCapabilities`.
*/
export const OMNIROUTE_DEFAULT_MODEL_CAPABILITIES: Record<string, ModelCapabilities> = {
"cc/claude-opus-4-8": { attachment: true, reasoning: true, temperature: true, tool_call: true },
"cc/claude-opus-4-7": { attachment: true, reasoning: true, temperature: true, tool_call: true },
"cc/claude-sonnet-4-6": { attachment: true, reasoning: true, temperature: true, tool_call: true },
"cc/claude-haiku-4-5-20251001": { attachment: true, temperature: true, tool_call: true },
Expand Down
10 changes: 6 additions & 4 deletions @omniroute/opencode-provider/tests/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,7 @@ test("createOmniRouteComboConfig includes optional fields when supplied", () =>

test("OMNIROUTE_DEFAULT_OPENCODE_MODELS includes cc/ prefixed models", () => {
const defaults = [...OMNIROUTE_DEFAULT_OPENCODE_MODELS];
assert.ok(defaults.includes("cc/claude-opus-4-8"));
assert.ok(
defaults.some((m) => m.startsWith("cc/")),
"should have cc/ prefixed models"
Expand All @@ -413,9 +414,10 @@ test("createOmniRouteProvider emits limit.context on default model entries", ()
baseURL: "http://localhost:20128",
apiKey: "sk_omniroute",
});
const entry = provider.models["cc/claude-opus-4-7"];
const entry = provider.models["cc/claude-opus-4-8"];
assert.ok(entry.limit, "model entry should have a limit field");
assert.equal(entry.limit!.context, 200_000);
assert.equal(entry.limit!.context, 1_000_000);
assert.equal(provider.models["cc/claude-opus-4-7"].limit!.context, 1_000_000);
});

test("createOmniRouteProvider omits limit.context for unknown model ids", () => {
Expand Down Expand Up @@ -480,8 +482,8 @@ test("createOmniRouteProvider emits default capability flags inline with the mod
baseURL: "http://localhost:20128",
apiKey: "sk_omniroute",
});
const entry = provider.models["cc/claude-opus-4-7"];
assert.equal(entry.name, "cc/claude-opus-4-7");
const entry = provider.models["cc/claude-opus-4-8"];
assert.equal(entry.name, "cc/claude-opus-4-8");
assert.equal(entry.attachment, true);
assert.equal(entry.reasoning, true);
assert.equal(entry.temperature, true);
Expand Down
4 changes: 2 additions & 2 deletions docs/guides/USER_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -578,7 +578,7 @@ For the full environment variable reference, see the [README](../README.md).

> The list below is curated from `open-sse/config/providerRegistry.ts` for v3.8.0. Cloud catalogs (Gemini, OpenRouter, etc.) are synced dynamically — for the full live catalog open **Dashboard → Providers → [provider] → Available Models** or call `GET /api/models/catalog`.

**Claude Code (`cc/`)** — Pro/Max OAuth: `cc/claude-opus-4-7`, `cc/claude-opus-4-6`, `cc/claude-opus-4-5-20251101`, `cc/claude-sonnet-4-6`, `cc/claude-sonnet-4-5-20250929`, `cc/claude-haiku-4-5-20251001`
**Claude Code (`cc/`)** — Pro/Max OAuth: `cc/claude-opus-4-8`, `cc/claude-opus-4-7`, `cc/claude-opus-4-6`, `cc/claude-opus-4-5-20251101`, `cc/claude-sonnet-4-6`, `cc/claude-sonnet-4-5-20250929`, `cc/claude-haiku-4-5-20251001`

**Codex (`cx/`)** — Plus/Pro OAuth: `cx/gpt-5.5` (+ effort tiers: `gpt-5.5-xhigh`, `gpt-5.5-high`, `gpt-5.5-medium`, `gpt-5.5-low`), `cx/gpt-5.4`, `cx/gpt-5.4-mini`, `cx/gpt-5.3-codex`, `cx/gpt-5.3-codex-spark`, `cx/gpt-5.2`

Expand Down Expand Up @@ -626,7 +626,7 @@ For the full environment variable reference, see the [README](../README.md).

**Other compatible providers** (selected): `cohere`, `databricks`, `snowflake`, `together`, `vertex`, `alibaba`, `alibaba-cn`, `bedrock` (via `aws-bedrock`), `azure-ai`, `openrouter` (passthrough catalog), `siliconflow`, `hyperbolic`, `huggingface`, `featherless-ai`, `cloudflare-ai`, `scaleway`, `deepinfra`, `vercel-ai-gateway`, `bazaarlink`, `friendliai`, `nous-research`, `reka`, `volcengine`, `ai21`, `gigachat`. Each maintains its own model list in `providerRegistry.ts` and can be auto-synced when the provider exposes a `/models` endpoint.

**Note on model IDs:** OmniRoute uses provider-native IDs (`claude-opus-4-7`, `gpt-5.5`, `glm-5.1`, `MiniMax-M2.7`, `kimi-k2.5`, `grok-4.20-0309-reasoning`). Some IDs include dotted versions because that is how the upstream API expects them. If a model is not listed above, run `omniroute models --search <term>` or hit `GET /api/models/catalog` to confirm availability.
**Note on model IDs:** OmniRoute uses provider-native IDs (`claude-opus-4-8`, `gpt-5.5`, `glm-5.1`, `MiniMax-M2.7`, `kimi-k2.5`, `grok-4.20-0309-reasoning`). Some IDs include dotted versions because that is how the upstream API expects them. If a model is not listed above, run `omniroute models --search <term>` or hit `GET /api/models/catalog` to confirm availability.

</details>

Expand Down
11 changes: 10 additions & 1 deletion open-sse/config/providerModels.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,5 +64,14 @@ export function supportsXHighEffort(aliasOrId: string, modelId: string): boolean
const providerModels = PROVIDER_MODELS[alias] || PROVIDER_MODELS[aliasOrId];
// Unknown provider (not in registry) — pass through unchanged.
if (!providerModels) return true;
return getProviderModel(alias, modelId)?.supportsXHighEffort === true;
const model = getProviderModel(alias, modelId);

// Claude Code models default to supporting extra-high effort. Keep explicit
// false entries as the unsupported-model list so newly added Claude models do
// not need opt-in flags.
if (alias === "cc") {
return model?.supportsXHighEffort !== false;
}

return model?.supportsXHighEffort === true;
}
7 changes: 6 additions & 1 deletion open-sse/config/providerRegistry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -634,10 +634,15 @@ export const REGISTRY: Record<string, RegistryEntry> = {
tokenUrl: "https://console.anthropic.com/v1/oauth/token",
},
models: [
{
id: "claude-opus-4-8",
name: "Claude Opus 4.8",
contextLength: 1000000,
maxOutputTokens: 128000,
},
{
id: "claude-opus-4-7",
name: "Claude Opus 4.7",
supportsXHighEffort: true,
contextLength: 1000000,
maxOutputTokens: 128000,
},
Expand Down
4 changes: 2 additions & 2 deletions open-sse/executors/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -217,8 +217,8 @@ function hasActiveClaudeThinking(body: Record<string, unknown>): boolean {
* Each rejection burns a combo fallback attempt before reaching a working
* provider. Apply provider-aware sanitation here (after transformRequest, so
* reintroductions by per-provider transforms are also caught) before fetch.
* Models that genuinely support xhigh (registry flag supportsXHighEffort)
* pass through unchanged.
* Models that genuinely support xhigh pass through unchanged. Claude models
* default to xhigh support unless they are marked as legacy unsupported entries.
*/
const MISTRAL_NO_REASONING_EFFORT_PATTERN = /devstral/i;
const GITHUB_NO_REASONING_EFFORT_PATTERN = /(claude|haiku|oswe)/i;
Expand Down
2 changes: 1 addition & 1 deletion open-sse/services/claudeCodeCompatible.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ const CLAUDE_CODE_COMPATIBLE_DEFAULT_SYSTEM_BLOCKS = [
text: "You are a Claude agent, built on Anthropic's Claude Agent SDK.",
},
];
const CONTEXT_1M_SUPPORTED_MODELS = ["claude-opus-4-7", "claude-opus-4-6"];
const CONTEXT_1M_SUPPORTED_MODELS = ["claude-opus-4-8", "claude-opus-4-7", "claude-opus-4-6"];
export const CLAUDE_CODE_COMPATIBLE_STAINLESS_TIMEOUT_SECONDS = getStainlessTimeoutSeconds(
process.env
);
Expand Down
1 change: 1 addition & 0 deletions open-sse/services/modelFamilyFallback.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ const MODEL_FAMILIES: Record<string, string[]> = {
"gemini-2.5-pro-preview-06-05": ["gemini-2.5-pro", "gemini-2.5-pro-exp-03-25"],

// Claude Opus family
"claude-opus-4-8": ["claude-opus-4-7", "claude-opus-4-6", "claude-sonnet-4-6"],
"claude-opus-4-7": ["claude-opus-4-6", "claude-opus-4-5-20251101", "claude-sonnet-4-6"],
"claude-opus-4-6": ["claude-opus-4-6-thinking", "claude-opus-4-5-20251101", "claude-sonnet-4-6"],
"claude-opus-4-6-thinking": ["claude-opus-4-6", "claude-opus-4-5-20251101"],
Expand Down
1 change: 1 addition & 0 deletions open-sse/services/providerCostData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export interface ModelPricing {
export const KNOWN_MODEL_PRICING: Record<string, ModelPricing> = {
"gpt-4o": { inputCostPer1M: 2.5, outputCostPer1M: 10.0, isFree: false },
"gpt-4o-mini": { inputCostPer1M: 0.15, outputCostPer1M: 0.6, isFree: false },
"claude-opus-4-8": { inputCostPer1M: 15.0, outputCostPer1M: 75.0, isFree: false },
"claude-opus-4-7": { inputCostPer1M: 15.0, outputCostPer1M: 75.0, isFree: false },
"claude-sonnet-4-6": { inputCostPer1M: 3.0, outputCostPer1M: 15.0, isFree: false },
"claude-haiku-4-5": { inputCostPer1M: 0.8, outputCostPer1M: 4.0, isFree: false },
Expand Down
5 changes: 4 additions & 1 deletion src/lib/db/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,10 @@ export async function getSettings() {
hideEndpointNgrokTunnel: false,
comboConfigMode: "guided",
codexServiceTier: { enabled: false },
claudeFastMode: { enabled: false, supportedModels: ["claude-opus-4-7", "claude-opus-4-6"] },
claudeFastMode: {
enabled: false,
supportedModels: ["claude-opus-4-8", "claude-opus-4-7", "claude-opus-4-6"],
},
codexSessionAffinityTtlMs: 0,
alwaysPreserveClientCache: "auto",
idempotencyWindowMs: 5000,
Expand Down
10 changes: 7 additions & 3 deletions src/lib/providers/claudeFastMode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@ type JsonRecord = Record<string, unknown>;
* Mirrors the binary-side gate observed in claude-code v2.1.145 (KT() check):
* only the latest Opus tiers can request the priority service path.
*/
export const CLAUDE_FAST_MODE_DEFAULT_MODELS = ["claude-opus-4-7", "claude-opus-4-6"] as const;
export const CLAUDE_FAST_MODE_DEFAULT_MODELS = [
"claude-opus-4-8",
"claude-opus-4-7",
"claude-opus-4-6",
] as const;

function asRecord(value: unknown): JsonRecord {
return value && typeof value === "object" && !Array.isArray(value) ? (value as JsonRecord) : {};
Expand Down Expand Up @@ -39,7 +43,7 @@ export function isClaudeFastModeEnabled(settings: unknown): boolean {

/**
* Returns the configured supported-model list, defaulting to the conservative
* Opus 4-7 / 4-6 pair (matches the binary KT() gate).
* Opus 4-8 / 4-7 / 4-6 set.
*/
export function getClaudeFastModeSupportedModels(settings: unknown): string[] {
const record = asRecord(settings);
Expand All @@ -51,7 +55,7 @@ export function getClaudeFastModeSupportedModels(settings: unknown): string[] {

/**
* True when the toggle is on AND the model id prefix-matches a supported model.
* Prefix matching tolerates dated suffixes (e.g. claude-opus-4-7-20260201).
* Prefix matching tolerates dated suffixes (e.g. claude-opus-4-8-20260528).
*/
export function shouldRequestClaudeFastMode(
settings: unknown,
Expand Down
1 change: 1 addition & 0 deletions src/lib/providers/staticModels.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ const STATIC_MODEL_PROVIDERS: Record<string, () => Array<{ id: string; name: str
],
antigravity: () => ANTIGRAVITY_PUBLIC_MODELS.map((model) => ({ ...model })),
claude: () => [
{ id: "claude-opus-4-8", name: "Claude Opus 4.8" },
{ id: "claude-opus-4-7", name: "Claude Opus 4.7" },
{ id: "claude-opus-4-6", name: "Claude Opus 4.6" },
{ id: "claude-sonnet-4-6", name: "Claude Sonnet 4.6" },
Expand Down
14 changes: 14 additions & 0 deletions src/shared/constants/modelSpecs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,20 @@ export const MODEL_SPECS: Record<string, ModelSpec> = {
aliases: BEDROCK_CLAUDE_ALIASES("claude-opus-4-7", "claude-opus-4.7"),
},

// ── Claude Opus 4.8 ─────────────────────────────────────────────
"claude-opus-4-8": {
maxOutputTokens: 128000,
contextWindow: 1000000,
// Opus 4.8 inherits Opus 4.7's adaptive thinking constraints: no fixed
// thinking budget requests, with effort controlled by output_config.
defaultThinkingBudget: 32000,
thinkingBudgetCap: 120000,
supportsThinking: true,
supportsTools: true,
supportsVision: true,
aliases: BEDROCK_CLAUDE_ALIASES("claude-opus-4-8", "claude-opus-4.8"),
},

// ── Claude Sonnet 4.5 ───────────────────────────────────────────
"claude-sonnet-4-5-20250929": {
maxOutputTokens: 64000,
Expand Down
10 changes: 10 additions & 0 deletions src/shared/constants/pricing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,13 @@ export const DEFAULT_PRICING = {

// Claude Code (cc)
cc: {
"claude-opus-4-8": {
input: 5.0,
output: 25.0,
cached: 2.5,
reasoning: 25.0,
cache_creation: 5.0,
},
"claude-opus-4-7": {
input: 5.0,
output: 25.0,
Expand Down Expand Up @@ -710,6 +717,9 @@ export const DEFAULT_PRICING = {
// Common model IDs (without dates) used across providers
// Intentional duplicates of dot-notation variants (e.g. claude-opus-4.6)
// to cover hyphen-notation IDs (claude-opus-4-6) used by some clients
"claude-opus-4.8": CLAUDE_OPUS_4_PRICING,
"claude-opus-4-8": CLAUDE_OPUS_4_PRICING,

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.

medium

The hyphen-notation mapping for claude-opus-4-7 is missing from the common model IDs list under the anthropic provider pricing. Without this mapping, clients requesting claude-opus-4-7 (using hyphens instead of dots) may fail to resolve the correct pricing tier, resulting in inaccurate cost tracking or fallback errors.

Adding "claude-opus-4-7": CLAUDE_OPUS_4_PRICING, ensures complete coverage for hyphen-notation requests.

Suggested change
"claude-opus-4-8": CLAUDE_OPUS_4_PRICING,
"claude-opus-4-8": CLAUDE_OPUS_4_PRICING,
"claude-opus-4-7": CLAUDE_OPUS_4_PRICING,

"claude-opus-4-7": CLAUDE_OPUS_4_PRICING,
"claude-opus-4-6": CLAUDE_OPUS_46_PRICING,
"claude-sonnet-4-6": CLAUDE_SONNET_46_PRICING,
"claude-opus-4-5-20251101": CLAUDE_OPUS_4_PRICING,
Expand Down
4 changes: 2 additions & 2 deletions tests/unit/base-executor-sanitize-effort.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,8 @@ test("sanitizeReasoningEffortForProvider: mistral/devstral preserves reasoning w
});

test("sanitizeReasoningEffortForProvider: codex with xhigh passes through unchanged when model supports it", () => {
// codex/gpt-5.5-xhigh and related Claude opus models are flagged
// supportsXHighEffort:true in providerRegistry. They must not be downgraded.
// codex/gpt-5.5-xhigh is flagged supportsXHighEffort:true in providerRegistry.
// Claude Opus 4.7+ models default to xhigh support unless explicitly opted out.
const body = {
model: "gpt-5.5-xhigh",
reasoning_effort: "xhigh",
Expand Down
7 changes: 4 additions & 3 deletions tests/unit/cc-compatible-provider.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ const {
CLAUDE_CODE_COMPATIBLE_DEFAULT_MODELS_PATH,
joinClaudeCodeCompatibleUrl,
} = await import("../../open-sse/services/claudeCodeCompatible.ts");
const { getModelsByProviderId } = await import("../../open-sse/config/providerModels.ts");
const { getModelsByProviderId, supportsXHighEffort } =
await import("../../open-sse/config/providerModels.ts");
const { handleChatCore } = await import("../../open-sse/handlers/chatCore.ts");
const { validateProviderApiKey } = await import("../../src/lib/providers/validation.ts");
const providerNodesRoute = await import("../../src/app/api/provider-nodes/route.ts");
Expand Down Expand Up @@ -144,8 +145,8 @@ test("buildClaudeCodeCompatibleRequest keeps prior role history while dropping t
});

test("buildClaudeCodeCompatibleRequest preserves xhigh for Claude models that support it", () => {
const xhighModel = getModelsByProviderId("claude").find(
(model) => model.supportsXHighEffort === true
const xhighModel = getModelsByProviderId("claude").find((model) =>
supportsXHighEffort("claude", model.id)
);
assert.ok(xhighModel, "expected at least one Claude model with xhigh support");
const payload = buildClaudeCodeCompatibleRequest({
Expand Down
9 changes: 6 additions & 3 deletions tests/unit/claude-code-compatible-request.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,15 @@ const {
resolveClaudeCodeCompatibleMaxTokens,
buildClaudeCodeCompatibleRequest,
} = await import("../../open-sse/services/claudeCodeCompatible.ts");
const { getModelsByProviderId } = await import("../../open-sse/config/providerModels.ts");
const { getModelsByProviderId, supportsXHighEffort } =
await import("../../open-sse/config/providerModels.ts");

function getClaudeEffortFixtures() {
const claudeModels = getModelsByProviderId("claude");
const xhighModel = claudeModels.find((model) => model.supportsXHighEffort === true);
const standardModel = claudeModels.find((model) => model.supportsXHighEffort === false);
const xhighModel = claudeModels.find((model) => supportsXHighEffort("claude", model.id));
const standardModel = claudeModels.find(
(model) => supportsXHighEffort("claude", model.id) === false
);
assert.ok(xhighModel, "expected at least one Claude model with xhigh support");
assert.ok(standardModel, "expected at least one Claude model without xhigh support");
return { xhighModel, standardModel };
Expand Down
Loading