79 lines
2.4 KiB
TypeScript
79 lines
2.4 KiB
TypeScript
import { loadConfig } from "../../config/config.js";
|
|
import type { CostUsageSummary } from "../../infra/session-cost-usage.js";
|
|
import { loadCostUsageSummary } from "../../infra/session-cost-usage.js";
|
|
import { loadProviderUsageSummary } from "../../infra/provider-usage.js";
|
|
import type { GatewayRequestHandlers } from "./types.js";
|
|
|
|
const COST_USAGE_CACHE_TTL_MS = 30_000;
|
|
|
|
type CostUsageCacheEntry = {
|
|
summary?: CostUsageSummary;
|
|
updatedAt?: number;
|
|
inFlight?: Promise<CostUsageSummary>;
|
|
};
|
|
|
|
const costUsageCache = new Map<number, CostUsageCacheEntry>();
|
|
|
|
const parseDays = (raw: unknown): number => {
|
|
if (typeof raw === "number" && Number.isFinite(raw)) return Math.floor(raw);
|
|
if (typeof raw === "string" && raw.trim() !== "") {
|
|
const parsed = Number(raw);
|
|
if (Number.isFinite(parsed)) return Math.floor(parsed);
|
|
}
|
|
return 30;
|
|
};
|
|
|
|
async function loadCostUsageSummaryCached(params: {
|
|
days: number;
|
|
config: ReturnType<typeof loadConfig>;
|
|
}): Promise<CostUsageSummary> {
|
|
const days = Math.max(1, params.days);
|
|
const now = Date.now();
|
|
const cached = costUsageCache.get(days);
|
|
if (cached?.summary && cached.updatedAt && now - cached.updatedAt < COST_USAGE_CACHE_TTL_MS) {
|
|
return cached.summary;
|
|
}
|
|
|
|
if (cached?.inFlight) {
|
|
if (cached.summary) return cached.summary;
|
|
return await cached.inFlight;
|
|
}
|
|
|
|
const entry: CostUsageCacheEntry = cached ?? {};
|
|
const inFlight = loadCostUsageSummary({ days, config: params.config })
|
|
.then((summary) => {
|
|
costUsageCache.set(days, { summary, updatedAt: Date.now() });
|
|
return summary;
|
|
})
|
|
.catch((err) => {
|
|
if (entry.summary) return entry.summary;
|
|
throw err;
|
|
})
|
|
.finally(() => {
|
|
const current = costUsageCache.get(days);
|
|
if (current?.inFlight === inFlight) {
|
|
current.inFlight = undefined;
|
|
costUsageCache.set(days, current);
|
|
}
|
|
});
|
|
|
|
entry.inFlight = inFlight;
|
|
costUsageCache.set(days, entry);
|
|
|
|
if (entry.summary) return entry.summary;
|
|
return await inFlight;
|
|
}
|
|
|
|
export const usageHandlers: GatewayRequestHandlers = {
|
|
"usage.status": async ({ respond }) => {
|
|
const summary = await loadProviderUsageSummary();
|
|
respond(true, summary, undefined);
|
|
},
|
|
"usage.cost": async ({ respond, params }) => {
|
|
const config = loadConfig();
|
|
const days = parseDays(params?.days);
|
|
const summary = await loadCostUsageSummaryCached({ days, config });
|
|
respond(true, summary, undefined);
|
|
},
|
|
};
|