@@ -495,7 +530,7 @@
@@ -866,9 +901,14 @@ import {
import {
formatUsageStreamLabel,
isUsageUpstreamStream,
+ isUsageWebSocket,
resolveDisplayRequestStatus,
resolveUsageStreamLabelSegments,
} from '../utils/status'
+import {
+ formatUsageWebSocketTransportLabel,
+ formatUsageWebSocketTransportTitle,
+} from '../utils/websocketTransport'
import { isCyberPolicyError } from '../utils/cyberError'
import {
mergeUsageRecordErrorMessage,
@@ -933,6 +973,12 @@ const emit = defineEmits<{
responseTimeMs?: number | null
firstByteTimeMs?: number | null
isStream?: boolean | null
+ isWebSocket?: boolean | null
+ websocketTransport?: string | null
+ usageAvailable?: boolean | null
+ usagePricingAvailable?: boolean | null
+ inputAudioTokens?: number | null
+ outputAudioTokens?: number | null
upstreamIsStream?: boolean | null
clientRequestedStream?: boolean | null
clientIsStream?: boolean | null
@@ -961,6 +1007,13 @@ const REQUEST_STATE_STATUSES = new Set([
const loading = ref(false)
const error = ref(null)
const detail = ref(null)
+const detailUsageAvailable = computed(() => detail.value?.usage_available !== false)
+const detailPricingAvailable = computed(() => (
+ detailUsageAvailable.value && detail.value?.usage_pricing_available !== false
+))
+const detailTotalCostForDisplay = computed(() => (
+ detail.value ? (detailTotalCost(detail.value) ?? 0) : 0
+))
const timelineRef = ref | null>(null)
const timelineLoaded = ref(false)
const timelineHasTrace = ref(false)
@@ -1237,6 +1290,12 @@ function emitDetailRequestState(nextDetail: RequestDetail) {
responseTimeMs: nextDetail.response_time_ms ?? undefined,
firstByteTimeMs: nextDetail.first_byte_time_ms ?? null,
isStream: nextDetail.is_stream ?? null,
+ isWebSocket: nextDetail.is_websocket ?? null,
+ websocketTransport: nextDetail.websocket_transport ?? null,
+ usageAvailable: nextDetail.usage_available ?? null,
+ usagePricingAvailable: nextDetail.usage_pricing_available ?? null,
+ inputAudioTokens: nextDetail.input_audio_tokens ?? null,
+ outputAudioTokens: nextDetail.output_audio_tokens ?? null,
upstreamIsStream: nextDetail.upstream_is_stream ?? null,
clientRequestedStream: nextDetail.client_requested_stream ?? null,
clientIsStream: nextDetail.client_is_stream ?? null,
@@ -1858,6 +1917,11 @@ const priceSourceLabel = computed(() => {
if (!detail.value) return '历史定价'
return resolveSettlementPricingSourceLabel(detail.value) ?? '历史定价'
})
+const detailPricingLabel = computed(() => {
+ if (!detailUsageAvailable.value) return '用量不可用'
+ if (!detailPricingAvailable.value) return '音频用量未计价'
+ return priceSourceLabel.value
+})
const cacheCreationInputTokens5m = computed(() => {
if (!detail.value) return 0
diff --git a/frontend/src/features/usage/components/UsageRecordsTable.vue b/frontend/src/features/usage/components/UsageRecordsTable.vue
index d07b283b2..4c6572a9a 100644
--- a/frontend/src/features/usage/components/UsageRecordsTable.vue
+++ b/frontend/src/features/usage/components/UsageRecordsTable.vue
@@ -158,6 +158,9 @@
标准
+
+ WebSocket (WS)
+
活跃
@@ -281,9 +284,10 @@
v-else-if="isUsageWebSocket(record)"
variant="outline"
data-usage-transport="websocket"
+ :title="getWebSocketTransportTitle(record)"
class="whitespace-nowrap border-sky-500/50 text-sky-600 dark:text-sky-400 text-[10px] px-1.5 h-4 leading-4 inline-flex items-center flex-shrink-0"
>
- WS
+ {{ getWebSocketTransportLabel(record) }}
- {{ formatCurrency(record.cost || 0) }}
{{ formatCurrency(record.cost || 0) }}
+ 不可用
+ 未计价
+ {{ formatCurrency(record.actual_cost) }}
@@ -380,11 +399,24 @@
:title="hasRecordCacheTokens(record) ? getRecordCacheTokensTitle(record) : undefined"
>
Tokens
-
{{ formatTokens(getRecordEffectiveInputTokens(record)) }} / {{ formatTokens(record.output_tokens || 0) }}
-
+ {{ formatTokens(getRecordEffectiveInputTokens(record)) }} / {{ formatTokens(record.output_tokens || 0) }}
+ 不可用
+
|
{{ formatOptionalTokens(getRecordCacheReadTokens(record)) }} / {{ formatOptionalTokens(getRecordCacheCreationTokens(record)) }}
+
+ | 音频
+ {{ formatOptionalTokens(record.input_audio_tokens) }} / {{ formatOptionalTokens(record.output_audio_tokens) }}
+