fix(free-tier): serve the Radar overlay from /api/free-tier/summary (#11550)

Validated in a combined 4-PR batch worktree off release/v3.8.51 tip. Also removed an unused `crypto` import from the new test file (one-line lint fix, 228→229 unrelated-drift comparison confirmed it was the only new finding) — pushed to this branch.
- Focused tests: free-tier-summary-radar-overlay.test.ts (9/9) + free-model-catalog/free-catalog-2026-07-expansion/free-providers-batch-2026-07 — part of batch's 60/60 node:test run
- typecheck:core, file-size, changelog-integrity, complexity, cognitive-complexity, check:docs-counts-sync — all OK
- Full-repo lint: 228 pre-existing dashboard react-hooks/* findings, unrelated to this diff (after the crypto-import fix)

Thanks for this — the community/live feed entitlement distinction (never re-publishing paid feed content to anonymous callers) mirrors #9686's treatment carefully, and the catalogUpdatedAt honesty (null over a fabricated download-time stand-in) is the right call.
This commit is contained in:
Dizzle
2026-08-26 00:49:00 +02:00
committed by GitHub
parent 3863dab149
commit d3c395bbcf
4 changed files with 367 additions and 7 deletions

View File

@@ -1,7 +1,16 @@
import { computeFreeModelTotals } from "@omniroute/open-sse/config/freeModelCatalog.ts";
import { FREE_CATALOG_CURATED_AT } from "@omniroute/open-sse/config/freeModelCatalog.data.ts";
import { listNoCredentialProviders } from "@/shared/utils/providerCredentialRequirement";
import {
computeFreeModelTotals,
type FreeModelBudget,
} from "@omniroute/open-sse/config/freeModelCatalog.ts";
import {
FREE_CATALOG_CURATED_AT,
FREE_MODEL_BUDGETS,
} from "@omniroute/open-sse/config/freeModelCatalog.data.ts";
import type { MergedEntry } from "@/lib/radar/applyFeed";
import { getRadarCatalog } from "@/lib/radar";
import { sumUsageTokensThisMonth } from "@/lib/db/usageSummary";
import { isAuthenticated } from "@/shared/utils/apiAuth";
import { listNoCredentialProviders } from "@/shared/utils/providerCredentialRequirement";
const CORS = {
"Access-Control-Allow-Origin": "*",
@@ -9,6 +18,39 @@ const CORS = {
"Access-Control-Allow-Headers": "Content-Type, Authorization",
};
/**
* `hardStopGuaranteed` is a hand-curated fact about the upstream provider that
* the Radar feed does not carry, so it is read back from the baseline entry
* rather than dropped when the overlay is served.
*/
const HARD_STOP_BY_KEY = new Map(
FREE_MODEL_BUDGETS.filter((m) => m.hardStopGuaranteed !== undefined).map((m) => [
`${m.provider}:${m.modelId}`,
m.hardStopGuaranteed,
])
);
/**
* Project a merged catalog entry back onto the response's `FreeModelBudget`
* shape: the overlay's extra fields (origin, provenance, capabilities…) stay
* internal, so the published contract is identical whichever source answered.
*/
function toBudgetEntry(entry: MergedEntry): FreeModelBudget & { enabled?: boolean } {
return {
provider: entry.provider,
modelId: entry.modelId,
displayName: entry.displayName,
monthlyTokens: entry.monthlyTokens,
creditTokens: entry.creditTokens,
freeType: entry.freeType,
poolKey: entry.poolKey,
tos: entry.tos,
trainsOnPrompts: entry.trainsOnPrompts,
hardStopGuaranteed: HARD_STOP_BY_KEY.get(`${entry.provider}:${entry.modelId}`),
enabled: entry.enabled,
};
}
export function OPTIONS(): Response {
return new Response(null, { status: 204, headers: CORS });
}
@@ -16,13 +58,34 @@ export function OPTIONS(): Response {
export async function GET(req: Request): Promise<Response> {
const url = new URL(req.url);
const excludeTosAvoid = url.searchParams.get("excludeTosAvoid") === "1";
const totals = computeFreeModelTotals({ excludeTosAvoid });
// Same catalog resolution as the dashboard screens: one source of
// truth, with meta === null meaning "the baseline answered" (flag off, no
// cache, or corrupt cache).
const { entries, meta } = getRadarCatalog();
// Entitlement follows the feed server's own download-time decision: the
// community feed is the free public catalog, so it serves everyone; the
// live feed is supporter-key content, so it only reaches callers this
// instance has authenticated — never anonymous visitors, or an exposed
// instance would re-publish the paid feed for free.
const serveOverlay = meta !== null && (meta.tier !== "live" || (await isAuthenticated(req)));
const totals = serveOverlay
? computeFreeModelTotals({ excludeTosAvoid, entries: entries.map(toBudgetEntry) })
: computeFreeModelTotals({ excludeTosAvoid });
const usedThisMonth = sumUsageTokensThisMonth();
const body = {
...totals,
usedThisMonth,
remaining: Math.max(0, totals.steadyRecurringTokens - usedThisMonth),
catalogUpdatedAt: FREE_CATALOG_CURATED_AT,
// Which source answered, and the date of what was actually served — the
// feed's own build date when the overlay answers (null when a cache row
// predates build-date tracking; never the download time standing in),
// the release curation date when the baseline does.
catalogUpdatedAt: serveOverlay ? meta.generatedAt : FREE_CATALOG_CURATED_AT,
catalogSource: serveOverlay ? ("radar-overlay" as const) : ("baseline" as const),
// Computed here, not in the component: deriving it client-side would pull
// the whole provider REGISTRY into the browser bundle.
noCredentialProviders: listNoCredentialProviders(),