mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-24 16:12:23 +03:00
feat(sse): add STRICT_ZERO_COST opt-in free-access policy (#10965)
⭐5 — freeAccessPolicy "strict" opt-in (default off): verifica candidatos de auto-combo contra estado de quota ao vivo + segurança econômica por conexão antes do dispatch (fail-closed — estado desconhecido/stale/incompleto é excluído). Zero mudança de comportamento com o default "off". 82 testes focados, eslint/prettier/typecheck limpos, docs em docs/routing/STRICT_ZERO_COST.md.
This commit is contained in:
@@ -238,6 +238,11 @@ export async function getSettings() {
|
||||
// (`:free` suffix, zero-price pricing, or FREE_MODEL_BUDGETS membership). Default
|
||||
// false preserves prior behaviour; opt-in only.
|
||||
hidePaidModels: false,
|
||||
// Opt-in, default off: same shape as hidePaidModels above, but requires a
|
||||
// live hard-stop-guaranteed quota check for non-keyless free candidates.
|
||||
// See open-sse/services/autoCombo/strictZeroCostFilter.ts.
|
||||
freeAccessPolicy: "off",
|
||||
excludeTosAvoid: false,
|
||||
// #9418: Opt-in filter that hides auto/* virtual combos from the /v1/models catalog.
|
||||
// User-defined combos are unaffected; routing still works for hidden ids sent explicitly.
|
||||
hideAutoCombos: false,
|
||||
|
||||
@@ -128,6 +128,16 @@ export const updateSettingsSchema = z.object({
|
||||
blockedProviders: z.array(z.string().max(100)).optional(),
|
||||
noAuthFallbackDisabledProviders: z.array(z.string().max(100)).optional(),
|
||||
hidePaidModels: z.boolean().optional(),
|
||||
// STRICT_ZERO_COST (opt-in, default "off"): stricter than hidePaidModels — a
|
||||
// candidate must be keyless (no credential exists, so no request against it
|
||||
// can ever be billed) OR pass a live, fresh, hard-stop-guaranteed quota
|
||||
// check, per candidate, before ranking/dispatch. See
|
||||
// open-sse/services/autoCombo/strictZeroCostFilter.ts.
|
||||
freeAccessPolicy: z.enum(["off", "strict"]).optional(),
|
||||
// Separate from freeAccessPolicy on purpose: excludes candidates whose
|
||||
// curated `tos` verdict is "avoid" (proxy/self-hosted use conflicts with the
|
||||
// provider's own terms) — a contractual concern, not an economic one.
|
||||
excludeTosAvoid: z.boolean().optional(),
|
||||
hideHealthCheckLogs: z.boolean().optional(),
|
||||
hideEndpointCloudflaredTunnel: z.boolean().optional(),
|
||||
hideEndpointTailscaleFunnel: z.boolean().optional(),
|
||||
|
||||
@@ -2453,6 +2453,18 @@ export async function markAccountUnavailable(
|
||||
try {
|
||||
await currentMutex;
|
||||
|
||||
// STRICT_ZERO_COST: this connection just failed (whatever the reason) —
|
||||
// drop any cached "SAFE" free-allowance reading for it immediately rather
|
||||
// than waiting out the TTL, so the very next candidate-pool build reads a
|
||||
// clean cache miss (UNKNOWN → excluded) instead of a stale SAFE. Cheap,
|
||||
// idempotent, and correct to over-invalidate on non-quota failures too —
|
||||
// worst case is one extra background refresh.
|
||||
if (provider) {
|
||||
const { invalidateFreeAccessState } =
|
||||
await import("@omniroute/open-sse/services/autoCombo/freeAccessQuota.ts");
|
||||
invalidateFreeAccessState(provider, connectionId);
|
||||
}
|
||||
|
||||
const resourceBypass = getResource404Bypass(status, errorText, connectionId, log);
|
||||
if (resourceBypass) return resourceBypass;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user