mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 09:20:22 +08:00
feat(codex): 增强 OAuth 导入解析与账号信息提取,优化维护调度器线程模型
Codex OAuth: - 导入解析支持附加账号字段(account_id/plan_type/user_id/email) - enrich_codex 扩展从 access_token 和直接字段提取账号信息 - parse_codex_id_token 支持 JWT/JSON 字符串/dict 三种输入格式 - request patching 新增 openai:compact 格式支持 - codex_usage_parser 新增 credits_unlimited 字段解析 维护调度器: - 同步 DB 操作迁移到线程池执行,避免阻塞事件循环 - 新增 request_candidates 定期清理任务 - 新增每周 VACUUM ANALYZE 数据库表维护任务 - 新增 enable_db_maintenance 配置项 前端: - ElapsedTimeText 从 setInterval 改为 requestAnimationFrame - 时间线过滤 available/unused 占位记录 - Usage 页面默认关闭全局自动刷新 - 移除号池管理中的配额更新时间显示
This commit is contained in:
@@ -10,20 +10,18 @@ const props = withDefaults(defineProps<{
|
||||
status?: string | null
|
||||
responseTimeMs?: number | null
|
||||
precision?: number
|
||||
intervalMs?: number
|
||||
}>(), {
|
||||
createdAt: null,
|
||||
status: null,
|
||||
responseTimeMs: null,
|
||||
precision: 2,
|
||||
intervalMs: 200
|
||||
})
|
||||
|
||||
const now = ref(Date.now())
|
||||
const precision = computed(() => Math.max(0, props.precision))
|
||||
const isActive = computed(() => props.status === 'pending' || props.status === 'streaming')
|
||||
|
||||
let timer: ReturnType<typeof setInterval> | null = null
|
||||
let rafId: number | null = null
|
||||
|
||||
function parseCreatedAtMs(value: string | null | undefined): number {
|
||||
if (!value) return Number.NaN
|
||||
@@ -32,31 +30,33 @@ function parseCreatedAtMs(value: string | null | undefined): number {
|
||||
return new Date(normalized).getTime()
|
||||
}
|
||||
|
||||
function stopTimer() {
|
||||
if (!timer) return
|
||||
clearInterval(timer)
|
||||
timer = null
|
||||
function stopRaf() {
|
||||
if (rafId == null) return
|
||||
cancelAnimationFrame(rafId)
|
||||
rafId = null
|
||||
}
|
||||
|
||||
function startTimer() {
|
||||
stopTimer()
|
||||
const intervalMs = Math.max(100, props.intervalMs)
|
||||
timer = setInterval(() => {
|
||||
now.value = Date.now()
|
||||
}, intervalMs)
|
||||
function tick() {
|
||||
now.value = Date.now()
|
||||
rafId = requestAnimationFrame(tick)
|
||||
}
|
||||
|
||||
watch([isActive, () => props.intervalMs], ([active]) => {
|
||||
function startRaf() {
|
||||
stopRaf()
|
||||
now.value = Date.now()
|
||||
rafId = requestAnimationFrame(tick)
|
||||
}
|
||||
|
||||
watch(isActive, (active) => {
|
||||
if (active) {
|
||||
now.value = Date.now()
|
||||
startTimer()
|
||||
startRaf()
|
||||
} else {
|
||||
stopTimer()
|
||||
stopRaf()
|
||||
}
|
||||
}, { immediate: true })
|
||||
|
||||
onUnmounted(() => {
|
||||
stopTimer()
|
||||
stopRaf()
|
||||
})
|
||||
|
||||
const displayText = computed(() => {
|
||||
|
||||
@@ -642,6 +642,10 @@ const normalizeTimelineStatus = (value: unknown): CandidateRecord['status'] => {
|
||||
return 'failed'
|
||||
}
|
||||
|
||||
const isPlannedOnlyStatus = (status: CandidateRecord['status']): boolean => {
|
||||
return status === 'available' || status === 'unused'
|
||||
}
|
||||
|
||||
// 候选时间线(按实际执行顺序排序)
|
||||
const rawTimeline = computed<CandidateRecord[]>(() => {
|
||||
if (!trace.value) return []
|
||||
@@ -658,6 +662,11 @@ const rawTimeline = computed<CandidateRecord[]>(() => {
|
||||
})
|
||||
})
|
||||
|
||||
// 仅保留“已进入执行链路”的节点,过滤预创建但未执行的 available/unused 占位记录。
|
||||
const executableTimeline = computed<CandidateRecord[]>(() => {
|
||||
return rawTimeline.value.filter(candidate => !isPlannedOnlyStatus(candidate.status))
|
||||
})
|
||||
|
||||
const schedulingAudit = computed<Record<string, unknown> | null>(() => {
|
||||
const metadata = props.requestMetadata
|
||||
if (!metadata || typeof metadata !== 'object' || Array.isArray(metadata)) return null
|
||||
@@ -677,7 +686,7 @@ const extractPoolGroupId = (candidate: CandidateRecord): string | null => {
|
||||
|
||||
const poolAttemptCandidates = computed<CandidateRecord[]>(() => {
|
||||
// 新链路:优先使用后端写入的 extra_data.pool_group_id。
|
||||
const fromTrace = rawTimeline.value.filter((candidate) => extractPoolGroupId(candidate) !== null)
|
||||
const fromTrace = executableTimeline.value.filter((candidate) => extractPoolGroupId(candidate) !== null)
|
||||
if (fromTrace.length > 0) {
|
||||
return fromTrace
|
||||
}
|
||||
@@ -765,8 +774,8 @@ const poolAttemptKeySet = computed<Set<string>>(() => {
|
||||
})
|
||||
|
||||
const timeline = computed<CandidateRecord[]>(() => {
|
||||
if (poolAttemptCandidates.value.length === 0) return rawTimeline.value
|
||||
return rawTimeline.value.filter(
|
||||
if (poolAttemptCandidates.value.length === 0) return executableTimeline.value
|
||||
return executableTimeline.value.filter(
|
||||
(candidate) => !poolAttemptKeySet.value.has(makeAttemptKey(candidate.candidate_index, candidate.retry_index)),
|
||||
)
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user