feat: 优化用量状态同步机制和 OpenAI CLI 流式转换

- 增加状态回退保护,防止异步响应覆盖已知状态
- 添加轮询并发保护 (pollInFlight),避免重复请求
- 支持 cancelled 状态筛选和显示
- 前端 mergeRecordStatus 保护活跃记录状态
- 后端 streaming 状态同步更新改为使用当前 DB 会话
- OpenAI CLI 流式转换增加工具调用事件支持
- 轮询接口新增 target_model 字段返回
- 修复迁移脚本 inspector 缓存问题,改用 information_schema
This commit is contained in:
fawney19
2026-02-04 03:17:55 +08:00
parent f3e2f84b38
commit 7f09f191a1
17 changed files with 957 additions and 84 deletions

View File

@@ -211,20 +211,10 @@ const filteredRecords = computed(() => {
records = records.filter(record =>
!record.is_stream && !record.error_message && (!record.status_code || record.status_code === 200)
)
} else if (filterStatus.value === 'error') {
records = records.filter(record =>
record.error_message || (record.status_code && record.status_code >= 400)
)
} else if (filterStatus.value === 'active') {
records = records.filter(record =>
record.status === 'pending' || record.status === 'streaming'
)
} else if (filterStatus.value === 'pending') {
records = records.filter(record => record.status === 'pending')
} else if (filterStatus.value === 'streaming') {
records = records.filter(record => record.status === 'streaming')
} else if (filterStatus.value === 'completed') {
records = records.filter(record => record.status === 'completed')
} else if (filterStatus.value === 'failed') {
// 失败请求需要同时考虑新旧两种判断方式:
// 1. 新方式status = "failed"
@@ -234,6 +224,8 @@ const filteredRecords = computed(() => {
(record.status_code && record.status_code >= 400) ||
record.error_message
)
} else if (filterStatus.value === 'cancelled') {
records = records.filter(record => record.status === 'cancelled')
}
}
@@ -260,8 +252,12 @@ const GLOBAL_AUTO_REFRESH_INTERVAL = 5000 // 5秒刷新一次全局自动刷
const globalAutoRefresh = ref(false) // 全局自动刷新开关
// 轮询活跃请求状态(轻量级,只更新状态变化的记录)
let pollInFlight = false
async function pollActiveRequests() {
if (!hasActiveRequests.value) return
if (pollInFlight) return
pollInFlight = true
try {
// 根据页面类型选择不同的 API
@@ -280,34 +276,53 @@ async function pollActiveRequests() {
continue
}
// 状态变化:completed/failed 需要刷新获取完整数据
if (record.status !== update.status) {
// 状态只允许单向推进避免异步响应回退pending -> streaming -> completed/failed/cancelled
const statusPriority: Record<string, number> = {
pending: 0,
streaming: 1,
completed: 2,
failed: 2,
cancelled: 2
}
const currentRank = record.status ? (statusPriority[record.status] ?? 0) : 0
const newRank = update.status ? (statusPriority[update.status] ?? 0) : 0
const shouldApply = newRank >= currentRank
if (shouldApply && record.status !== update.status) {
record.status = update.status
}
if (update.status === 'completed' || update.status === 'failed') {
if (shouldApply && (update.status === 'completed' || update.status === 'failed')) {
shouldRefresh = true
}
// 进行中状态也需要持续更新provider/key/TTFB 可能在 streaming 后才落库)
record.input_tokens = update.input_tokens
record.output_tokens = update.output_tokens
record.cache_creation_input_tokens = update.cache_creation_input_tokens ?? undefined
record.cache_read_input_tokens = update.cache_read_input_tokens ?? undefined
record.cost = update.cost
record.actual_cost = update.actual_cost ?? undefined
record.rate_multiplier = update.rate_multiplier ?? undefined
record.response_time_ms = update.response_time_ms ?? undefined
record.first_byte_time_ms = update.first_byte_time_ms ?? undefined
// API 格式/格式转换streaming 时已可确定,轮询时同步更新
if (update.api_format !== undefined) record.api_format = update.api_format
if (update.endpoint_api_format !== undefined) record.endpoint_api_format = update.endpoint_api_format
if (update.has_format_conversion !== undefined) record.has_format_conversion = update.has_format_conversion
// 管理员接口返回额外字段
if ('provider' in update && typeof update.provider === 'string') {
record.provider = update.provider
}
if ('api_key_name' in update) {
record.api_key_name = typeof update.api_key_name === 'string' ? update.api_key_name : undefined
if (shouldApply) {
// 进行中状态也需要持续更新provider/key/TTFB 可能在 streaming 后才落库)
record.input_tokens = update.input_tokens
record.output_tokens = update.output_tokens
record.cache_creation_input_tokens = update.cache_creation_input_tokens ?? undefined
record.cache_read_input_tokens = update.cache_read_input_tokens ?? undefined
record.cost = update.cost
record.actual_cost = update.actual_cost ?? undefined
record.rate_multiplier = update.rate_multiplier ?? undefined
record.response_time_ms = update.response_time_ms ?? undefined
record.first_byte_time_ms = update.first_byte_time_ms ?? undefined
// API 格式/格式转换streaming 时已可确定,轮询时同步更新
if (update.api_format != null) record.api_format = update.api_format
if (update.endpoint_api_format != null) record.endpoint_api_format = update.endpoint_api_format
if (update.has_format_conversion != null) record.has_format_conversion = update.has_format_conversion
// 模型映射streaming 时已可确定
if ('target_model' in update && (typeof update.target_model === 'string' || update.target_model === null)) {
record.target_model = update.target_model
}
// 管理员接口返回额外字段
// 只有当返回的 provider 不是 pending/unknown 时才更新,避免覆盖已有的正确值
if ('provider' in update && typeof update.provider === 'string' &&
update.provider !== 'pending' && update.provider !== 'unknown') {
record.provider = update.provider
}
if ('api_key_name' in update) {
record.api_key_name = typeof update.api_key_name === 'string' ? update.api_key_name : undefined
}
}
}
@@ -316,6 +331,8 @@ async function pollActiveRequests() {
}
} catch (error) {
log.error('轮询活跃请求状态失败:', error)
} finally {
pollInFlight = false
}
}