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
13 changes: 12 additions & 1 deletion lib/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,18 @@ export const API_CONFIG = {

// OpenRouter
OPENROUTER_API_URL: 'https://openrouter.ai/api/v1/chat/completions',
OPENROUTER_DEFAULT_MODEL: 'anthropic/claude-sonnet-5',
// Must be a FREE id. OpenRouter is the FALLBACK here — reached when Groq's
// free tier is spent, i.e. only when nobody is watching. This default was
// `anthropic/claude-sonnet-5`, a premium paid model, so "the free tier ran
// out" produced a bill rather than a degraded answer — a spending decision
// made by an outage instead of by a person. (It also broke the standing rule
// that Anthropic is never a primary or fallback provider in this fleet.)
//
// `openai/gpt-oss-20b:free` reports pricing.prompt = 0 in OpenRouter's own
// catalogue (checked 2026-08-16) and was probed live for tool support on
// 2026-08-15. Free catalogues rot — when it does, replace it with another
// `:free` id, never by dropping the suffix.
OPENROUTER_DEFAULT_MODEL: 'openai/gpt-oss-20b:free',

// Token limits
MAX_CONTEXT_CHARS: 8000,
Expand Down
27 changes: 27 additions & 0 deletions tests/__tests__/lib/free-fallback.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/**
* The OpenRouter fallback must never be a paid model.
*
* Pinned as a test rather than trusted to review because this path is invisible
* in normal operation: OpenRouter is only reached when Groq's free tier is
* spent, so a paid id there bills precisely when nobody is watching. The
* previous default was `anthropic/claude-sonnet-5` — a premium model, and a
* breach of the standing fleet rule that Anthropic is never a primary or
* fallback provider.
*
* The rule mirrors `modelCost` in the shared `ai-ration` package: a routed id
* (`vendor/model`) is free only with the `:free` suffix. That suffix is the
* entire difference between free routing and a per-call charge for identical
* weights, which is why an id one token away from correct kept slipping through.
*/
import { API_CONFIG } from '@/lib/constants';

describe('free-first fallback', () => {
it('uses a :free OpenRouter model as the fallback default', () => {
expect(API_CONFIG.OPENROUTER_DEFAULT_MODEL).toMatch(/:free$/);
});

it('never falls back to Anthropic', () => {
// Standing fleet rule: Anthropic is paid and is not a fallback anywhere.
expect(API_CONFIG.OPENROUTER_DEFAULT_MODEL).not.toMatch(/anthropic/i);
});
});
Loading