Files
new-api-analytics/lib/query-rankings.ts

146 lines
5.2 KiB
TypeScript

import { query } from "./db";
import { quotaToUsd } from "./metrics";
import { CACHE_CREATION, CACHE_READ, REAL_MODEL, cacheKey, cached, getChannelNames, getDisplayNames, timeWhere } from "./query-shared";
// ── 排名 ──────────────────────────────────────────────────────
export interface RankingItem {
rank: number;
name: string;
id?: number;
calls: number;
prompt_tokens: number;
completion_tokens: number;
cache_creation_tokens: number;
cache_read_tokens: number;
total_tokens: number;
quota: number;
quota_usd: number;
}
export function getUserRanking(
startTs?: number,
endTs?: number,
limit = 50
): Promise<RankingItem[]> {
return cached(cacheKey("userRank", startTs, endTs, limit), async () => {
const params: (string | number | boolean | null)[] = [];
const where = timeWhere(params, startTs, endTs);
params.push(limit);
const displayNames = await getDisplayNames();
const rows = await query(
`SELECT user_id, username,
COUNT(*)::int as calls,
COALESCE(SUM(prompt_tokens), 0)::bigint as prompt,
COALESCE(SUM(completion_tokens), 0)::bigint as completion,
COALESCE(SUM(${CACHE_CREATION}), 0)::bigint as cache_creation,
COALESCE(SUM(${CACHE_READ}), 0)::bigint as cache_read,
COALESCE(SUM(quota), 0)::bigint as quota
FROM logs WHERE ${where}
GROUP BY user_id, username
ORDER BY COALESCE(SUM(prompt_tokens),0) + COALESCE(SUM(completion_tokens),0) + COALESCE(SUM(${CACHE_CREATION}),0) + COALESCE(SUM(${CACHE_READ}),0) DESC
LIMIT $${params.length}`,
params
);
return rows.map((r, i) => ({
rank: i + 1,
name: displayNames[r.user_id] || r.username,
username: r.username as string,
id: Number(r.user_id),
calls: Number(r.calls),
prompt_tokens: Number(r.prompt),
completion_tokens: Number(r.completion),
cache_creation_tokens: Number(r.cache_creation),
cache_read_tokens: Number(r.cache_read),
total_tokens: Number(r.prompt) + Number(r.completion) + Number(r.cache_creation) + Number(r.cache_read),
quota: Number(r.quota),
quota_usd: quotaToUsd(Number(r.quota)),
}));
});
}
export function getModelRanking(
startTs?: number,
endTs?: number,
limit = 50
): Promise<RankingItem[]> {
return cached(cacheKey("modelRank", startTs, endTs, limit), async () => {
const params: (string | number | boolean | null)[] = [];
const where = timeWhere(params, startTs, endTs);
params.push(limit);
const rows = await query(
`SELECT ${REAL_MODEL} as model,
COUNT(*)::int as calls,
COALESCE(SUM(prompt_tokens), 0)::bigint as prompt,
COALESCE(SUM(completion_tokens), 0)::bigint as completion,
COALESCE(SUM(${CACHE_CREATION}), 0)::bigint as cache_creation,
COALESCE(SUM(${CACHE_READ}), 0)::bigint as cache_read,
COALESCE(SUM(quota), 0)::bigint as quota
FROM logs WHERE ${where}
GROUP BY model
ORDER BY COALESCE(SUM(prompt_tokens),0) + COALESCE(SUM(completion_tokens),0) + COALESCE(SUM(${CACHE_CREATION}),0) + COALESCE(SUM(${CACHE_READ}),0) DESC
LIMIT $${params.length}`,
params
);
return rows.map((r, i) => ({
rank: i + 1,
name: r.model || "(unknown)",
calls: Number(r.calls),
prompt_tokens: Number(r.prompt),
completion_tokens: Number(r.completion),
cache_creation_tokens: Number(r.cache_creation),
cache_read_tokens: Number(r.cache_read),
total_tokens: Number(r.prompt) + Number(r.completion) + Number(r.cache_creation) + Number(r.cache_read),
quota: Number(r.quota),
quota_usd: quotaToUsd(Number(r.quota)),
}));
});
}
export function getChannelRanking(
startTs?: number,
endTs?: number,
limit = 50
): Promise<RankingItem[]> {
return cached(cacheKey("channelRank", startTs, endTs, limit), async () => {
const params: (string | number | boolean | null)[] = [];
const where = timeWhere(params, startTs, endTs);
params.push(limit);
const channelNames = await getChannelNames();
const rows = await query(
`SELECT channel_id,
COUNT(*)::int as calls,
COALESCE(SUM(prompt_tokens), 0)::bigint as prompt,
COALESCE(SUM(completion_tokens), 0)::bigint as completion,
COALESCE(SUM(${CACHE_CREATION}), 0)::bigint as cache_creation,
COALESCE(SUM(${CACHE_READ}), 0)::bigint as cache_read,
COALESCE(SUM(quota), 0)::bigint as quota
FROM logs WHERE ${where}
GROUP BY channel_id
ORDER BY COALESCE(SUM(prompt_tokens),0) + COALESCE(SUM(completion_tokens),0) + COALESCE(SUM(${CACHE_CREATION}),0) + COALESCE(SUM(${CACHE_READ}),0) DESC
LIMIT $${params.length}`,
params
);
return rows.map((r, i) => ({
rank: i + 1,
name: channelNames[r.channel_id] || `已删除(${r.channel_id})`,
id: Number(r.channel_id),
calls: Number(r.calls),
prompt_tokens: Number(r.prompt),
completion_tokens: Number(r.completion),
cache_creation_tokens: Number(r.cache_creation),
cache_read_tokens: Number(r.cache_read),
total_tokens: Number(r.prompt) + Number(r.completion) + Number(r.cache_creation) + Number(r.cache_read),
quota: Number(r.quota),
quota_usd: quotaToUsd(Number(r.quota)),
}));
});
}