mirror of
https://github.com/fawney19/Aether.git
synced 2026-01-03 00:02:28 +08:00
feat(frontend-usage): enhance usage UI with first byte latency metrics
- Update usage records table to display first_byte_time_ms metrics - Improve request timeline visualization for latency tracking - Extend usage types for new timing information
This commit is contained in:
@@ -479,10 +479,25 @@ const groupedTimeline = computed<NodeGroup[]>(() => {
|
|||||||
return groups
|
return groups
|
||||||
})
|
})
|
||||||
|
|
||||||
// 计算链路总耗时(从第一个节点开始到最后一个节点结束)
|
// 计算链路总耗时(使用成功候选的 latency_ms 字段)
|
||||||
|
// 优先使用 latency_ms,因为它与 Usage.response_time_ms 使用相同的时间基准
|
||||||
|
// 避免 finished_at - started_at 带来的额外延迟(数据库操作时间)
|
||||||
const totalTraceLatency = computed(() => {
|
const totalTraceLatency = computed(() => {
|
||||||
if (!timeline.value || timeline.value.length === 0) return 0
|
if (!timeline.value || timeline.value.length === 0) return 0
|
||||||
|
|
||||||
|
// 查找成功的候选,使用其 latency_ms
|
||||||
|
const successCandidate = timeline.value.find(c => c.status === 'success')
|
||||||
|
if (successCandidate?.latency_ms != null) {
|
||||||
|
return successCandidate.latency_ms
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果没有成功的候选,查找失败但有 latency_ms 的候选
|
||||||
|
const failedWithLatency = timeline.value.find(c => c.status === 'failed' && c.latency_ms != null)
|
||||||
|
if (failedWithLatency?.latency_ms != null) {
|
||||||
|
return failedWithLatency.latency_ms
|
||||||
|
}
|
||||||
|
|
||||||
|
// 回退:使用 finished_at - started_at 计算
|
||||||
let earliestStart: number | null = null
|
let earliestStart: number | null = null
|
||||||
let latestEnd: number | null = null
|
let latestEnd: number | null = null
|
||||||
|
|
||||||
|
|||||||
@@ -177,8 +177,9 @@
|
|||||||
费用
|
费用
|
||||||
</TableHead>
|
</TableHead>
|
||||||
<TableHead class="h-12 font-semibold w-[70px] text-right">
|
<TableHead class="h-12 font-semibold w-[70px] text-right">
|
||||||
<div class="inline-block max-w-[2rem] leading-tight">
|
<div class="flex flex-col items-end text-xs gap-0.5">
|
||||||
响应时间
|
<span>首字</span>
|
||||||
|
<span class="text-muted-foreground font-normal">总耗时</span>
|
||||||
</div>
|
</div>
|
||||||
</TableHead>
|
</TableHead>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
@@ -356,15 +357,28 @@
|
|||||||
</div>
|
</div>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell class="text-right py-4 w-[70px]">
|
<TableCell class="text-right py-4 w-[70px]">
|
||||||
<span
|
<div
|
||||||
v-if="record.status === 'pending' || record.status === 'streaming'"
|
v-if="record.status === 'pending' || record.status === 'streaming'"
|
||||||
class="text-primary tabular-nums"
|
class="flex flex-col items-end text-xs gap-0.5"
|
||||||
>
|
>
|
||||||
|
<span class="text-primary tabular-nums">
|
||||||
{{ getElapsedTime(record) }}
|
{{ getElapsedTime(record) }}
|
||||||
</span>
|
</span>
|
||||||
<span v-else-if="record.response_time_ms">
|
</div>
|
||||||
{{ (record.response_time_ms / 1000).toFixed(2) }}s
|
<div
|
||||||
</span>
|
v-else-if="record.response_time_ms != null"
|
||||||
|
class="flex flex-col items-end text-xs gap-0.5"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
v-if="record.first_byte_time_ms != null"
|
||||||
|
class="tabular-nums"
|
||||||
|
>{{ (record.first_byte_time_ms / 1000).toFixed(2) }}s</span>
|
||||||
|
<span
|
||||||
|
v-else
|
||||||
|
class="text-muted-foreground"
|
||||||
|
>-</span>
|
||||||
|
<span class="text-muted-foreground tabular-nums">{{ (record.response_time_ms / 1000).toFixed(2) }}s</span>
|
||||||
|
</div>
|
||||||
<span
|
<span
|
||||||
v-else
|
v-else
|
||||||
class="text-muted-foreground"
|
class="text-muted-foreground"
|
||||||
|
|||||||
@@ -78,6 +78,7 @@ export interface UsageRecord {
|
|||||||
cost: number
|
cost: number
|
||||||
actual_cost?: number
|
actual_cost?: number
|
||||||
response_time_ms?: number
|
response_time_ms?: number
|
||||||
|
first_byte_time_ms?: number // 首字时间 (TTFB)
|
||||||
is_stream: boolean
|
is_stream: boolean
|
||||||
status_code?: number
|
status_code?: number
|
||||||
error_message?: string
|
error_message?: string
|
||||||
|
|||||||
Reference in New Issue
Block a user