mirror of
https://github.com/fawney19/Aether.git
synced 2026-10-04 16:37:46 +08:00
feat(rust): 支持 image 同步转流式 SSE、free_team_first 调度预设及账户错误自动重试
- openai:image 同步响应桥接为流式 SSE(image_generation.completed / image_edit.completed 事件) - image 请求解析与前置校验移除模型白名单,支持任意自定义模型名 - 调度器新增 free_team_first 预设(mode: both / free_only / team_only) - pool config 解析重构:新增 POOL_ALLOWED_SCHEDULING_PRESETS 白名单,规范化 mode 字段 - 错误分类器新增账户/账单错误模式集,升级为 RetryUpstreamFailure 而非 StopSemanticClientError - 修复 stream execution 中上游 headers 与输出 headers 混用导致 content-type 判断错误的问题 - codex image 工具始终写入 action 字段(generate/edit),仅 generate 操作填充默认 size/quality - 前端:pool 节点组只展示实际执行过的候选节点,全部 skipped 时折叠为最后一个节点 - Redis 测试就绪检测从 TCP 连接改为 PING/PONG 协议验证
This commit is contained in:
@@ -496,6 +496,7 @@ import { parseApiError } from '@/utils/errorParser'
|
||||
import { formatApiFormat } from '@/api/endpoints/types/api-format'
|
||||
import { resolveTimelineFinalStatus } from '../utils/status'
|
||||
import {
|
||||
buildPoolGroupVisibleAttempts,
|
||||
buildPoolParticipatedCandidates,
|
||||
extractPoolGroupId,
|
||||
makeAttemptKey,
|
||||
@@ -508,7 +509,7 @@ interface NodeGroup {
|
||||
providerName: string
|
||||
primary: CandidateRecord
|
||||
primaryStatus: string
|
||||
allAttempts: CandidateRecord[] // 所有尝试(包括首次和重试)
|
||||
allAttempts: CandidateRecord[] // 当前展示的尝试(含主节点)
|
||||
retryCount: number
|
||||
totalLatency: number // 所有尝试的总延迟
|
||||
startIndex: number
|
||||
@@ -867,15 +868,19 @@ const groupedTimeline = computed<NodeGroup[]>(() => {
|
||||
})
|
||||
if (attempts.length === 0) continue
|
||||
|
||||
const poolPrimaryStatus = attempts.reduce((best, current) => {
|
||||
const visibleAttempts = buildPoolGroupVisibleAttempts(attempts)
|
||||
if (visibleAttempts.length === 0) continue
|
||||
|
||||
const poolPrimaryStatus = visibleAttempts.reduce((best, current) => {
|
||||
const bestPriority = STATUS_PRIORITY[best] ?? 0
|
||||
const currentStatus = getDisplayStatus(current)
|
||||
const currentPriority = STATUS_PRIORITY[currentStatus] ?? 0
|
||||
return currentPriority > bestPriority ? currentStatus : best
|
||||
}, getDisplayStatus(attempts[0]))
|
||||
}, getDisplayStatus(visibleAttempts[0]))
|
||||
|
||||
const successAttempt = attempts.find((item) => item.status === 'success')
|
||||
const poolPrimary = successAttempt || attempts[attempts.length - 1] || attempts[0]
|
||||
const successAttempt = visibleAttempts.find((item) => item.status === 'success')
|
||||
const poolPrimary =
|
||||
successAttempt || visibleAttempts[visibleAttempts.length - 1] || visibleAttempts[0]
|
||||
const startIndex = Math.min(...attempts.map(item => item.candidate_index))
|
||||
const endIndex = Math.max(...attempts.map(item => item.candidate_index))
|
||||
|
||||
@@ -884,12 +889,12 @@ const groupedTimeline = computed<NodeGroup[]>(() => {
|
||||
providerName: getProviderDisplayName(poolPrimary, { allowAuthTypeFallback: false }),
|
||||
primary: poolPrimary,
|
||||
primaryStatus: poolPrimaryStatus,
|
||||
allAttempts: attempts,
|
||||
retryCount: Math.max(0, attempts.length - 1),
|
||||
totalLatency: attempts.reduce((sum, item) => sum + (item.latency_ms || 0), 0),
|
||||
allAttempts: visibleAttempts,
|
||||
retryCount: Math.max(0, visibleAttempts.length - 1),
|
||||
totalLatency: visibleAttempts.reduce((sum, item) => sum + (item.latency_ms || 0), 0),
|
||||
startIndex,
|
||||
endIndex,
|
||||
hasConversion: attempts.some((item) => item.extra_data?.needs_conversion === true),
|
||||
hasConversion: visibleAttempts.some((item) => item.extra_data?.needs_conversion === true),
|
||||
providerApiFormat: null,
|
||||
isPoolGroup: true,
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user