feat(usage): show cancelled and incomplete requests separately

Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
Zyy
2026-11-06 11:04:07 +08:00
co-authored by Codex
parent 2b7a64dae6
commit 4527db7b5d
4 changed files with 22 additions and 53 deletions
+1 -2
View File
@@ -23,8 +23,7 @@
"empty": "No requests in this range",
"errors": "Errors",
"noSamples": "No samples",
"completionRate": "Completion",
"abnormalEnd": "Abnormal"
"completionRate": "Completion"
},
"billing": {
"serviceLevel": "Service tier",
+1 -2
View File
@@ -23,8 +23,7 @@
"empty": "当前范围暂无请求",
"errors": "错误",
"noSamples": "无样本",
"completionRate": "完成率",
"abnormalEnd": "非正常结束"
"completionRate": "完成率"
},
"billing": {
"serviceLevel": "服务档位",
+1 -2
View File
@@ -23,8 +23,7 @@
"empty": "當前範圍暫無請求",
"errors": "錯誤",
"noSamples": "無樣本",
"completionRate": "完成率",
"abnormalEnd": "非正常結束"
"completionRate": "完成率"
},
"billing": {
"serviceLevel": "服務檔位",
@@ -1,5 +1,5 @@
<script setup lang="ts">
import type { BarSeriesOption, EChartsOption, LineSeriesOption } from 'echarts'
import type { EChartsOption } from 'echarts'
import type { getUsageRecordInsightsOverview } from '@/api'
import { ZCard, ZEmpty } from '@codex-proxy/ui'
@@ -16,7 +16,6 @@ import { formatLocalizedCompactNumber as formatCompactNumber, formatPercent } fr
import {
usageCategoryAxis,
usageGapAwareLineSeries,
usageLegend,
usageTooltip,
usageTooltipItem,
@@ -67,28 +66,12 @@ const description = computed(() => props.loading || !granularityText.value
const chartOption = computed<EChartsOption>(() => {
const theme = palette.value
const chartPoints = points.value
const activePointCount = chartPoints.filter(point => requestCount(point) > 0).length
const successRateSeries = usageGapAwareLineSeries(
t('usage.health.serviceSuccessRate'),
chartPoints.map(serviceSuccessRate),
theme.success,
{ yAxisIndex: 1 },
)
const series: Array<BarSeriesOption | LineSeriesOption> = [
const series = [
outcomeSeries(t('common.status.success'), chartPoints.map(point => point.successRequests), theme.success),
outcomeSeries(t('usage.health.point.serviceFailed'), chartPoints.map(point => point.failedRequests), theme.danger),
outcomeSeries(t('usage.status.cancelled'), chartPoints.map(point => point.cancelledRequests), theme.info),
outcomeSeries(t('usage.health.notCompleted'), chartPoints.map(point => point.incompleteRequests), theme.warning),
outcomeSeries(t('usage.health.point.callerError'), chartPoints.map(point => point.callerErrorRequests), theme.normal),
...successRateSeries.map((item, index) => index === 0
? {
...item,
symbol: activePointCount <= 16
? 'circle'
: 'none',
symbolSize: 4,
}
: item),
]
return {
@@ -101,27 +84,13 @@ const chartOption = computed<EChartsOption>(() => {
outerBoundsMode: 'same',
outerBoundsContain: 'axisLabel',
},
legend: usageLegend(theme, [
t('common.status.success'),
t('usage.health.point.serviceFailed'),
t('usage.status.cancelled'),
t('usage.health.notCompleted'),
t('usage.health.point.callerError'),
t('usage.health.serviceSuccessRate'),
]),
legend: usageLegend(theme, series.map(item => item.name)),
tooltip: usageTooltip(theme, formatTooltip),
xAxis: usageCategoryAxis(
chartPoints.map(point => point.label),
theme,
),
yAxis: [
usageValueAxis(theme, value => formatCompactNumber(value)),
usageValueAxis(theme, value => formatPercent(value), {
min: 0,
max: 1,
splitLine: false,
}),
],
yAxis: usageValueAxis(theme, value => formatCompactNumber(value)),
series,
}
})
@@ -130,10 +99,10 @@ function requestCount(point: HealthPoint) {
return Math.max(0, point.totalRequests ?? 0)
}
function serviceSuccessRate(point: HealthPoint) {
return point.errorRate == null
? null
: 1 - point.errorRate
function serviceSuccessRateText(rate: number | null) {
return rate == null
? t('usage.noValidSample')
: formatPercent(rate)
}
function outcomeSeries(name: string, data: number[], color: string) {
@@ -153,7 +122,6 @@ function formatTooltip(params: unknown) {
if (!point)
return ''
const successRate = serviceSuccessRate(point)
const theme = palette.value
return chartTooltipContent(point.label, [
@@ -165,9 +133,7 @@ function formatTooltip(params: unknown) {
usageTooltipItem(t('usage.health.point.callerError'), formatCompactNumber(point.callerErrorRequests), theme.normal),
usageTooltipItem(
t('usage.health.serviceSuccessRate'),
successRate == null
? t('usage.health.noSamples')
: formatPercent(successRate),
serviceSuccessRateText(point.errorRate == null ? null : 1 - point.errorRate),
theme.success,
),
])
@@ -183,11 +149,11 @@ function formatTooltip(params: unknown) {
>
<template #body>
<div class="grid min-h-66" :class="hasData ? 'gap-3' : 'h-full'">
<div v-if="hasData && health" class="grid grid-cols-3 gap-2 rounded-xl bg-z-fill-quaternary/45 p-2">
<div v-if="hasData && health" class="grid grid-cols-2 gap-2 rounded-xl bg-z-fill-quaternary/45 p-2 sm:grid-cols-4">
<div class="grid gap-1 px-2">
<span class="text-[10px] font-bold text-z-text-quaternary">{{ t('usage.health.serviceSuccessRate') }}</span>
<strong class="font-mono text-z-base font-heavy tabular-nums text-z-green-text">
{{ formatPercent(health.successRate) }}
{{ serviceSuccessRateText(health.successRate) }}
</strong>
</div>
<div class="grid gap-1 px-2">
@@ -197,9 +163,15 @@ function formatTooltip(params: unknown) {
</strong>
</div>
<div class="grid gap-1 px-2">
<span class="text-[10px] font-bold text-z-text-quaternary">{{ t('usage.health.abnormalEnd') }}</span>
<span class="text-[10px] font-bold text-z-text-quaternary">{{ t('usage.status.cancelled') }}</span>
<strong class="font-mono text-z-base font-heavy tabular-nums text-z-blue-text">
{{ formatCompactNumber(health.cancelledRequests) }}
</strong>
</div>
<div class="grid gap-1 px-2">
<span class="text-[10px] font-bold text-z-text-quaternary">{{ t('usage.health.notCompleted') }}</span>
<strong class="font-mono text-z-base font-heavy tabular-nums text-z-orange-text">
{{ formatCompactNumber(health.cancelledRequests + health.incompleteRequests) }}
{{ formatCompactNumber(health.incompleteRequests) }}
</strong>
</div>
</div>