Add hourly rate limits (tokens/hour, requests/hour) and missing providers (chutes, cerebras, ollama)
This commit is contained in:
@@ -324,8 +324,9 @@ const TOPUP_PRICES = {
|
||||
const BILLING_CYCLES = ['monthly', 'yearly'];
|
||||
const SUPPORTED_CURRENCIES = ['usd', 'gbp', 'eur'];
|
||||
const MINUTE_MS = 60_000;
|
||||
const DODO_PRODUCTS_CACHE_TTL_MS = Math.max(30_000, Number(process.env.DODO_PRODUCTS_CACHE_TTL_MS || 5 * MINUTE_MS));
|
||||
const HOUR_MS = 3_600_000;
|
||||
const DAY_MS = 86_400_000;
|
||||
const DODO_PRODUCTS_CACHE_TTL_MS = Math.max(30_000, Number(process.env.DODO_PRODUCTS_CACHE_TTL_MS || 5 * MINUTE_MS));
|
||||
const FORTY_EIGHT_HOURS_MS = 48 * DAY_MS;
|
||||
const AVG_CHARS_PER_TOKEN = 4; // rough heuristic
|
||||
const MAX_JSON_BODY_SIZE = Number(process.env.MAX_JSON_BODY_SIZE || 6_000_000); // 6 MB default for JSON payloads (attachments)
|
||||
@@ -516,7 +517,7 @@ const PLAN_PRICES = {
|
||||
};
|
||||
const AUTO_MODEL_TOKEN = 'auto';
|
||||
const DEFAULT_PROVIDER_FALLBACK = 'opencode';
|
||||
const DEFAULT_PROVIDER_SEEDS = ['openrouter', 'mistral', 'google', 'groq', 'nvidia', 'chutes', DEFAULT_PROVIDER_FALLBACK];
|
||||
const DEFAULT_PROVIDER_SEEDS = ['openrouter', 'mistral', 'google', 'groq', 'nvidia', 'chutes', 'cerebras', 'ollama', DEFAULT_PROVIDER_FALLBACK];
|
||||
const PROVIDER_PERSIST_DEBOUNCE_MS = 200;
|
||||
const TOKEN_ESTIMATION_BUFFER = 400;
|
||||
const BOOST_PACK_SIZE = 500_000;
|
||||
@@ -5728,8 +5729,10 @@ function defaultProviderLimit(provider) {
|
||||
provider: normalizeProviderName(provider),
|
||||
scope: 'provider',
|
||||
tokensPerMinute: 0,
|
||||
tokensPerHour: 0,
|
||||
tokensPerDay: 0,
|
||||
requestsPerMinute: 0,
|
||||
requestsPerHour: 0,
|
||||
requestsPerDay: 0,
|
||||
perModel: {},
|
||||
};
|
||||
@@ -5771,16 +5774,20 @@ function ensureProviderLimitDefaults(provider) {
|
||||
const cfg = providerLimits.limits[key];
|
||||
cfg.scope = cfg.scope === 'model' ? 'model' : 'provider';
|
||||
cfg.tokensPerMinute = sanitizeLimitNumber(cfg.tokensPerMinute);
|
||||
cfg.tokensPerHour = sanitizeLimitNumber(cfg.tokensPerHour);
|
||||
cfg.tokensPerDay = sanitizeLimitNumber(cfg.tokensPerDay);
|
||||
cfg.requestsPerMinute = sanitizeLimitNumber(cfg.requestsPerMinute);
|
||||
cfg.requestsPerHour = sanitizeLimitNumber(cfg.requestsPerHour);
|
||||
cfg.requestsPerDay = sanitizeLimitNumber(cfg.requestsPerDay);
|
||||
cfg.perModel = cfg.perModel && typeof cfg.perModel === 'object' ? cfg.perModel : {};
|
||||
Object.keys(cfg.perModel).forEach((model) => {
|
||||
const entry = cfg.perModel[model] || {};
|
||||
cfg.perModel[model] = {
|
||||
tokensPerMinute: sanitizeLimitNumber(entry.tokensPerMinute),
|
||||
tokensPerHour: sanitizeLimitNumber(entry.tokensPerHour),
|
||||
tokensPerDay: sanitizeLimitNumber(entry.tokensPerDay),
|
||||
requestsPerMinute: sanitizeLimitNumber(entry.requestsPerMinute),
|
||||
requestsPerHour: sanitizeLimitNumber(entry.requestsPerHour),
|
||||
requestsPerDay: sanitizeLimitNumber(entry.requestsPerDay),
|
||||
};
|
||||
});
|
||||
@@ -6927,13 +6934,16 @@ function summarizeProviderUsage(provider, model) {
|
||||
const key = normalizeUsageProvider(provider, model);
|
||||
const now = Date.now();
|
||||
const minuteAgo = now - MINUTE_MS;
|
||||
const hourAgo = now - HOUR_MS;
|
||||
const dayAgo = now - DAY_MS;
|
||||
const entries = ensureProviderUsageBucket(key);
|
||||
const filterByModel = !!(model && providerLimits.limits[key] && providerLimits.limits[key].scope === 'model');
|
||||
const result = {
|
||||
tokensLastMinute: 0,
|
||||
tokensLastHour: 0,
|
||||
tokensLastDay: 0,
|
||||
requestsLastMinute: 0,
|
||||
requestsLastHour: 0,
|
||||
requestsLastDay: 0,
|
||||
perModel: {},
|
||||
};
|
||||
@@ -6943,22 +6953,31 @@ function summarizeProviderUsage(provider, model) {
|
||||
const matchesModel = !filterByModel || (entry.model && model && entry.model === model);
|
||||
const targetKey = entry.model || 'unknown';
|
||||
const isMinute = entry.ts >= minuteAgo;
|
||||
const isHour = entry.ts >= hourAgo;
|
||||
const isDay = entry.ts >= dayAgo;
|
||||
if (isMinute && matchesModel) {
|
||||
result.tokensLastMinute += Number(entry.tokens || 0);
|
||||
result.requestsLastMinute += Number(entry.requests || 0);
|
||||
}
|
||||
if (isHour && matchesModel) {
|
||||
result.tokensLastHour += Number(entry.tokens || 0);
|
||||
result.requestsLastHour += Number(entry.requests || 0);
|
||||
}
|
||||
if (isDay && matchesModel) {
|
||||
result.tokensLastDay += Number(entry.tokens || 0);
|
||||
result.requestsLastDay += Number(entry.requests || 0);
|
||||
}
|
||||
if (!result.perModel[targetKey]) {
|
||||
result.perModel[targetKey] = { tokensLastMinute: 0, tokensLastDay: 0, requestsLastMinute: 0, requestsLastDay: 0 };
|
||||
result.perModel[targetKey] = { tokensLastMinute: 0, tokensLastHour: 0, tokensLastDay: 0, requestsLastMinute: 0, requestsLastHour: 0, requestsLastDay: 0 };
|
||||
}
|
||||
if (isMinute) {
|
||||
result.perModel[targetKey].tokensLastMinute += Number(entry.tokens || 0);
|
||||
result.perModel[targetKey].requestsLastMinute += Number(entry.requests || 0);
|
||||
}
|
||||
if (isHour) {
|
||||
result.perModel[targetKey].tokensLastHour += Number(entry.tokens || 0);
|
||||
result.perModel[targetKey].requestsLastHour += Number(entry.requests || 0);
|
||||
}
|
||||
if (isDay) {
|
||||
result.perModel[targetKey].tokensLastDay += Number(entry.tokens || 0);
|
||||
result.perModel[targetKey].requestsLastDay += Number(entry.requests || 0);
|
||||
@@ -6975,8 +6994,10 @@ function isProviderLimited(provider, model) {
|
||||
const modelCfg = (cfg.scope === 'model' && model && cfg.perModel[model]) ? cfg.perModel[model] : cfg;
|
||||
const checks = [
|
||||
['tokensPerMinute', usage.tokensLastMinute, 'minute tokens'],
|
||||
['tokensPerHour', usage.tokensLastHour, 'hourly tokens'],
|
||||
['tokensPerDay', usage.tokensLastDay, 'daily tokens'],
|
||||
['requestsPerMinute', usage.requestsLastMinute, 'minute requests'],
|
||||
['requestsPerHour', usage.requestsLastHour, 'hourly requests'],
|
||||
['requestsPerDay', usage.requestsLastDay, 'daily requests'],
|
||||
];
|
||||
for (const [field, used, label] of checks) {
|
||||
@@ -14530,7 +14551,7 @@ async function handleAdminProviderLimitsPost(req, res) {
|
||||
|
||||
const targetModel = (body.model || '').trim();
|
||||
const target = cfg.scope === 'model' && targetModel ? (cfg.perModel[targetModel] = cfg.perModel[targetModel] || {}) : cfg;
|
||||
['tokensPerMinute', 'tokensPerDay', 'requestsPerMinute', 'requestsPerDay'].forEach((field) => {
|
||||
['tokensPerMinute', 'tokensPerHour', 'tokensPerDay', 'requestsPerMinute', 'requestsPerHour', 'requestsPerDay'].forEach((field) => {
|
||||
if (body[field] !== undefined) target[field] = sanitizeLimitNumber(body[field]);
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user