mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-04 14:22:09 +03:00
refactor(usage): extract pure quota-normalize helpers from providerLimits.ts (#5730)
providerLimits.ts (954 lines, frozen-baselined) is the heavily DB/network-coupled
provider quota sync module. Extract a small, fully SELF-CONTAINED leaf of pure
quota-key/quota-value normalization helpers (+ the isRecord type guard they
share), leaving all sync/DB/network code in the host:
- providerLimits/quotaNormalize.ts (72) isRecord, isUsageQuotaKeyAllowed,
normalizeUsageQuotaKey, normalizeUsageQuotasForProvider,
sanitizeUsageQuotasForProvider
Host providerLimits.ts: 954 -> 890. The leaf imports only the external
antigravity/agy model-alias helpers the moved bodies reference (moved from the
host's import block) — it does NOT import the host, so check:cycles stays clean
(no cycle). isRecord (used ~9x in the host) is co-extracted and imported back.
These five were all module-internal, so the public API is unchanged (13
exported functions). Bodies moved byte-identical.
Behavior-preserving: 18 existing provider-limits consumer tests stay green
(sanitize-scope 3, db-provider-limits 3, proxy-fail-closed 3,
rotating-expired-guard 7, codex-quota-sync 2); new
tests/unit/providerlimits-quotanormalize-split.test.ts (19 assertions) pins
isRecord + isUsageQuotaKeyAllowed + guards the 13-function public API.
Refs #3501.
This commit is contained in:
committed by
GitHub
parent
04d22a784d
commit
ba7f2145d0
@@ -27,12 +27,13 @@ import {
|
||||
extractCodeAssistSubscriptionTier,
|
||||
} from "@omniroute/open-sse/services/codeAssistSubscription.ts";
|
||||
import { runWithProxyContext } from "@omniroute/open-sse/utils/proxyFetch.ts";
|
||||
import {
|
||||
isUserCallableAntigravityModelId,
|
||||
toClientAntigravityModelId,
|
||||
} from "@omniroute/open-sse/config/antigravityModelAliases.ts";
|
||||
import { isUserCallableAgyModelId } from "@omniroute/open-sse/config/agyModels.ts";
|
||||
import { onUsageRecorded } from "./usageEvents";
|
||||
import {
|
||||
isRecord,
|
||||
isUsageQuotaKeyAllowed,
|
||||
normalizeUsageQuotasForProvider,
|
||||
sanitizeUsageQuotasForProvider,
|
||||
} from "./providerLimits/quotaNormalize";
|
||||
|
||||
type JsonRecord = Record<string, unknown>;
|
||||
type SyncSource = "manual" | "scheduled";
|
||||
@@ -79,10 +80,6 @@ const PROVIDER_LIMITS_AUTO_SYNC_SETTING_KEY = "provider_limits_auto_sync_last_ru
|
||||
const DEFAULT_PROVIDER_LIMITS_POST_USAGE_REFRESH_DELAY_MS = 5_000;
|
||||
const pendingPostUsageRefreshes = new Set<string>();
|
||||
|
||||
function isRecord(value: unknown): value is JsonRecord {
|
||||
return value !== null && typeof value === "object" && !Array.isArray(value);
|
||||
}
|
||||
|
||||
function toProviderLimitsCacheEntry(
|
||||
usage: JsonRecord,
|
||||
source: SyncSource,
|
||||
@@ -135,67 +132,6 @@ export function notifyProviderUsageRecorded(
|
||||
// the provider-limits route and the background auto-sync scheduler at server boot.
|
||||
onUsageRecorded(notifyProviderUsageRecorded);
|
||||
|
||||
function isUsageQuotaKeyAllowed(provider: string, quotaKey: string): boolean {
|
||||
if (quotaKey === "credits" || quotaKey === "models") return true;
|
||||
if (provider === "antigravity") return isUserCallableAntigravityModelId(quotaKey);
|
||||
if (provider === "agy") return isUserCallableAgyModelId(quotaKey);
|
||||
return true;
|
||||
}
|
||||
|
||||
function normalizeUsageQuotaKey(provider: string, quotaKey: string): string | null {
|
||||
if (quotaKey === "credits" || quotaKey === "models") return quotaKey;
|
||||
if (provider === "antigravity" || provider === "agy") {
|
||||
const clientKey = toClientAntigravityModelId(quotaKey);
|
||||
return isUsageQuotaKeyAllowed(provider, clientKey) ? clientKey : null;
|
||||
}
|
||||
return isUsageQuotaKeyAllowed(provider, quotaKey) ? quotaKey : null;
|
||||
}
|
||||
|
||||
function normalizeUsageQuotasForProvider(
|
||||
provider: string,
|
||||
quotas: JsonRecord | null | undefined
|
||||
): JsonRecord | null {
|
||||
if (!isRecord(quotas)) return quotas ?? null;
|
||||
|
||||
const normalized: JsonRecord = {};
|
||||
let changed = false;
|
||||
|
||||
for (const [quotaKey, quota] of Object.entries(quotas)) {
|
||||
const normalizedKey = normalizeUsageQuotaKey(provider, quotaKey);
|
||||
if (!normalizedKey) {
|
||||
changed = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
const existing = normalized[normalizedKey];
|
||||
if (existing && isRecord(existing) && isRecord(quota)) {
|
||||
const existingSource = String(existing.quotaSource ?? "");
|
||||
const nextSource = String(quota.quotaSource ?? "");
|
||||
const sourceRank: Record<string, number> = {
|
||||
fetchAvailableModels: 0,
|
||||
localUsageHistory: 1,
|
||||
retrieveUserQuota: 2,
|
||||
};
|
||||
if ((sourceRank[existingSource] ?? 0) > (sourceRank[nextSource] ?? 0)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
normalized[normalizedKey] = quota as JsonRecord;
|
||||
if (normalizedKey !== quotaKey) changed = true;
|
||||
}
|
||||
|
||||
return changed ? normalized : quotas;
|
||||
}
|
||||
|
||||
function sanitizeUsageQuotasForProvider(provider: string, usage: JsonRecord): JsonRecord {
|
||||
if (provider !== "antigravity" && provider !== "agy") return usage;
|
||||
if (!isRecord(usage.quotas)) return usage;
|
||||
|
||||
const sanitizedQuotas = normalizeUsageQuotasForProvider(provider, usage.quotas);
|
||||
return sanitizedQuotas === usage.quotas ? usage : { ...usage, quotas: sanitizedQuotas };
|
||||
}
|
||||
|
||||
function hasRetrieveUserQuotaSource(
|
||||
provider: string,
|
||||
cache: ProviderLimitsCacheEntry | undefined
|
||||
|
||||
72
src/lib/usage/providerLimits/quotaNormalize.ts
Normal file
72
src/lib/usage/providerLimits/quotaNormalize.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
import {
|
||||
isUserCallableAntigravityModelId,
|
||||
toClientAntigravityModelId,
|
||||
} from "@omniroute/open-sse/config/antigravityModelAliases.ts";
|
||||
import { isUserCallableAgyModelId } from "@omniroute/open-sse/config/agyModels.ts";
|
||||
|
||||
type JsonRecord = Record<string, unknown>;
|
||||
|
||||
export function isRecord(value: unknown): value is JsonRecord {
|
||||
return value !== null && typeof value === "object" && !Array.isArray(value);
|
||||
}
|
||||
|
||||
export function isUsageQuotaKeyAllowed(provider: string, quotaKey: string): boolean {
|
||||
if (quotaKey === "credits" || quotaKey === "models") return true;
|
||||
if (provider === "antigravity") return isUserCallableAntigravityModelId(quotaKey);
|
||||
if (provider === "agy") return isUserCallableAgyModelId(quotaKey);
|
||||
return true;
|
||||
}
|
||||
|
||||
export function normalizeUsageQuotaKey(provider: string, quotaKey: string): string | null {
|
||||
if (quotaKey === "credits" || quotaKey === "models") return quotaKey;
|
||||
if (provider === "antigravity" || provider === "agy") {
|
||||
const clientKey = toClientAntigravityModelId(quotaKey);
|
||||
return isUsageQuotaKeyAllowed(provider, clientKey) ? clientKey : null;
|
||||
}
|
||||
return isUsageQuotaKeyAllowed(provider, quotaKey) ? quotaKey : null;
|
||||
}
|
||||
|
||||
export function normalizeUsageQuotasForProvider(
|
||||
provider: string,
|
||||
quotas: JsonRecord | null | undefined
|
||||
): JsonRecord | null {
|
||||
if (!isRecord(quotas)) return quotas ?? null;
|
||||
|
||||
const normalized: JsonRecord = {};
|
||||
let changed = false;
|
||||
|
||||
for (const [quotaKey, quota] of Object.entries(quotas)) {
|
||||
const normalizedKey = normalizeUsageQuotaKey(provider, quotaKey);
|
||||
if (!normalizedKey) {
|
||||
changed = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
const existing = normalized[normalizedKey];
|
||||
if (existing && isRecord(existing) && isRecord(quota)) {
|
||||
const existingSource = String(existing.quotaSource ?? "");
|
||||
const nextSource = String(quota.quotaSource ?? "");
|
||||
const sourceRank: Record<string, number> = {
|
||||
fetchAvailableModels: 0,
|
||||
localUsageHistory: 1,
|
||||
retrieveUserQuota: 2,
|
||||
};
|
||||
if ((sourceRank[existingSource] ?? 0) > (sourceRank[nextSource] ?? 0)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
normalized[normalizedKey] = quota as JsonRecord;
|
||||
if (normalizedKey !== quotaKey) changed = true;
|
||||
}
|
||||
|
||||
return changed ? normalized : quotas;
|
||||
}
|
||||
|
||||
export function sanitizeUsageQuotasForProvider(provider: string, usage: JsonRecord): JsonRecord {
|
||||
if (provider !== "antigravity" && provider !== "agy") return usage;
|
||||
if (!isRecord(usage.quotas)) return usage;
|
||||
|
||||
const sanitizedQuotas = normalizeUsageQuotasForProvider(provider, usage.quotas);
|
||||
return sanitizedQuotas === usage.quotas ? usage : { ...usage, quotas: sanitizedQuotas };
|
||||
}
|
||||
104
tests/unit/providerlimits-quotanormalize-split.test.ts
Normal file
104
tests/unit/providerlimits-quotanormalize-split.test.ts
Normal file
@@ -0,0 +1,104 @@
|
||||
/**
|
||||
* Characterization + API-surface test: providerLimits.ts god-file decomposition.
|
||||
*
|
||||
* The pure quota-key/quota-value normalization helpers (+ the isRecord type
|
||||
* guard they share) were extracted verbatim from src/lib/usage/providerLimits.ts
|
||||
* into the self-contained leaf src/lib/usage/providerLimits/quotaNormalize.ts
|
||||
* (no DB, no network — the leaf does NOT import the host, so no cycle). The sync
|
||||
* orchestration / DB / network code stays in the host.
|
||||
*
|
||||
* Verifies that:
|
||||
* 1. isRecord + isUsageQuotaKeyAllowed behave correctly (DB-free).
|
||||
* 2. The host providerLimits.ts still exposes the FULL public API (13 funcs).
|
||||
* 3. The quotaNormalize leaf exports its helpers directly.
|
||||
*
|
||||
* Deeper quota-sanitization behaviour is covered by the existing
|
||||
* provider-limits-sanitize-scope-3821 / db-provider-limits suites; this test
|
||||
* pins the extraction boundary + the simplest pure branches.
|
||||
*/
|
||||
|
||||
import { describe, it } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
import {
|
||||
isRecord,
|
||||
isUsageQuotaKeyAllowed,
|
||||
normalizeUsageQuotasForProvider,
|
||||
sanitizeUsageQuotasForProvider,
|
||||
} from "../../src/lib/usage/providerLimits/quotaNormalize.ts";
|
||||
|
||||
describe("providerLimits/quotaNormalize — isRecord", () => {
|
||||
it("is true only for plain non-null, non-array objects", () => {
|
||||
assert.equal(isRecord({ a: 1 }), true);
|
||||
assert.equal(isRecord({}), true);
|
||||
assert.equal(isRecord(null), false);
|
||||
assert.equal(isRecord([1, 2]), false);
|
||||
assert.equal(isRecord("x"), false);
|
||||
assert.equal(isRecord(5), false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("providerLimits/quotaNormalize — isUsageQuotaKeyAllowed", () => {
|
||||
it("always allows the generic 'credits' and 'models' keys", () => {
|
||||
assert.equal(isUsageQuotaKeyAllowed("openai", "credits"), true);
|
||||
assert.equal(isUsageQuotaKeyAllowed("anthropic", "models"), true);
|
||||
assert.equal(isUsageQuotaKeyAllowed("antigravity", "credits"), true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("providerLimits/quotaNormalize — sanitize/normalize are callable & pure-shaped", () => {
|
||||
it("sanitizeUsageQuotasForProvider returns a record (no throw on a plain usage object)", () => {
|
||||
const usage = { quotas: { credits: { used: 1, limit: 10 } } };
|
||||
const out = sanitizeUsageQuotasForProvider("openai", usage);
|
||||
assert.equal(isRecord(out), true);
|
||||
});
|
||||
it("normalizeUsageQuotasForProvider is exported and callable", () => {
|
||||
assert.equal(typeof normalizeUsageQuotasForProvider, "function");
|
||||
});
|
||||
});
|
||||
|
||||
// ── host public API surface ──────────────────────────────────────────────────
|
||||
|
||||
const host = await import("../../src/lib/usage/providerLimits.ts");
|
||||
|
||||
describe("providerLimits.ts public API surface (13 functions)", () => {
|
||||
const expected = [
|
||||
"fetchAndPersistProviderLimits",
|
||||
"fetchLiveProviderLimits",
|
||||
"getCachedProviderLimitsMap",
|
||||
"getLastProviderLimitsAutoSyncTime",
|
||||
"getProviderLimitsSyncIntervalMinutes",
|
||||
"getProviderLimitsSyncIntervalMs",
|
||||
"getProviderLimitsSyncSpacingMs",
|
||||
"getSanitizedCachedProviderLimitsMap",
|
||||
"notifyProviderUsageRecorded",
|
||||
"quotaPathShouldMarkExpired",
|
||||
"refreshAndUpdateCredentials",
|
||||
"shouldAttemptRotatingRefresh",
|
||||
"syncAllProviderLimits",
|
||||
];
|
||||
for (const name of expected) {
|
||||
it(`exposes ${name} as a function`, () => {
|
||||
assert.equal(typeof host[name], "function", `${name} must be a function on the host`);
|
||||
});
|
||||
}
|
||||
it("loses no public function in the split", () => {
|
||||
const missing = expected.filter((n) => typeof host[n] !== "function");
|
||||
assert.deepEqual(missing, [], `missing: ${missing.join(", ")}`);
|
||||
});
|
||||
});
|
||||
|
||||
describe("quotaNormalize.ts exports its helpers directly", () => {
|
||||
it("the moved helpers are functions on the leaf", async () => {
|
||||
const qn = await import("../../src/lib/usage/providerLimits/quotaNormalize.ts");
|
||||
for (const fn of [
|
||||
"isRecord",
|
||||
"isUsageQuotaKeyAllowed",
|
||||
"normalizeUsageQuotaKey",
|
||||
"normalizeUsageQuotasForProvider",
|
||||
"sanitizeUsageQuotasForProvider",
|
||||
]) {
|
||||
assert.equal(typeof qn[fn], "function", fn);
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user