feat(fallback): add OMNIROUTE_EMERGENCY_FALLBACK env switch (#3741)

Adds an OMNIROUTE_EMERGENCY_FALLBACK env switch to disable the emergency budget-exhaustion fallback (reroute to free nvidia/gpt-oss-120b). Default unchanged (enabled). Closes #3739, related #2879.

Integrated into release/v3.8.23.
This commit is contained in:
Zois Pagoulatos
2026-06-13 01:48:26 +02:00
committed by GitHub
parent d710c23c08
commit 18821b4e95
3 changed files with 101 additions and 0 deletions

View File

@@ -874,6 +874,7 @@ Provider quota endpoints, network tunnels (Tailscale, Ngrok, MITM debug proxy),
| `ALIBABA_CODING_PLAN_QUOTA_URL` | derived from host | `open-sse/services/bailianQuotaFetcher.ts` | Full quota URL override for Alibaba Bailian. |
| `CONTEXT_RESERVE_TOKENS` | `1024` | `open-sse/services/contextManager.ts` | Tokens reserved for completion output when computing prompt budgets. |
| `MODEL_ALIAS_COMPAT_ENABLED` | enabled | `open-sse/services/model.ts` | Toggle the legacy model-alias compatibility layer used by older clients. |
| `OMNIROUTE_EMERGENCY_FALLBACK` | enabled | `open-sse/services/emergencyFallback.ts` | Set `false` (or `0`) to disable the emergency budget-exhaustion fallback that reroutes failed requests to the free `nvidia`/`openai/gpt-oss-120b` model. |
| `COMMAND_CODE_CALLBACK_PORT` | _(unset)_ | `src/app/api/providers/command-code/auth/shared.ts` | Local port used for OAuth-style callbacks from the Command Code CLI helper. |
| `COMMAND_CODE_VERSION` | `0.33.2` | `open-sse/executors/commandCode.ts` | Value sent as the `x-command-code-version` header to the Command Code upstream. Override to bump the CLI version. |
| `MITM_LOCAL_PORT` | `443` | `src/mitm/server.cjs` | Local bind port for the MITM debug proxy. |

View File

@@ -7,6 +7,9 @@
*
* Inspired by ClawRouter: "gpt-oss-120b costs nothing and serves as
* automatic fallback when wallet is empty."
*
* Operators can disable the redirect entirely with
* `OMNIROUTE_EMERGENCY_FALLBACK=false` (or `0`). Default remains enabled.
*/
export interface EmergencyFallbackConfig {
@@ -63,6 +66,11 @@ export interface NoFallbackDecision {
export type FallbackResult = FallbackDecision | NoFallbackDecision;
export function isEmergencyFallbackEnvEnabled(): boolean {
const raw = process.env.OMNIROUTE_EMERGENCY_FALLBACK;
return raw !== "false" && raw !== "0";
}
export function shouldUseFallback(
status: number,
errorBody: string,
@@ -70,6 +78,12 @@ export function shouldUseFallback(
config: EmergencyFallbackConfig = EMERGENCY_FALLBACK_CONFIG
): FallbackResult {
if (!config.enabled) return { shouldFallback: false, reason: "emergency fallback disabled" };
if (!isEmergencyFallbackEnvEnabled()) {
return {
shouldFallback: false,
reason: "emergency fallback disabled via OMNIROUTE_EMERGENCY_FALLBACK",
};
}
if (config.skipForToolRequests && requestHasTools) {
return { shouldFallback: false, reason: "skipped: request has tools" };
}

View File

@@ -0,0 +1,86 @@
import test from "node:test";
import assert from "node:assert/strict";
import {
shouldUseFallback,
isEmergencyFallbackEnvEnabled,
EMERGENCY_FALLBACK_CONFIG,
} from "../../../open-sse/services/emergencyFallback.ts";
function withEnv(value: string | undefined, fn: () => void) {
const previous = process.env.OMNIROUTE_EMERGENCY_FALLBACK;
if (value === undefined) {
delete process.env.OMNIROUTE_EMERGENCY_FALLBACK;
} else {
process.env.OMNIROUTE_EMERGENCY_FALLBACK = value;
}
try {
fn();
} finally {
if (previous === undefined) {
delete process.env.OMNIROUTE_EMERGENCY_FALLBACK;
} else {
process.env.OMNIROUTE_EMERGENCY_FALLBACK = previous;
}
}
}
test("emergency fallback stays enabled when the env switch is unset (default behavior)", () => {
withEnv(undefined, () => {
assert.equal(isEmergencyFallbackEnvEnabled(), true);
const decision = shouldUseFallback(402, "", false);
assert.equal(decision.shouldFallback, true);
if (decision.shouldFallback) {
assert.equal(decision.provider, EMERGENCY_FALLBACK_CONFIG.provider);
assert.equal(decision.model, EMERGENCY_FALLBACK_CONFIG.model);
}
});
});
test("budget keywords trigger fallback when the env switch is unset", () => {
withEnv(undefined, () => {
const decision = shouldUseFallback(429, "All accounts quota exceeded", false);
assert.equal(decision.shouldFallback, true);
});
});
test("OMNIROUTE_EMERGENCY_FALLBACK=false disables the 402 redirect", () => {
withEnv("false", () => {
assert.equal(isEmergencyFallbackEnvEnabled(), false);
const decision = shouldUseFallback(402, "", false);
assert.equal(decision.shouldFallback, false);
assert.match(decision.reason, /OMNIROUTE_EMERGENCY_FALLBACK/);
});
});
test("OMNIROUTE_EMERGENCY_FALLBACK=0 disables the budget-keyword redirect", () => {
withEnv("0", () => {
const decision = shouldUseFallback(429, "quota exceeded for account", false);
assert.equal(decision.shouldFallback, false);
assert.match(decision.reason, /OMNIROUTE_EMERGENCY_FALLBACK/);
});
});
test("explicit truthy values keep the fallback enabled", () => {
withEnv("true", () => {
assert.equal(isEmergencyFallbackEnvEnabled(), true);
assert.equal(shouldUseFallback(402, "", false).shouldFallback, true);
});
});
test("env switch does not override config.enabled=false", () => {
withEnv("true", () => {
const decision = shouldUseFallback(402, "", false, {
...EMERGENCY_FALLBACK_CONFIG,
enabled: false,
});
assert.equal(decision.shouldFallback, false);
});
});
test("tool-bearing requests are still skipped regardless of env switch", () => {
withEnv(undefined, () => {
const decision = shouldUseFallback(402, "", true);
assert.equal(decision.shouldFallback, false);
assert.match(decision.reason, /tools/);
});
});