146 lines
5.2 KiB
TypeScript
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)),
|
|
}));
|
|
});
|
|
}
|