mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-26 17:12:27 +03:00
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:
@@ -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(),
|
||||
|
||||
Reference in New Issue
Block a user