From dcab9a451cc79fa9497326b550fd4248f7854654 Mon Sep 17 00:00:00 2001 From: Aditya Vikram Singh <247195684+avs-io@users.noreply.github.com> Date: Sun, 23 Aug 2026 17:59:27 +0530 Subject: [PATCH] Identify OrcaRouter route ids as the routed model, not a branded label --- CHANGELOG.md | 2 +- src/models.ts | 39 +++++++++++++++++++++------------------ tests/models.test.ts | 17 ++++++++++++----- 3 files changed, 34 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d3853f33..d2190248 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,7 @@ - **`CODEBURN_CACHE_SCOPE=all` forces a full session-cache read.** A ranged query reads only the month shards that can contribute a turn to it, which is a real behaviour change on a warm cache; this is the escape hatch for the case where a number looks wrong and you want to know whether the scoped read is why. Set it and every load ignores its scope and reads every shard, one-shot runs and the resident `codeburn serve` alike. It is a read policy, not an input to any cache fingerprint: setting or unsetting it re-parses nothing and invalidates nothing. ### Added (OrcaRouter) -- **OrcaRouter sessions now price and label like the model they route to.** OrcaRouter is a gateway that exposes route ids (`orcarouter/auto`, `orcarouter/fusion`, …) plus plain upstream ids (`deepseek/deepseek-v4-pro`), and reports the upstream id that actually ran in the completion response's `model` field. The `orcarouter/` prefix is now a routing wrapper like `cmd/` / `antigravity/`, so a routed spelling prices at its upstream LiteLLM row instead of $0; the smart route `orcarouter/auto` aliases to a fast Sonnet rate and the fusion routes alias to their current `openai/gpt-oss-120b` target, each keeping a branded route label (like the other `*-auto` presets); and an unknown vendor nested inside the route still fails closed. +- **OrcaRouter sessions now price and label like the model they route to.** OrcaRouter is a gateway that exposes route ids (`orcarouter/auto`, `orcarouter/fusion`, …) plus plain upstream ids (`deepseek/deepseek-v4-pro`), and reports the upstream id that actually ran in the completion response's `model` field. The `orcarouter/` prefix is now a routing wrapper like `cmd/` / `antigravity/`, so a routed spelling prices at its upstream LiteLLM row instead of $0; the smart route `orcarouter/auto` aliases to a fast Sonnet rate and the fusion routes alias to their current `openai/gpt-oss-120b` target, so display and report identity match those routed models; and an unknown vendor nested inside the route still fails closed. ### Added (Windows) - **`codeburn menubar` installs and launches the tray app on Windows.** The same command that installs the macOS menubar now does the Windows one, through the same pinned-release path: it resolves `windows-v`, falls back to a scan of the newest `windows-v*` release carrying both assets when that tag has none, downloads the `.msi` with the same retry and backoff, and verifies its sha256 before anything executes it — a mismatch aborts without ever handing the file to the installer. It then runs `msiexec` out of `%SystemRoot%\System32` (never a bare name, so nothing dropped next to the CLI can impersonate it) with `/i /passive /norestart`, treats exit 3010 as installed-pending-restart and 1602 as a cancelled install rather than failures, and launches the exe named by the product's Uninstall registry key. An already-installed matching version skips the download and just launches; `--force` reinstalls. diff --git a/src/models.ts b/src/models.ts index 6923f94c..c9196aff 100644 --- a/src/models.ts +++ b/src/models.ts @@ -290,8 +290,9 @@ export async function loadPricing(): Promise { // Known model name variants that providers emit but LiteLLM/fallback don't index under. // OMP emits 'anthropic--claude-4.6-opus' (double-dash, dot version, tier-last). -// getCanonicalName strips a KNOWN vendor/router prefix first, so only the -// post-strip forms need to be listed here. +// getCanonicalName strips a KNOWN vendor/router prefix first unless the +// full cleaned id itself is an alias (orcarouter/auto). Post-strip forms +// still cover every other entry here. const BUILTIN_ALIASES: Record = { 'anthropic--claude-4.6-opus': 'claude-opus-4-6', 'anthropic--claude-4.6-sonnet': 'claude-sonnet-4-6', @@ -347,11 +348,12 @@ const BUILTIN_ALIASES: Record = { // OrcaRouter's smart route. The route picks a model per request; at the time // of writing it lands on a Qwen/Llama flash model, so a fast Sonnet rate is // the honest estimate until the route's target stabilizes (mirrors the - // `*-auto` presets above). + // `*-auto` presets above). Display and report identity follow this target. 'orcarouter/auto': 'claude-sonnet-4-5', // OrcaRouter's fusion routes currently serve openai/gpt-oss-120b (verified // live 2026-08). If the gateway re-routes them, these drift like any other - // preset estimate; they keep a priced row instead of reporting $0. + // preset estimate; they keep a priced row instead of reporting $0. Display + // and report identity follow this target, not a branded route label. 'orcarouter/fusion': 'openai/gpt-oss-120b', 'orcarouter/fusion-flash': 'openai/gpt-oss-120b', 'orcarouter/fusion-mini': 'openai/gpt-oss-120b', @@ -785,12 +787,21 @@ function resolveAlias(model: string): string { return model } function getCanonicalName(model: string): string { - return stripKnownFirstNamespace( - model - .replace(/@.*$/, '') - .replace(/-\d{8}$/, '') - .replace(/\[[^\]]*\]$/, ''), - ) + const cleaned = model + .replace(/@.*$/, '') + .replace(/-\d{8}$/, '') + .replace(/\[[^\]]*\]$/, '') + // Full-id aliases (orcarouter/auto) must stay visible so resolveAlias can + // see them. Stripping first would leave a leaf (`auto`) with no mapping. + // Nested wrappers without an alias still peel as before. + if (Object.hasOwn(userAliases, cleaned) || Object.hasOwn(BUILTIN_ALIASES, cleaned)) { + return cleaned + } + const lowercase = cleaned.toLowerCase() + if (lowercase !== cleaned && Object.hasOwn(BUILTIN_ALIASES, lowercase)) { + return cleaned + } + return stripKnownFirstNamespace(cleaned) } /// Alias-resolved identity for report merge. Display names stay cosmetic — @@ -1167,14 +1178,6 @@ const autoModelNames: Record = { 'openclaw-auto': 'OpenClaw (auto)', 'qwen-auto': 'Qwen (auto)', 'kimi-auto': 'Kimi (auto)', - // OrcaRouter route ids. The smart route routes per request and the fusion - // routes resolve to a gateway-picked upstream, so each keeps its branded - // route label rather than the current upstream's display name. Prices are - // aliased in BUILTIN_ALIASES to the route's current target. - 'orcarouter/auto': 'OrcaRouter (auto)', - 'orcarouter/fusion': 'OrcaRouter Fusion', - 'orcarouter/fusion-flash': 'OrcaRouter Fusion Flash', - 'orcarouter/fusion-mini': 'OrcaRouter Fusion Mini', 'codex-auto-review': 'Codex Auto Review', } diff --git a/tests/models.test.ts b/tests/models.test.ts index 3652d16b..b5bc2db0 100644 --- a/tests/models.test.ts +++ b/tests/models.test.ts @@ -218,6 +218,13 @@ describe('resolveCanonicalModelId', () => { expect(resolveCanonicalModelId('claude-opus-4.6')).toBe('claude-opus-4-6') expect(resolveCanonicalModelId('kimi-code')).toBe('kimi-k2-thinking') expect(resolveCanonicalModelId('cline-pass/kimi-k3')).toBe('kimi-k3') + expect(resolveCanonicalModelId('orcarouter/auto')).toBe(resolveCanonicalModelId('claude-sonnet-4-5')) + expect(resolveCanonicalModelId('orcarouter/fusion')).toBe(resolveCanonicalModelId('openai/gpt-oss-120b')) + expect(resolveCanonicalModelId('orcarouter/fusion-flash')).toBe(resolveCanonicalModelId('openai/gpt-oss-120b')) + expect(resolveCanonicalModelId('orcarouter/fusion-mini')).toBe(resolveCanonicalModelId('openai/gpt-oss-120b')) + expect(resolveCanonicalModelId('orcarouter/deepseek/deepseek-v4-pro')).toBe( + resolveCanonicalModelId('deepseek/deepseek-v4-pro'), + ) }) }) @@ -282,11 +289,11 @@ describe('getShortModelName', () => { expect(getShortModelName('cp/cline-pass/glm-5.3')).toBe('GLM-5.3') // OrcaRouter route ids peel to the upstream id, which keeps the upstream label. expect(getShortModelName('orcarouter/deepseek/deepseek-v4-pro')).toBe('DeepSeek v4 Pro') - // The route ids keep their branded labels (like the other `*-auto` presets). - expect(getShortModelName('orcarouter/auto')).toBe('OrcaRouter (auto)') - expect(getShortModelName('orcarouter/fusion')).toBe('OrcaRouter Fusion') - expect(getShortModelName('orcarouter/fusion-flash')).toBe('OrcaRouter Fusion Flash') - expect(getShortModelName('orcarouter/fusion-mini')).toBe('OrcaRouter Fusion Mini') + // Route ids display as the model they route to, not a branded gateway label. + expect(getShortModelName('orcarouter/auto')).toBe(getShortModelName('claude-sonnet-4-5')) + expect(getShortModelName('orcarouter/fusion')).toBe(getShortModelName('openai/gpt-oss-120b')) + expect(getShortModelName('orcarouter/fusion-flash')).toBe(getShortModelName('openai/gpt-oss-120b')) + expect(getShortModelName('orcarouter/fusion-mini')).toBe(getShortModelName('openai/gpt-oss-120b')) // Grok Build prices via the grok-build-0.1 sibling. expect(getShortModelName('grok-build')).toBe('Grok Build') expect(getShortModelName('grok-build-0.1')).toBe('Grok Build')