mirror of
https://github.com/fawney19/Aether.git
synced 2026-10-08 02:17:46 +08:00
refactor(gateway): 重构 ai pipeline 规划链路
This commit is contained in:
@@ -1,6 +1,12 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import { isUsageRecordFailed, isUsageRecordSuccessful } from '../status'
|
||||
import {
|
||||
isUsageRecordFailed,
|
||||
isUsageRecordSuccessful,
|
||||
mapRequestStatusToTimelineStatus,
|
||||
normalizeRequestStatus,
|
||||
resolveTimelineFinalStatus,
|
||||
} from '../status'
|
||||
import type { UsageRecord } from '../../types'
|
||||
|
||||
function buildUsageRecord(overrides: Partial<UsageRecord> = {}): UsageRecord {
|
||||
@@ -40,4 +46,44 @@ describe('usage status helpers', () => {
|
||||
expect(isUsageRecordFailed(record)).toBe(true)
|
||||
expect(isUsageRecordSuccessful(record)).toBe(false)
|
||||
})
|
||||
|
||||
it('treats explicit failed status with a 2xx status code as successful for display', () => {
|
||||
const record = buildUsageRecord({
|
||||
status: 'failed',
|
||||
status_code: 200,
|
||||
error_message: 'stale failure flag'
|
||||
})
|
||||
|
||||
expect(isUsageRecordFailed(record)).toBe(false)
|
||||
expect(isUsageRecordSuccessful(record)).toBe(true)
|
||||
})
|
||||
|
||||
it('normalizes request status strings before mapping timeline status', () => {
|
||||
expect(normalizeRequestStatus(' Completed ')).toBe('completed')
|
||||
expect(mapRequestStatusToTimelineStatus('completed')).toBe('success')
|
||||
expect(mapRequestStatusToTimelineStatus('failed')).toBe('failed')
|
||||
})
|
||||
|
||||
it('treats explicit success status code as authoritative for the timeline', () => {
|
||||
expect(resolveTimelineFinalStatus({
|
||||
traceFinalStatus: 'success',
|
||||
requestStatus: 'failed',
|
||||
statusCode: 200,
|
||||
})).toBe('success')
|
||||
})
|
||||
|
||||
it('falls back to request lifecycle status when status code and trace are missing', () => {
|
||||
expect(resolveTimelineFinalStatus({
|
||||
requestStatus: 'failed',
|
||||
})).toBe('failed')
|
||||
})
|
||||
|
||||
it('uses status code only as a last fallback for timeline status', () => {
|
||||
expect(resolveTimelineFinalStatus({
|
||||
statusCode: 200,
|
||||
})).toBe('success')
|
||||
expect(resolveTimelineFinalStatus({
|
||||
statusCode: 503,
|
||||
})).toBe('failed')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import type { UsageRecord } from '../types'
|
||||
import type { RequestStatus, UsageRecord } from '../types'
|
||||
|
||||
export type TimelineFinalStatus = 'success' | 'failed' | 'streaming' | 'pending' | 'cancelled'
|
||||
|
||||
type RequestStatusLike = RequestStatus | string | null | undefined
|
||||
|
||||
function hasLegacyFailureSignal(
|
||||
record: Pick<UsageRecord, 'status_code' | 'error_message'>
|
||||
@@ -7,10 +11,32 @@ function hasLegacyFailureSignal(
|
||||
(typeof record.error_message === 'string' && record.error_message.trim().length > 0)
|
||||
}
|
||||
|
||||
function hasTerminalSuccessStatusCode(
|
||||
record: Pick<UsageRecord, 'status_code'>
|
||||
): boolean {
|
||||
return typeof record.status_code === 'number' &&
|
||||
record.status_code >= 200 &&
|
||||
record.status_code < 400
|
||||
}
|
||||
|
||||
export function isUsageRecordFailed(
|
||||
record: Pick<UsageRecord, 'status' | 'status_code' | 'error_message'>
|
||||
): boolean {
|
||||
const status = typeof record.status === 'string' ? record.status.trim().toLowerCase() : ''
|
||||
if (status) {
|
||||
if (status === 'pending' || status === 'streaming' || status === 'cancelled') {
|
||||
return false
|
||||
}
|
||||
if (status === 'completed') {
|
||||
return false
|
||||
}
|
||||
if (status === 'failed') {
|
||||
return !hasTerminalSuccessStatusCode(record)
|
||||
}
|
||||
}
|
||||
if (hasTerminalSuccessStatusCode(record)) {
|
||||
return false
|
||||
}
|
||||
if (status) {
|
||||
return status === 'failed'
|
||||
}
|
||||
@@ -22,7 +48,90 @@ export function isUsageRecordSuccessful(
|
||||
): boolean {
|
||||
const status = typeof record.status === 'string' ? record.status.trim().toLowerCase() : ''
|
||||
if (status) {
|
||||
return status === 'completed'
|
||||
if (status === 'completed') {
|
||||
return true
|
||||
}
|
||||
if (status === 'failed') {
|
||||
return hasTerminalSuccessStatusCode(record)
|
||||
}
|
||||
return false
|
||||
}
|
||||
if (hasTerminalSuccessStatusCode(record)) {
|
||||
return true
|
||||
}
|
||||
return !hasLegacyFailureSignal(record)
|
||||
}
|
||||
|
||||
export function normalizeRequestStatus(status: RequestStatusLike): RequestStatus | undefined {
|
||||
const normalized = typeof status === 'string' ? status.trim().toLowerCase() : ''
|
||||
switch (normalized) {
|
||||
case 'pending':
|
||||
case 'streaming':
|
||||
case 'completed':
|
||||
case 'failed':
|
||||
case 'cancelled':
|
||||
return normalized
|
||||
default:
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
export function mapRequestStatusToTimelineStatus(
|
||||
status: RequestStatusLike
|
||||
): TimelineFinalStatus | undefined {
|
||||
switch (normalizeRequestStatus(status)) {
|
||||
case 'completed':
|
||||
return 'success'
|
||||
case 'failed':
|
||||
return 'failed'
|
||||
case 'streaming':
|
||||
return 'streaming'
|
||||
case 'pending':
|
||||
return 'pending'
|
||||
case 'cancelled':
|
||||
return 'cancelled'
|
||||
default:
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeTimelineFinalStatus(status: string | null | undefined): TimelineFinalStatus | undefined {
|
||||
const normalized = typeof status === 'string' ? status.trim().toLowerCase() : ''
|
||||
switch (normalized) {
|
||||
case 'success':
|
||||
case 'failed':
|
||||
case 'streaming':
|
||||
case 'pending':
|
||||
case 'cancelled':
|
||||
return normalized
|
||||
default:
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
export function resolveTimelineFinalStatus(params: {
|
||||
hasPendingCandidates?: boolean
|
||||
traceFinalStatus?: string | null
|
||||
requestStatus?: RequestStatusLike
|
||||
statusCode?: number
|
||||
}): TimelineFinalStatus {
|
||||
if (params.hasPendingCandidates) {
|
||||
return 'pending'
|
||||
}
|
||||
|
||||
if (typeof params.statusCode === 'number') {
|
||||
return params.statusCode >= 200 && params.statusCode < 400 ? 'success' : 'failed'
|
||||
}
|
||||
|
||||
const traceStatus = normalizeTimelineFinalStatus(params.traceFinalStatus)
|
||||
if (traceStatus) {
|
||||
return traceStatus
|
||||
}
|
||||
|
||||
const requestStatus = mapRequestStatusToTimelineStatus(params.requestStatus)
|
||||
if (requestStatus) {
|
||||
return requestStatus
|
||||
}
|
||||
|
||||
return 'pending'
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user