fix(usage): prevent detail view from overriding active request status

- Keep pending/streaming lifecycle status authoritative for active requests
- Prevent detail status code or trace state from misclassifying in-flight requests as stream/failed
- Add regression coverage for status resolution and timeline state emission
This commit is contained in:
AAEE86
2026-05-15 19:04:50 +08:00
parent b0ba1f250d
commit 9c7757f801
3 changed files with 55 additions and 4 deletions

View File

@@ -350,6 +350,35 @@ describe('HorizontalRequestTimeline', () => {
expect(nodeDot?.classList.contains('status-success')).toBe(false)
})
it('keeps emitted trace state active while the request lifecycle is still streaming', async () => {
const onTraceState = vi.fn()
const trace = buildTrace([
buildCandidate({
id: 'cand-stale-failed',
provider_id: 'provider-stale',
provider_name: 'Provider Stale',
key_id: 'key-stale',
key_name: 'Stale Key',
candidate_index: 0,
status: 'failed',
status_code: 503,
}),
])
trace.final_status = 'failed'
mountTimeline(trace, {
requestStatus: 'streaming',
overrideStatusCode: 200,
onTraceState,
})
await nextTick()
const lastCall = onTraceState.mock.calls.at(-1)?.[0]
expect(lastCall).toMatchObject({
finalStatus: 'streaming',
})
})
it('shows request path from request metadata', async () => {
const trace = buildTrace([
buildCandidate({

View File

@@ -141,6 +141,25 @@ describe('usage status helpers', () => {
})).toBe('failed')
})
it('keeps active request lifecycle status authoritative over detail status code inference', () => {
expect(resolveTimelineFinalStatus({
requestStatus: 'streaming',
statusCode: 200,
})).toBe('streaming')
expect(resolveTimelineFinalStatus({
requestStatus: 'streaming',
statusCode: 503,
traceFinalStatus: 'failed',
})).toBe('streaming')
expect(resolveTimelineFinalStatus({
requestStatus: 'pending',
statusCode: 200,
traceFinalStatus: 'success',
})).toBe('pending')
})
it('uses explicit has_fallback flag for transfer filtering', () => {
expect(hasUsageFallback(buildUsageRecord({ has_fallback: true }))).toBe(true)
expect(hasUsageFallback(buildUsageRecord({ has_fallback: false }))).toBe(false)

View File

@@ -295,6 +295,9 @@ export function resolveTimelineFinalStatus(params: {
}
return requestStatus
}
if (requestStatus === 'pending' || requestStatus === 'streaming') {
return requestStatus
}
const traceStatus = normalizeTimelineFinalStatus(params.traceFinalStatus)
if (traceStatus === 'success' || traceStatus === 'failed' || traceStatus === 'cancelled') {
@@ -304,14 +307,14 @@ export function resolveTimelineFinalStatus(params: {
return traceStatus
}
if (hasTerminalSuccessStatusCode !== undefined) {
return hasTerminalSuccessStatusCode ? 'success' : 'failed'
}
if (params.hasPendingCandidates) {
return 'pending'
}
if (hasTerminalSuccessStatusCode !== undefined) {
return hasTerminalSuccessStatusCode ? 'success' : 'failed'
}
if (traceStatus) {
return traceStatus
}