fix(usage): synchronize Keeper metrics and improve detail refresh

- Align TPS and token normalization with Keeper
- Add Redis usage queue batch pulling with HTTP fallback
- Prevent cache and invalid token double counting
- Refresh usage details immediately after new records arrive
This commit is contained in:
lzt404 committed 2026-09-15 02:39:03 +08:00
1 parent 19a9b4fc09
commit 32b55920af
9 files changed
+1188 -185

No files matched your search

+2 -2
View File
@@ -414,7 +414,7 @@ export const jaOverrides = {
'usage.stat.averageLatency': '平均レイテンシ',
'usage.stat.tps': 'TPS',
'usage.stat.performanceMeta': '有効サンプル {samples} 件 · RPM {rpm}',
'usage.stat.performanceMetaTitle': 'TPS {tps}、有効サンプル {samples} 件、RPM {rpm}、平均レイテンシ {latency} ms',
'usage.stat.performanceMetaTitle': 'TPS {tps}(総レイテンシで加重)、有効サンプル {samples} 件、RPM {rpm}、平均レイテンシ {latency} ms',
'usage.stat.cacheHitRate': 'キャッシュヒット率',
'usage.stat.cacheHitMeta': 'キャッシュ読取 {hit} · 入力 {input}',
'usage.stat.cacheHitMetaTitle': 'キャッシュヒット率 {rate}%、読取 {hit} Token、入力 {input} Token',
@@ -498,7 +498,7 @@ export const jaOverrides = {
'usage.column.cache': 'キャッシュ',
'usage.column.cacheRate': 'キャッシュ率',
'usage.column.total': '合計',
'usage.column.speed': '生成速度',
'usage.column.speed': '出力速度',
'usage.key.noRemark': 'メモなし',
'usage.previous': '前へ',
'usage.next': '次へ',
+2 -2
View File
@@ -401,7 +401,7 @@ export const en: Record<MessageKey, string> = {
'usage.stat.averageLatency': 'Average Latency',
'usage.stat.tps': 'TPS',
'usage.stat.performanceMeta': '{samples} samples · RPM {rpm}',
'usage.stat.performanceMetaTitle': 'TPS {tps}, {samples} valid samples, RPM {rpm}, average latency {latency} ms',
'usage.stat.performanceMetaTitle': 'TPS {tps} (weighted by total latency), {samples} valid samples, RPM {rpm}, average latency {latency} ms',
'usage.stat.cacheHitRate': 'Cache Hit Rate',
'usage.stat.cacheHitMeta': 'Cache read {hit} · Input {input}',
'usage.stat.cacheHitMetaTitle': 'Cache hit rate {rate}%, cache read {hit} tokens, input {input} tokens',
@@ -486,7 +486,7 @@ export const en: Record<MessageKey, string> = {
'usage.column.cache': 'Cache',
'usage.column.cacheRate': 'Cache Rate',
'usage.column.total': 'Total',
'usage.column.speed': 'Generation Speed',
'usage.column.speed': 'Output Speed',
'usage.key.noRemark': 'No remark',
'usage.previous': 'Previous',
'usage.next': 'Next',
+2 -2
View File
@@ -400,7 +400,7 @@ export const zhCN = {
'usage.stat.averageLatency': '平均延迟',
'usage.stat.tps': 'TPS',
'usage.stat.performanceMeta': '{samples} 条有效样本 · RPM {rpm}',
'usage.stat.performanceMetaTitle': 'TPS {tps},{samples} 条有效样本,RPM {rpm},平均延迟 {latency} ms',
'usage.stat.performanceMetaTitle': 'TPS {tps}(按总延迟加权),{samples} 条有效样本,RPM {rpm},平均延迟 {latency} ms',
'usage.stat.cacheHitRate': '缓存命中率',
'usage.stat.cacheHitMeta': '缓存读取 {hit} · 输入 {input}',
'usage.stat.cacheHitMetaTitle': '缓存命中率 {rate}%,缓存读取 {hit} Token,输入 {input} Token',
@@ -485,7 +485,7 @@ export const zhCN = {
'usage.column.cache': '缓存',
'usage.column.cacheRate': '缓存率',
'usage.column.total': '总计',
'usage.column.speed': '生成速度',
'usage.column.speed': '输出速度',
'usage.key.noRemark': '未备注',
'usage.previous': '上一页',
'usage.next': '下一页',
+3 -4
View File
@@ -400,9 +400,9 @@ export function UsageRecordsPage() {
);
const loadData = useCallback(
(quiet = false) => {
(quiet = false, immediate = !quiet) => {
if (!quiet) setLoading(true);
return schedulerRef.current!.schedule(() => executeLoadData(quiet), !quiet);
return schedulerRef.current!.schedule(() => executeLoadData(quiet), immediate);
},
[executeLoadData],
);
@@ -419,7 +419,7 @@ export function UsageRecordsPage() {
let disposed = false;
let unlisten: (() => void) | null = null;
const refresh = () => {
if (!disposed && !document.hidden) void loadData(true);
if (!disposed && !document.hidden) void loadData(true, true);
};
listen('usage-records-updated', refresh)
.then((stop) => {
@@ -1440,7 +1440,6 @@ function UsageEventCell({
const value = formatGenerationSpeed({
outputTokens: record.tokens.output_tokens,
latencyMs: record.latency_ms,
ttftMs: record.ttft_ms,
});
return <td className="usage-td-speed align-center" title={value === '—' ? undefined : value}>{value}</td>;
}
+2 -7
View File
@@ -1,7 +1,6 @@
type GenerationSpeedInput = {
outputTokens: number;
latencyMs: number;
ttftMs: number | null;
};
type CacheReadRateInput = {
@@ -12,21 +11,17 @@ type CacheReadRateInput = {
export const calculateGenerationSpeed = ({
outputTokens,
latencyMs,
ttftMs,
}: GenerationSpeedInput): number | null => {
if (
!Number.isFinite(outputTokens) ||
!Number.isFinite(latencyMs) ||
ttftMs === null ||
!Number.isFinite(ttftMs) ||
outputTokens <= 0 ||
ttftMs <= 0 ||
latencyMs <= ttftMs
latencyMs <= 0
) {
return null;
}
const speed = outputTokens / ((latencyMs - ttftMs) / 1_000);
const speed = outputTokens / (latencyMs / 1_000);
return Number.isFinite(speed) && speed > 0 ? speed : null;
};