From 8c3c949c9d7fd896985634171bacbbc19bce9fca Mon Sep 17 00:00:00 2001 From: diegosouzapw Date: Fri, 12 Jun 2026 23:09:19 -0300 Subject: [PATCH] chore(release): reconcile CI gates for v3.8.23 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - file-size baseline: re-freeze 8 files grown by PRs #3742/#3743/#3740 (cost drilldown, provider display modes, cache key isolation) - ARCHITECTURE.md: update executor count 55→60 (check:docs-counts drift) - .env.example: add OMNIROUTE_EMERGENCY_FALLBACK (#3741, env-doc-sync) - CHANGELOG: add formatted bullets for #3742, #3743, #3708, #3740, model-family-fallback fix; remove duplicate raw ### Fixed section --- .env.example | 5 +++++ CHANGELOG.md | 22 ++++++++++------------ docs/architecture/ARCHITECTURE.md | 2 +- file-size-baseline.json | 19 ++++++++++--------- 4 files changed, 26 insertions(+), 22 deletions(-) diff --git a/.env.example b/.env.example index f98c656048..5bede16ef5 100644 --- a/.env.example +++ b/.env.example @@ -507,6 +507,11 @@ PROVIDER_LIMITS_SYNC_SPACING_MS=1500 # Used by: src/lib/jobs/budgetResetJob.ts. Floor: 10000. #OMNIROUTE_BUDGET_RESET_JOB_INTERVAL_MS=600000 +# Emergency budget-exhaustion fallback (set false or 0 to disable the reroute to +# nvidia/openai/gpt-oss-120b when a request fails with a 402 budget error). +# Used by: open-sse/services/emergencyFallback.ts. Default: enabled. +#OMNIROUTE_EMERGENCY_FALLBACK=true + # Reasoning cache cleanup cadence (ms). Default: 1800000 (30m). Floor: 60000. # Used by: src/lib/jobs/reasoningCacheCleanupJob.ts. #OMNIROUTE_REASONING_CACHE_CLEANUP_INTERVAL_MS=1800000 diff --git a/CHANGELOG.md b/CHANGELOG.md index 484b46b363..234d26150c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,10 @@ - **Model Lockout: full settings UI with success-decay recovery** ([#3629](https://github.com/diegosouzapw/OmniRoute/pull/3629) — thanks @Chewji9875): end-to-end wiring of the per-model lockout feature — settings UI (enable/disable, configure thresholds), backend integration, structured error classification, and a success-decay mechanism that gradually recovers a locked model's fitness as successful calls accumulate. Lockout now applies to all providers when enabled, not just per-model-quota providers. +- **Provider display modes — All / Configured / Compact** ([#3743](https://github.com/diegosouzapw/OmniRoute/pull/3743) — thanks @rdself): adds a three-state display mode control to the Providers page. "All" shows every registered provider; "Configured" shows only providers with at least one connection; "Compact" shows configured providers in a condensed card layout for denser views. + +- **API key cost drilldown + quota % used** ([#3742](https://github.com/diegosouzapw/OmniRoute/pull/3742) — thanks @Witroch4): the API Keys page now shows a per-key cost breakdown and the percentage of quota consumed for each key. + ### 🔧 Bug Fixes - **`@omniroute/opencode-plugin` bundled in the npm tarball + `omniroute setup opencode` CLI command** ([#3726](https://github.com/diegosouzapw/OmniRoute/pull/3726) — thanks @herjarsa): the plugin was never compiled as part of the publish pipeline, requiring manual extraction. Now ships pre-built inside the `omniroute` package and installed via `omniroute setup opencode` (copies plugin into `~/.config/opencode/plugins/omniroute/`, updates `opencode.json` idempotently). Also fixes `provider.models` baseURL resolution — checks `_provider.options.baseURL` as a third fallback so partner/tiered providers no longer return zero models. ([#3711](https://github.com/diegosouzapw/OmniRoute/issues/3711)) @@ -54,6 +58,12 @@ - **Qwen Web: migrated to v2 chat API** ([#3723](https://github.com/diegosouzapw/OmniRoute/pull/3723) — thanks @diegosouzapw): the legacy `/api/chat/completions` endpoint was retired upstream returning `504` HTML from Alibaba's gateway for all requests. The executor now uses the two-step v2 flow (`/api/v2/chats/new` → `/api/v2/chat/completions?chat_id=`), replays the full browser cookie jar (cna + ssxmod_itna/itna2 + token) required by Alibaba's WAF instead of only a Bearer token, parses phase-based SSE (think→reasoning, answer→content), and refreshes the model catalog to current ids (`qwen3.7-max`, `qwen3.7-plus`, `qwen3.6-plus`; legacy ids kept as aliases). 17 unit tests. (Closes [#3288](https://github.com/diegosouzapw/OmniRoute/issues/3288)) +- **Responses API: `stream` defaults to `false` when omitted (spec compliance)** ([#3708](https://github.com/diegosouzapw/OmniRoute/pull/3708) — thanks @diegosouzapw): `/v1/responses` requests that omit `stream` no longer 502 (`STREAM_EARLY_EOF`) when the upstream returns a valid JSON response. `resolveStreamFlag` now applies the OpenAI Responses API spec default (stream=false) in addition to the existing Anthropic Messages API default — previously only `sourceFormat=claude` triggered this path, leaving `sourceFormat=openai-responses` to fall through to the wildcard-Accept heuristic (`Accept: */*` → streaming intent), which caused spec-compliant upstreams that return JSON to appear as a dead stream. Codex CLI (always sends `stream: true`) and explicit SSE clients (`Accept: text/event-stream`) are unaffected. + +- **Semantic cache: scope to requesting API key** ([#3740](https://github.com/diegosouzapw/OmniRoute/pull/3740) — thanks @diegosouzapw): two callers with different API keys sending the same prompt and model no longer receive each other's cached responses. `generateSignature` now includes the `api_key_id` dimension in the SHA-256 hash; unauthenticated requests (no API key) remain isolated from keyed requests. Existing cache entries (generated without the key dimension) are cleared by migration `098`. + +- **Model-family fallback: dot-notation model IDs now resolve correctly** (thanks @diegosouzapw): `getNextFamilyFallback` normalizes dots to hyphens for the initial lookup but also falls back to the bare model name, supporting IDs like `gemini-3.1-pro-high` whose dots are part of the literal name. Previously, `gemini-3.1-pro-high` silently returned null and bypassed the entire family. + ### ♻️ Code Quality - **Dashboard god-component (#3501): Phases 1g → 1t complete — ≤800 LOC target reached** ([#3717](https://github.com/diegosouzapw/OmniRoute/pull/3717), [#3721](https://github.com/diegosouzapw/OmniRoute/pull/3721), [#3725](https://github.com/diegosouzapw/OmniRoute/pull/3725), [#3727](https://github.com/diegosouzapw/OmniRoute/pull/3727) — thanks @diegosouzapw): four extraction phases bring `ProviderDetailPageClient.tsx` from 4,062 to 781 LOC — the ≤800 target set at the start of the refactor. Extracted OAuth flow helpers, quota display, traffic-inspector panel, logs viewer, combo-target editor, and remaining inline UI into standalone components under `providers/[id]/components/`. @@ -77,18 +87,6 @@ - **Combo strategy fallback coverage** (`tests/unit/combo-strategy-fallbacks.test.ts`, 11 tests): fill-first / p2c / random / cost-optimized / strict-random fallback paths (previously happy-path only), price-tie stability, stale strict-random deck degradation, unknown-strategy normalization to priority, and circuit-breaker HALF_OPEN recovery inside the combo loop + `preScreenTargets` (lazy-recovery contract). - **`#1731` fast-skip suite restored** (`tests/integration/combo-provider-exhaustion.test.ts`): the five skipped tests were rewritten against the current routing policy (quota-exhausted 429 marks the provider for the request; transient 429 retries other connections; connection errors skip per-connection; nothing persists across requests) and re-enabled — 8/8 green. - **Proxy context passthrough** (`tests/integration/proxy-context-passthrough.test.ts`): combo targets each execute under their own connection's proxy; `count_tokens` runs inside the connection's proxy context. -### 🐛 Fixed - -- chore(quality-gate): reconcile check-file-size baseline — freeze 27 inherited/this-round grown files at current LOC + register providerLimits.ts (greens the file-size gate; shrink tracked via #3501) -- fix(gemini): standard Gemini provider no longer sends tool calls without a thought_signature (falls back to context mode when the signature is unavailable), fixing HTTP 400 on multi-turn thinking-model tool calls (#3688) -- fix(antigravity): preserve gemini-3.1-pro High/Low budget tiers (upstream accepts the suffixed ids; stop collapsing to bare gemini-3.1-pro) (#3696) -- fix: streaming combos now fail over to the next target when an upstream returns an empty/content-filtered response instead of surfacing a blank reply (#3685) -- fix(qwen-web): migrate the Qwen Web provider to the v2 chat API. The legacy `/api/chat/completions` endpoint was retired upstream and returned `504` HTML from Alibaba's gateway for every request regardless of credentials, so no token refresh could help (#3288, discussion #2768). The executor now uses the two-step v2 flow (`/api/v2/chats/new` → `/api/v2/chat/completions?chat_id=`), replays the full browser cookie jar (cna + ssxmod_itna/itna2 + token) required by Alibaba's WAF instead of only a Bearer token, parses the phase-based SSE (think→reasoning, answer→content), and maps the WAF/expired HTML page to a clear actionable auth error instead of surfacing raw HTML. The credential requirement is now a full Cookie header, browser-login capture grabs the WAF cookies, and the model catalog is refreshed to the current upstream ids (`qwen3.7-max`, `qwen3.7-plus`, `qwen3.6-plus`; legacy ids kept as aliases). 17 unit tests. -- fix(responses): `/v1/responses` requests that omit `stream` no longer 502 (`STREAM_EARLY_EOF`) when the upstream returns a valid JSON response. `resolveStreamFlag` now applies the OpenAI Responses API spec default (stream=false when omitted) in addition to the existing Anthropic Messages API default — previously only `sourceFormat=claude` triggered this path, leaving `sourceFormat=openai-responses` to fall through to the wildcard-Accept heuristic (`Accept: */*` → streaming intent), which caused spec-compliant upstreams that return JSON to appear as a dead stream. Codex CLI (always sends `stream: true`) and explicit SSE clients (send `Accept: text/event-stream`) are unaffected. (#3708) -- fix(cache): semantic cache responses are now scoped to the requesting API key — two callers with different API keys that send the same prompt and model will no longer receive each other's cached responses (#3740). `generateSignature` includes the `api_key_id` dimension in the SHA-256 hash; unauthenticated requests (no API key) remain isolated from keyed requests. Existing cache entries (generated without the key dimension) are cleared by migration `098`. -- **Combo strategy fallback coverage** (`tests/unit/combo-strategy-fallbacks.test.ts`, 11 tests — thanks @diegosouzapw): fill-first / p2c / random / cost-optimized / strict-random fallback paths (previously happy-path only), price-tie stability, stale strict-random deck degradation, unknown-strategy normalization to priority, and circuit-breaker HALF_OPEN recovery inside the combo loop + `preScreenTargets` (lazy-recovery contract). -- **`#1731` fast-skip suite restored** (`tests/integration/combo-provider-exhaustion.test.ts` — thanks @diegosouzapw): 5 previously-skipped integration tests rewritten against the current routing policy — 8/8 green. -- **Proxy context passthrough** (`tests/integration/proxy-context-passthrough.test.ts` — thanks @diegosouzapw): combo targets each execute under their own connection's proxy; `count_tokens` runs inside the connection's proxy context. --- diff --git a/docs/architecture/ARCHITECTURE.md b/docs/architecture/ARCHITECTURE.md index e416f57f38..3792dec1ec 100644 --- a/docs/architecture/ARCHITECTURE.md +++ b/docs/architecture/ARCHITECTURE.md @@ -17,7 +17,7 @@ It provides a single OpenAI-compatible endpoint (`/v1/*`) and routes traffic acr Core capabilities: -- OpenAI-compatible API surface for CLI/tools (177 providers, 55 executors) +- OpenAI-compatible API surface for CLI/tools (177 providers, 60 executors) - Request/response translation across provider formats - Model combo fallback (multi-model sequence) - Structured combo steps (`provider + model + connection`) with runtime ordering by `compositeTiers` diff --git a/file-size-baseline.json b/file-size-baseline.json index d2ba7c8977..62e48dcfdb 100644 --- a/file-size-baseline.json +++ b/file-size-baseline.json @@ -30,32 +30,32 @@ "open-sse/services/combo.ts": 5054, "open-sse/services/rateLimitManager.ts": 1017, "open-sse/services/tokenRefresh.ts": 1997, - "open-sse/services/usage.ts": 3341, + "open-sse/services/usage.ts": 3394, "open-sse/translator/request/openai-to-gemini.ts": 844, "open-sse/translator/response/openai-responses.ts": 873, "open-sse/utils/cursorAgentProtobuf.ts": 1499, "open-sse/utils/stream.ts": 2710, "src/app/(dashboard)/dashboard/HomePageClient.tsx": 1385, "src/app/(dashboard)/dashboard/analytics/ComboHealthTab.tsx": 1020, - "src/app/(dashboard)/dashboard/api-manager/ApiManagerPageClient.tsx": 2680, + "src/app/(dashboard)/dashboard/api-manager/ApiManagerPageClient.tsx": 2701, "src/app/(dashboard)/dashboard/cache/media/MediaPageClient.tsx": 1105, "src/app/(dashboard)/dashboard/cache/page.tsx": 841, "src/app/(dashboard)/dashboard/cli-code/components/CodexToolCard.tsx": 894, "src/app/(dashboard)/dashboard/cloud-agents/page.tsx": 913, "src/app/(dashboard)/dashboard/combos/page.tsx": 4350, - "src/app/(dashboard)/dashboard/costs/CostOverviewTab.tsx": 1481, + "src/app/(dashboard)/dashboard/costs/CostOverviewTab.tsx": 1495, "src/app/(dashboard)/dashboard/costs/quota-share/components/PoolWizard.tsx": 1007, "src/app/(dashboard)/dashboard/endpoint/EndpointPageClient.tsx": 2570, "src/app/(dashboard)/dashboard/health/page.tsx": 1091, "src/app/(dashboard)/dashboard/playground/components/tabs/ApiTab.tsx": 847, - "src/app/(dashboard)/dashboard/providers/[id]/ProviderDetailPageClient.tsx": 782, + "src/app/(dashboard)/dashboard/providers/[id]/ProviderDetailPageClient.tsx": 784, "src/app/(dashboard)/dashboard/providers/[id]/components/ConnectionRow.tsx": 941, "src/app/(dashboard)/dashboard/providers/[id]/components/modals/AddApiKeyModal.tsx": 843, "src/app/(dashboard)/dashboard/providers/[id]/components/modals/EditConnectionModal.tsx": 1171, "src/app/(dashboard)/dashboard/providers/[id]/hooks/useProviderConnections.ts": 954, "src/app/(dashboard)/dashboard/providers/[id]/hooks/useProviderModels.ts": 155, "src/app/(dashboard)/dashboard/providers/[id]/hooks/useProviderSettings.ts": 264, - "src/app/(dashboard)/dashboard/providers/[id]/providerPageHelpers.ts": 897, + "src/app/(dashboard)/dashboard/providers/[id]/providerPageHelpers.ts": 939, "src/app/(dashboard)/dashboard/providers/components/onboarding/ProviderOnboardingWizard.tsx": 906, "src/app/(dashboard)/dashboard/providers/page.tsx": 1925, "src/app/(dashboard)/dashboard/runtime/RuntimePageClient.tsx": 1198, @@ -90,18 +90,18 @@ "src/lib/providers/validation.ts": 4209, "src/lib/tailscaleTunnel.ts": 1189, "src/lib/usage/callLogs.ts": 975, - "src/lib/usage/providerLimits.ts": 941, + "src/lib/usage/providerLimits.ts": 943, "src/lib/usage/usageHistory.ts": 854, "src/shared/components/OAuthModal.tsx": 956, "src/shared/components/RequestLoggerV2.tsx": 1232, "src/shared/components/analytics/charts.tsx": 1558, "src/shared/constants/cliTools.ts": 875, "src/shared/constants/pricing.ts": 1470, - "src/shared/constants/providers.ts": 3144, + "src/shared/constants/providers.ts": 3146, "src/shared/constants/sidebarVisibility.ts": 990, "src/shared/services/cliRuntime.ts": 1084, "src/shared/validation/schemas.ts": 2515, - "src/sse/handlers/chat.ts": 1389, + "src/sse/handlers/chat.ts": 1392, "src/sse/services/auth.ts": 2207 }, "_rebaseline_2026_06_09": "Re-baseline consciente pre-release v3.8.19: 9 arquivos cresceram durante o ciclo (features mergeadas: RequestLoggerV2 +281 request-logger rework, stream +101, combo +73, chatCore +45, catalog +32 fable-5/catalog-flag, callLogs +4, accountFallback +2, usageHistory novo 840) + core.ts +7 (fix resetAllDbModuleState, PR 3536). A catraca segue valendo destes valores — proximo crescimento falha. Decisao: encolher (esp. RequestLoggerV2/chatCore) e a issue #3501 ficam para o ciclo seguinte.", @@ -109,5 +109,6 @@ "_rebaseline_2026_06_12_review_issues": "Re-baseline consciente do /review-issues v3.8.23: 27 arquivos com crescimento herdado (v3.8.22 nunca reconciliado) + fixes deste round (combo.ts #3685, openai-to-gemini.ts #3688, tokenRefresh.ts #3692, validation/proxies de outras merges). providerLimits.ts (941) adicionado como frozen (split coeso de usage). Shrink endereçado separadamente pelo #3501.", "_rebaseline_2026_06_12_phase1g1j": "Phase 1g-1j (#3501): ProviderDetailPageClient.tsx 4063→3409 (extraídos ProviderPlaygroundPanel, useCommandCodeAuth, useExternalLinkFlow+ExternalLinkModal, useAuthFileHandlers — zero lógica nova). models/route.ts 2344→2426: drift do #3712 (vertex dynamic model discovery) reconciliado aqui.", "_rebaseline_2026_06_12_phase1n1s": "Phase 1n-1s (#3501): ProviderDetailPageClient.tsx 2554→1376 (extraídos ConnectionsListPanel, ConnectionsHeaderToolbar, ZedImportCard, BatchTestResultsModal, AdaptaTutorialModal + hooks/useApiKeySave + 4 helper closures→providerPageHelpers.ts). providerPageHelpers.ts 822→897 justificado: recebe 4 closures do god-component (getApiLabel/getApiDefaultPath/getApiPath/getHeaderIconProviderId), zero lógica nova, cliente encolhe mais do que helpers crescem.", - "_rebaseline_2026_06_12_phase1t": "Phase 1t (#3501): ProviderDetailPageClient.tsx 1377→782 — META ≤800 ATINGIDA (extraídos ProviderPageHeader, CompatibleNodeCard, ProviderModalsPanel, EmptyConnectionsPlaceholder, UpstreamProxyCard, SearchProviderCard + hooks useConnectionGate/useProviderNodeActions). Drift concorrente reconciliado: ResilienceTab/sse-chat/sse-auth/accountFallback/combo (merges #3629 model-lockout etc.)." + "_rebaseline_2026_06_12_phase1t": "Phase 1t (#3501): ProviderDetailPageClient.tsx 1377→782 — META ≤800 ATINGIDA (extraídos ProviderPageHeader, CompatibleNodeCard, ProviderModalsPanel, EmptyConnectionsPlaceholder, UpstreamProxyCard, SearchProviderCard + hooks useConnectionGate/useProviderNodeActions). Drift concorrente reconciliado: ResilienceTab/sse-chat/sse-auth/accountFallback/combo (merges #3629 model-lockout etc.).", + "_rebaseline_2026_06_12_v3823_new_features": "Re-baseline v3.8.23 pós-merge de #3742 (cost drilldown: ApiManagerPageClient.tsx +21, CostOverviewTab.tsx +14, providerLimits.ts +2, usage.ts +53) + #3743 (provider display modes: ProviderDetailPageClient.tsx +2, providerPageHelpers.ts +42, providers.ts +2) + #3740 (semantic cache key isolation: chat.ts +3). Crescimento justificado por features novas mergeadas no ciclo." }