Merge branch 'fawney19:main' into main

This commit is contained in:
ZheFox
2026-05-23 23:46:18 +08:00
committed by GitHub
2 changed files with 41 additions and 51 deletions
@@ -597,42 +597,20 @@
>({{ record.rate_multiplier }}x)</span>
</span>
</div>
<!-- 故障转移图标优先显示 -->
<svg
<Shuffle
v-if="record.has_fallback"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
data-usage-attempt-marker="fallback"
class="w-3.5 h-3.5 text-amber-600 dark:text-amber-400 flex-shrink-0"
title="此请求发生了 Provider 故障转移"
>
<path d="m16 3 4 4-4 4" />
<path d="M20 7H4" />
<path d="m8 21-4-4 4-4" />
<path d="M4 17h16" />
</svg>
<!-- 重试图标仅在无故障转移时显示 -->
<svg
v-else-if="record.has_retry"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
aria-label="发生 Provider 故障转移"
/>
<RefreshCcw
v-if="record.has_retry"
data-usage-attempt-marker="retry"
class="w-3.5 h-3.5 text-blue-600 dark:text-blue-400 flex-shrink-0"
title="此请求发生了亲和缓存重试"
>
<path d="M3 12a9 9 0 0 0 9 9 9.75 9.75 0 0 0 6.74-2.74L21 16" />
<path d="M21 21v-5h-5" />
<path d="M21 12a9 9 0 0 0-9-9 9.75 9.75 0 0 0-6.74 2.74L3 8" />
<path d="M3 3v5h5" />
</svg>
title="此请求发生了重试"
aria-label="发生重试"
/>
</div>
</TableCell>
<TableCell
@@ -871,7 +849,7 @@ import {
SortableTableHead,
TableFilterMenu,
} from '@/components/ui'
import { RefreshCcw, Search } from 'lucide-vue-next'
import { RefreshCcw, Search, Shuffle } from 'lucide-vue-next'
import { formatTokens, formatCurrency } from '@/utils/format'
import { getCacheCreationTokens, getCacheReadTokens, getEffectiveInputTokens } from '../token-normalization'
import {
@@ -888,7 +866,7 @@ import {
resolveUsageStreamLabelSegments
} from '../utils/status'
import { useRowClick } from '@/composables/useRowClick'
import { formatApiFormat } from '@/api/endpoints/types/api-format'
import { API_FORMAT_ORDER, formatApiFormat } from '@/api/endpoints/types/api-format'
import { formatClientFamily } from '@/features/usage/utils/clientFamily'
import type { DateRangeParams, UsageRecord } from '../types'
import { MultiSelect, TimeRangePicker } from '@/components/common'
@@ -1014,20 +992,11 @@ const emit = defineEmits<{
'prefetchDetail': [id: string]
}>()
// 静态常量(放在 defineProps/defineEmits 之后)
const AVAILABLE_API_FORMATS = [
{ value: 'openai:chat', label: 'OpenAI Chat' },
{ value: 'openai:responses', label: 'OpenAI Responses' },
{ value: 'openai:responses:compact', label: 'OpenAI Responses Compact' },
{ value: 'openai:video', label: 'OpenAI Video' },
{ value: 'claude:messages', label: 'Claude Messages' },
{ value: 'gemini:generate_content', label: 'Gemini Generate Content' },
{ value: 'gemini:video', label: 'Gemini Video' },
{ value: 'gemini:files', label: 'Gemini Files' },
] as const
// 使用模块级常量
const availableApiFormats = AVAILABLE_API_FORMATS
// 使用统一 API 格式枚举,避免使用记录筛选项和系统格式列表漂移。
const availableApiFormats = API_FORMAT_ORDER.map((value) => ({
value,
label: formatApiFormat(value),
}))
const adminVisibleColumnIds = useLocalStorage<UsageRecordColumnId[]>(
'usage-records-visible-columns-admin',
@@ -74,14 +74,15 @@ vi.mock('lucide-vue-next', async () => {
const { defineComponent, h } = await import('vue')
const Icon = defineComponent({
name: 'IconStub',
setup() {
return () => h('span')
setup(_, { attrs }) {
return () => h('span', attrs)
},
})
return {
RefreshCcw: Icon,
Search: Icon,
Shuffle: Icon,
ChevronDown: Icon,
Check: Icon,
}
@@ -180,7 +181,8 @@ describe('UsageRecordsTable', () => {
.filter((element) => element.textContent?.trim() === '100 tps')
expect(tpsElements.some((element) => element.classList.contains('text-[11px]'))).toBe(false)
const titles = [...root.querySelectorAll<HTMLElement>('[title]')].map((element) => element.title)
const titles = [...root.querySelectorAll<HTMLElement>('[title]')]
.map((element) => element.getAttribute('title'))
expect(titles).toContain([
'首字: 0.50s',
'总耗时: 1.00s',
@@ -262,4 +264,23 @@ describe('UsageRecordsTable', () => {
expect(root.textContent).toContain('0.50s / 1.00s')
expect(root.textContent).toContain('gpt-5')
})
it('offers embedding API formats in the usage record filter', () => {
const root = mountUsageRecordsTable([buildRecord({ api_format: 'openai:chat' })])
expect(root.textContent).toContain('OpenAI Embedding')
expect(root.textContent).toContain('Gemini Embedding')
expect(root.textContent).toContain('Jina Embedding')
expect(root.textContent).toContain('Doubao Embedding')
})
it('shows retry and fallback markers together when both flags are set', () => {
const root = mountUsageRecordsTable([buildRecord({
has_fallback: true,
has_retry: true,
})])
expect(root.querySelector('[data-usage-attempt-marker="fallback"]')).not.toBeNull()
expect(root.querySelector('[data-usage-attempt-marker="retry"]')).not.toBeNull()
})
})