mirror of
https://github.com/Nezumi-2711/9router.git
synced 2026-09-22 13:38:31 +00:00
feat: implement the feature for checking quota of orbit provider
This commit is contained in:
@@ -12,6 +12,7 @@ import { getKiroUsage } from "./usage/kiro.js";
|
||||
import { getMiniMaxUsage } from "./usage/minimax.js";
|
||||
import { getCodeBuddyCnUsage } from "./usage/codebuddy-cn.js";
|
||||
import { getGrokCliUsage } from "./usage/grok-cli.js";
|
||||
import { getOrbitUsage } from "./usage/orbit.js";
|
||||
import {
|
||||
getQwenUsage,
|
||||
getIflowUsage,
|
||||
@@ -45,6 +46,7 @@ const USAGE_HANDLERS = {
|
||||
"vercel-ai-gateway": (c) => getVercelAiGatewayUsage(c.apiKey, c.proxyOptions),
|
||||
"codebuddy-cn": (c) => getCodeBuddyCnUsage(c.accessToken, c.apiKey, c.providerSpecificData, c.proxyOptions),
|
||||
"grok-cli": (c) => getGrokCliUsage(c.accessToken, c.providerSpecificData, c.proxyOptions),
|
||||
"orbit-provider": (c) => getOrbitUsage(c.apiKey, c.proxyOptions),
|
||||
};
|
||||
|
||||
export async function getUsageForProvider(connection, proxyOptions = null) {
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
/**
|
||||
* Orbit Provider usage handler.
|
||||
*/
|
||||
|
||||
import { proxyAwareFetch } from "../../utils/proxyFetch.js";
|
||||
import { U, parseResetTime, toFiniteNumber } from "./shared.js";
|
||||
|
||||
const ORBIT_USAGE_URL = U("orbit-provider").url;
|
||||
|
||||
function clampPercentage(value) {
|
||||
return Math.min(100, Math.max(0, value));
|
||||
}
|
||||
|
||||
function getRemainingPercentage(used, total, upstreamUsagePercent) {
|
||||
if (total <= 0) return 0;
|
||||
const usagePercent = Number.isFinite(Number(upstreamUsagePercent))
|
||||
? toFiniteNumber(upstreamUsagePercent)
|
||||
: (used / total) * 100;
|
||||
return clampPercentage(100 - usagePercent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize Orbit's usage response into quota rows consumed by the dashboard.
|
||||
* @param {object} responseBody Orbit API response.
|
||||
* @returns {object} Normalized provider usage.
|
||||
*/
|
||||
export function parseOrbitUsage(responseBody) {
|
||||
if (responseBody?.success !== true || !responseBody?.data || typeof responseBody.data !== "object") {
|
||||
return { message: "Orbit Provider usage response was invalid." };
|
||||
}
|
||||
|
||||
const data = responseBody.data;
|
||||
const used = Math.max(0, toFiniteNumber(data.tokensUsed));
|
||||
const total = Math.max(0, toFiniteNumber(data.tokenLimit));
|
||||
const upstreamRemaining = Math.max(0, toFiniteNumber(data.tokensRemaining, Math.max(0, total - used)));
|
||||
const remaining = data.isExhausted === true ? 0 : Math.min(total, upstreamRemaining);
|
||||
const remainingPercentage = data.isExhausted === true
|
||||
? 0
|
||||
: getRemainingPercentage(used, total, data.usagePercent);
|
||||
const resetPeriod = typeof data.resetPeriod === "string" && data.resetPeriod.trim()
|
||||
? data.resetPeriod.trim().toLowerCase()
|
||||
: "period";
|
||||
const resetAt = parseResetTime(data.periodEnd);
|
||||
const quotas = {
|
||||
[`Tokens (${resetPeriod})`]: {
|
||||
used,
|
||||
total,
|
||||
remaining,
|
||||
remainingPercentage,
|
||||
resetAt,
|
||||
unlimited: false,
|
||||
},
|
||||
};
|
||||
|
||||
const credit = data.credit;
|
||||
if (credit && typeof credit === "object") {
|
||||
const balance = Math.max(0, toFiniteNumber(credit.balanceUsd));
|
||||
const granted = Math.max(0, toFiniteNumber(credit.grantedUsd));
|
||||
const spent = Math.max(0, toFiniteNumber(credit.spentUsd));
|
||||
const creditTotal = granted > 0 ? granted : balance + spent;
|
||||
const creditRemainingPercentage = creditTotal > 0
|
||||
? clampPercentage((balance / creditTotal) * 100)
|
||||
: 0;
|
||||
|
||||
quotas["Credit (USD)"] = {
|
||||
used: spent,
|
||||
total: creditTotal,
|
||||
remaining: balance,
|
||||
remainingPercentage: creditRemainingPercentage,
|
||||
resetAt: null,
|
||||
unlimited: false,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
plan: typeof data.plan === "string" && data.plan.trim() ? data.plan.trim() : "Unknown",
|
||||
quotas,
|
||||
isExhausted: data.isExhausted === true,
|
||||
resetPeriod,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch usage for an Orbit Provider API-key connection.
|
||||
* @param {string} apiKey Orbit API key.
|
||||
* @param {object|null} proxyOptions Connection proxy configuration.
|
||||
* @returns {Promise<object>} Normalized usage data.
|
||||
*/
|
||||
export async function getOrbitUsage(apiKey, proxyOptions = null) {
|
||||
if (!apiKey) {
|
||||
return { message: "Orbit Provider API key not available." };
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await proxyAwareFetch(ORBIT_USAGE_URL, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
Authorization: `Bearer ${apiKey}`,
|
||||
Accept: "application/json",
|
||||
},
|
||||
}, proxyOptions);
|
||||
|
||||
if (response.status === 401 || response.status === 403) {
|
||||
return { message: "Orbit Provider API key invalid or expired." };
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text().catch(() => "");
|
||||
const detail = errorText ? `: ${errorText.slice(0, 200)}` : "";
|
||||
return { message: `Orbit Provider usage API error (${response.status})${detail}` };
|
||||
}
|
||||
|
||||
const body = await response.json().catch(() => null);
|
||||
return parseOrbitUsage(body);
|
||||
} catch (error) {
|
||||
return { message: `Orbit Provider usage error: ${error.message}` };
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user