mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-09-15 11:22:15 +03:00
feat(quota): Moonshot Open Platform balance and TPD lock for custom nodes (#12590)
Validado em lote numa worktree combinada com os 9 PRs desta leva sobre o tip de `release/v3.8.51`: `typecheck:core` limpo, `check:provider-consistency` OK (273 entradas REGISTRY, **356** providers canônicos), `check-docs-counts-sync` exit 0 e **300/300** nos testes que a leva toca. O crescimento de arquivo que os PRs empilham uns sobre os outros foi rebaselinado num único registro datado (`_rebaseline_2026_09_03_houminxi_batch`), com a decomposição por arquivo: `providers/page.tsx` +18 (import CSV do #12504 + busca do #12495 no mesmo painel), `accountFallback.ts` +6 (o #12566 sobre o rebaseline que o #12590 já registrou — os dois tocam `checkFallbackError`) e `chatCore.ts` +3 (invalidez de cache de quota no 429 do #12325). As violações restantes (`codex.ts`, `stream.ts`) foram medidas também no tip puro e são drift da base, não desta leva.
This commit is contained in:
@@ -91,6 +91,14 @@ const QUOTA_PATTERNS: ReadonlyArray<RegExp> = [
|
||||
// Trailing punctuation/whitespace before the closing quote is tolerated
|
||||
// because real API responses may include a period or trailing space.
|
||||
/"error"\s*:\s*"usage limit reached[.\s]*"/i,
|
||||
|
||||
// Moonshot Open Platform organization TPD (tokens-per-day). Live body:
|
||||
// "request reached organization TPD rate limit, current: N, limit: M".
|
||||
// Do not use a bare /TPD/ — too wide. Limit is read from the body, never
|
||||
// hardcoded (Tier0=1.5M, Tier1+=unlimited).
|
||||
/organization TPD rate limit/i,
|
||||
/\bTPD rate limit\b/i,
|
||||
/insufficient balance/i,
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -155,6 +163,9 @@ const TERMINAL_QUOTA_PATTERNS: ReadonlyArray<RegExp> = [
|
||||
/individual quota reached/i,
|
||||
/enable overages/i,
|
||||
/daily free allocation/i,
|
||||
/organization TPD rate limit/i,
|
||||
/\bTPD rate limit\b/i,
|
||||
/insufficient balance/i,
|
||||
];
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,13 +1,23 @@
|
||||
import { USAGE_SUPPORTED_PROVIDERS } from "@/shared/constants/providers";
|
||||
import { isMoonshotOpenPlatformConnection } from "@omniroute/open-sse/services/usage/moonshotOpenPlatform.ts";
|
||||
|
||||
export interface ProviderQuotaVisibilityConnection {
|
||||
quotaVisible?: boolean;
|
||||
provider?: string;
|
||||
providerSpecificData?: unknown;
|
||||
}
|
||||
|
||||
export function isProviderQuotaVisible(connection: ProviderQuotaVisibilityConnection): boolean {
|
||||
return connection.quotaVisible !== false;
|
||||
}
|
||||
|
||||
export function supportsProviderQuota(providerId: string): boolean {
|
||||
return USAGE_SUPPORTED_PROVIDERS.includes(providerId);
|
||||
export function supportsProviderQuota(
|
||||
providerId: string,
|
||||
connection?: { provider?: string; providerSpecificData?: unknown },
|
||||
): boolean {
|
||||
if (USAGE_SUPPORTED_PROVIDERS.includes(providerId)) return true;
|
||||
return isMoonshotOpenPlatformConnection({
|
||||
provider: providerId,
|
||||
providerSpecificData: connection?.providerSpecificData,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -22,16 +22,36 @@ import {
|
||||
isReservedProviderPrefix,
|
||||
reservedProviderPrefixMessage,
|
||||
} from "@/shared/constants/reservedProviderPrefixes";
|
||||
|
||||
import {
|
||||
isValidIanaTimeZone,
|
||||
isValidResetHour,
|
||||
} from "@omniroute/open-sse/services/dailyQuotaReset.ts";
|
||||
import {
|
||||
upstreamHeadersRecordSchema,
|
||||
modelCompatPerProtocolSchema,
|
||||
customHeadersSchema,
|
||||
} from "./misc.ts";
|
||||
import { isValidProviderIconUrl } from "@/shared/validation/iconUrl";
|
||||
|
||||
export { validateProviderSpecificData };
|
||||
|
||||
import { isValidProviderIconUrl } from "@/shared/validation/iconUrl";
|
||||
const dailyQuotaResetTimezoneSchema = z
|
||||
.string()
|
||||
.trim()
|
||||
.optional()
|
||||
.or(z.literal(""))
|
||||
.refine((value) => !value || isValidIanaTimeZone(value), {
|
||||
message: "Unknown IANA timezone",
|
||||
});
|
||||
|
||||
const dailyQuotaResetHourSchema = z
|
||||
.number()
|
||||
.int()
|
||||
.optional()
|
||||
.nullable()
|
||||
.refine((value) => value == null || isValidResetHour(value), {
|
||||
message: "Hour must be 0-23",
|
||||
});
|
||||
|
||||
// ──── Provider Schemas ────
|
||||
|
||||
@@ -337,6 +357,8 @@ export const createProviderNodeSchema = z
|
||||
// isValidProviderIconUrl (2000 chars for http(s), 256 KiB for data:image).
|
||||
iconUrl: providerNodeIconUrlSchema,
|
||||
customHeaders: customHeadersSchema,
|
||||
dailyQuotaResetTimezone: dailyQuotaResetTimezoneSchema,
|
||||
dailyQuotaResetHour: dailyQuotaResetHourSchema,
|
||||
})
|
||||
.superRefine((value, ctx) => {
|
||||
const nodeType = value.type || "openai-compatible";
|
||||
@@ -419,6 +441,8 @@ export const updateProviderNodeSchema = z
|
||||
// clears a previously stored custom icon.
|
||||
iconUrl: providerNodeIconUrlSchema,
|
||||
customHeaders: customHeadersSchema,
|
||||
dailyQuotaResetTimezone: dailyQuotaResetTimezoneSchema,
|
||||
dailyQuotaResetHour: dailyQuotaResetHourSchema,
|
||||
})
|
||||
.superRefine((value, ctx) => {
|
||||
// Reserved-prefix guard (tokenrouter bug) — same rationale as the guard in
|
||||
|
||||
Reference in New Issue
Block a user