fix(codex): preserve explicit service tiers and adapt usage badges

This commit is contained in:
ZheFox
2026-09-22 22:19:52 +08:00
parent 0b7c7f94ac
commit 69930a6059
7 changed files with 127 additions and 43 deletions
@@ -8,8 +8,8 @@
: undefined"
>
<div
class="flex min-w-0 max-w-full items-center gap-1"
:class="[modelRowClass, actualModel ? 'flex-wrap' : '']"
class="flex min-w-0 max-w-full flex-wrap items-center gap-1"
:class="modelRowClass"
>
<span
class="min-w-0 truncate"
@@ -29,7 +29,7 @@
:data-usage-model-badge="badge.key"
:data-request-detail-model-badge="context === 'detail' ? badge.key : undefined"
:variant="badge.variant"
class="h-4 shrink-0 whitespace-nowrap rounded-full px-1.5 text-[10px] leading-4"
class="h-4 max-w-full shrink-0 truncate rounded-full px-1.5 text-[10px] leading-4"
:class="badge.className"
:title="badge.title"
:aria-label="badge.ariaLabel"
@@ -51,7 +51,7 @@
:data-usage-model-badge="badge.key"
:data-request-detail-model-badge="context === 'detail' ? badge.key : undefined"
:variant="badge.variant"
class="h-4 shrink-0 whitespace-nowrap rounded-full px-1.5 text-[10px] leading-4"
class="h-4 max-w-full shrink-0 truncate rounded-full px-1.5 text-[10px] leading-4"
:class="badge.className"
:title="badge.title"
:aria-label="badge.ariaLabel"
@@ -69,7 +69,7 @@ import { Badge } from '@/components/ui'
import { isCyberPolicyError } from '../utils/cyberError'
import { formatServiceTierFact } from '../utils/service-tier'
type ModelBadgeKey = 'compact' | 'reasoning' | 'fast' | 'cyber' | 'reasoning_tokens'
type ModelBadgeKey = 'compact' | 'reasoning' | 'fast' | 'service-tier' | 'cyber' | 'reasoning_tokens'
interface ModelBadgePresentation {
key: ModelBadgeKey
@@ -154,14 +154,16 @@ const modelBadges = computed<ModelBadgePresentation[]>(() => {
})
}
if (props.showServiceTierBadge && formatServiceTierFact(props.record.service_tier) === 'Fast') {
const serviceTier = formatServiceTierFact(props.record.service_tier)
if (props.showServiceTierBadge && serviceTier
&& !['auto', 'default', 'standard'].includes(serviceTier.toLowerCase())) {
badges.push({
key: 'fast',
label: 'Fast',
key: serviceTier === 'Fast' ? 'fast' : 'service-tier',
label: serviceTier,
variant: 'outline-transparent',
className: 'text-blue-500 dark:text-blue-300',
title: '上游请求档位:Fast\n计费档位:Fast',
ariaLabel: '上游请求档位:Fast,计费档位:Fast',
title: `上游请求档位:${serviceTier}\n计费档位:${serviceTier}`,
ariaLabel: `上游请求档位:${serviceTier},计费档位:${serviceTier}`,
})
}
@@ -309,7 +309,11 @@ describe('RequestDetailDrawer settlement pricing', () => {
})
})
it('shows mapping, reasoning, Fast, and Cyber together in the model header', async () => {
it.each([
{ tier: 'priority', label: 'Fast', badgeKey: 'fast' },
{ tier: 'ultrafast', label: 'ultrafast', badgeKey: 'service-tier' },
{ tier: 'future-tier', label: 'future-tier', badgeKey: 'service-tier' },
])('shows mapping, reasoning, $tier, and Cyber together in the model header', async ({ tier, label, badgeKey }) => {
apiMocks.getRequestDetail.mockResolvedValue({
...buildEmbeddingDetail(),
id: 'usage-cyber-risk-demo',
@@ -322,13 +326,13 @@ describe('RequestDetailDrawer settlement pricing', () => {
model: 'gpt-5',
reasoning: { effort: 'xhigh' },
},
service_tier: 'priority',
service_tier: tier,
// A response-side tier must not be used for the Fast badge or billing.
actual_service_tier: 'default',
provider_request_body: {
model: 'gpt-5.1',
reasoning: { effort: 'max' },
service_tier: 'priority',
service_tier: tier,
},
status: 'failed',
status_code: 400,
@@ -369,10 +373,8 @@ describe('RequestDetailDrawer settlement pricing', () => {
.toContain('gpt-5.1')
expect(document.body.querySelector('[data-request-detail-model-badge="reasoning"]')?.textContent)
.toContain('xhigh -> max')
expect(document.body.querySelector('[data-request-detail-model-badge="fast"]')?.textContent)
.toContain('Fast')
expect(document.body.querySelector('[data-request-detail-model-badge="fast"]')?.textContent?.trim())
.toBe('Fast')
expect(document.body.querySelector(`[data-request-detail-model-badge="${badgeKey}"]`)?.textContent?.trim())
.toBe(label)
expect(document.body.querySelector('[data-request-detail-model-badge="cyber"]')?.textContent)
.toContain('Cyber')
const modelLayout = document.body.querySelector(
@@ -386,8 +388,8 @@ describe('RequestDetailDrawer settlement pricing', () => {
.toBe(true)
expect(modelRow?.querySelector('[data-request-detail-model-badge="reasoning"]')?.textContent)
.toContain('xhigh -> max')
expect(modelRow?.querySelector('[data-request-detail-model-badge="fast"]')?.textContent)
.toContain('Fast')
expect(modelRow?.querySelector(`[data-request-detail-model-badge="${badgeKey}"]`)?.textContent)
.toContain(label)
expect(modelRow?.querySelector('[data-request-detail-model-badge="cyber"]')?.textContent)
.toContain('Cyber')
expect(modelLayout?.querySelector('[data-request-detail-model-badges-row]')).toBeNull()
@@ -395,7 +397,7 @@ describe('RequestDetailDrawer settlement pricing', () => {
expect([...serviceTierFacts?.querySelectorAll('dt') ?? []].map(node => node.textContent?.trim()))
.toEqual(['上游请求层级', '计费层级'])
expect([...serviceTierFacts?.querySelectorAll('dd') ?? []].map(node => node.textContent?.trim()))
.toEqual(['Fast', 'Fast'])
.toEqual([label, label])
})
})
@@ -560,6 +560,37 @@ describe('UsageRecordsTable', () => {
},
)
it.each(['ultrafast', 'flex', 'future-tier', ' UltraFast '])(
'shows the final provider request tier %s without a fixed badge allowlist',
(requested) => {
const root = mountUsageRecordsTable([buildRecord({
service_tier: requested,
actual_service_tier: 'default',
})])
const badges = [...root.querySelectorAll<HTMLElement>('[data-usage-model-badge="service-tier"]')]
expect(badges.length).toBeGreaterThan(0)
for (const badge of badges) {
expect(badge.textContent?.trim()).toBe(requested.trim())
expect(badge.title).toBe(`上游请求档位:${requested.trim()}\n计费档位:${requested.trim()}`)
expect(badge.getAttribute('aria-label')).toBe(
`上游请求档位:${requested.trim()},计费档位:${requested.trim()}`,
)
}
},
)
it.each(['auto', 'default', 'standard', ' DEFAULT ', '', ' ', null])(
'does not show a badge for the default or absent request tier %s',
(requested) => {
const root = mountUsageRecordsTable([buildRecord({
service_tier: requested,
actual_service_tier: 'ultrafast',
})])
expect(root.querySelector('[data-usage-model-badge="service-tier"]')).toBeNull()
expect(root.querySelector('[data-usage-model-badge="fast"]')).toBeNull()
},
)
it.each(['default', 'flex', null])(
'ignores the response-side tier %s when the request tier is Fast',
(actualServiceTier) => {