mirror of
https://github.com/fawney19/Aether.git
synced 2026-10-04 16:37:46 +08:00
feat(stream-bridge): 支持上游 sync 响应转 SSE 流式输出,记录 client/upstream 流模式差异
- 新增 sync_to_stream 桥接模块,将非 SSE 上游响应转换为 SSE 格式回传给流式客户端 - stream_pump 检测非 SSE 响应头后缓冲整包并通过桥接逻辑重写为 SSE 帧 - proxy handler 同步支持 sync→stream 聚合与转换(覆盖 openai/claude/gemini 四种格式) - sync_products 补全 openai:cli 的完整流式事件聚合(text delta、reasoning、tool call 等) - usage runtime 写入 client_requested_stream / upstream_is_stream 到 request_metadata - SQL 查询层将两个布尔字段从 request_metadata jsonb 中提取并回传给前端 - 前端 status.ts 新增 resolveUsageStreamLabelSegments,优先读取 client_requested_stream - RequestDetailDrawer 在流式转换场景下显示"客户端→上游"两段 Badge
This commit is contained in:
@@ -54,11 +54,24 @@
|
||||
{{ detail.status_code }}
|
||||
</Badge>
|
||||
<Badge
|
||||
v-if="detail"
|
||||
variant="outline"
|
||||
class="text-xs"
|
||||
v-if="detail && resolveUsageStreamLabelSegments(detail).hasConversion"
|
||||
:variant="resolveUsageStreamLabelSegments(detail).client === '流式' ? 'secondary' : 'outline'"
|
||||
:class="resolveUsageStreamLabelSegments(detail).client === '流式'
|
||||
? 'text-xs inline-flex items-center gap-1'
|
||||
: 'text-xs inline-flex items-center gap-1 border-border/60 text-muted-foreground'"
|
||||
>
|
||||
{{ detail ? formatUsageStreamLabel(detail) : '标准' }}
|
||||
<span>{{ resolveUsageStreamLabelSegments(detail).client }}</span>
|
||||
<span class="opacity-60">→</span>
|
||||
<span>{{ resolveUsageStreamLabelSegments(detail).upstream }}</span>
|
||||
</Badge>
|
||||
<Badge
|
||||
v-else-if="detail"
|
||||
:variant="isUsageUpstreamStream(detail) ? 'secondary' : 'outline'"
|
||||
:class="isUsageUpstreamStream(detail)
|
||||
? 'text-xs'
|
||||
: 'text-xs border-border/60 text-muted-foreground'"
|
||||
>
|
||||
{{ formatUsageStreamLabel(detail) }}
|
||||
</Badge>
|
||||
</div>
|
||||
<div class="flex items-center gap-1 shrink-0">
|
||||
@@ -695,7 +708,11 @@ import { formatApiFormat } from '@/api/endpoints/types/api-format'
|
||||
import { formatShortRequestId } from '@/utils/format'
|
||||
import { log } from '@/utils/logger'
|
||||
import { getEffectiveInputTokens } from '../token-normalization'
|
||||
import { formatUsageStreamLabel } from '../utils/status'
|
||||
import {
|
||||
formatUsageStreamLabel,
|
||||
isUsageUpstreamStream,
|
||||
resolveUsageStreamLabelSegments,
|
||||
} from '../utils/status'
|
||||
|
||||
// 子组件
|
||||
import RequestHeadersContent from './RequestDetailDrawer/RequestHeadersContent.vue'
|
||||
|
||||
@@ -232,6 +232,17 @@
|
||||
>
|
||||
取消
|
||||
</Badge>
|
||||
<Badge
|
||||
v-else-if="getStreamModeSegments(record).hasConversion"
|
||||
:variant="getStreamModeSegments(record).client === '流式' ? 'secondary' : 'outline'"
|
||||
:class="getStreamModeSegments(record).client === '流式'
|
||||
? 'whitespace-nowrap text-[10px] px-1.5 h-4 leading-4 inline-flex items-center gap-0.5'
|
||||
: 'whitespace-nowrap border-border/60 text-muted-foreground text-[10px] px-1.5 h-4 leading-4 inline-flex items-center gap-0.5'"
|
||||
>
|
||||
<span>{{ getStreamModeSegments(record).client }}</span>
|
||||
<span class="opacity-60">→</span>
|
||||
<span>{{ getStreamModeSegments(record).upstream }}</span>
|
||||
</Badge>
|
||||
<Badge
|
||||
v-else
|
||||
:variant="getUpstreamStream(record) ? 'secondary' : 'outline'"
|
||||
@@ -529,6 +540,17 @@
|
||||
>
|
||||
已取消
|
||||
</Badge>
|
||||
<Badge
|
||||
v-else-if="getStreamModeSegments(record).hasConversion"
|
||||
:variant="getStreamModeSegments(record).client === '流式' ? 'secondary' : 'outline'"
|
||||
:class="getStreamModeSegments(record).client === '流式'
|
||||
? 'whitespace-nowrap inline-flex items-center gap-1'
|
||||
: 'whitespace-nowrap border-border/60 text-muted-foreground inline-flex items-center gap-1'"
|
||||
>
|
||||
<span>{{ getStreamModeSegments(record).client }}</span>
|
||||
<span class="opacity-60">→</span>
|
||||
<span>{{ getStreamModeSegments(record).upstream }}</span>
|
||||
</Badge>
|
||||
<Badge
|
||||
v-else
|
||||
:variant="getUpstreamStream(record) ? 'secondary' : 'outline'"
|
||||
@@ -672,7 +694,8 @@ import {
|
||||
formatUsageStreamLabel,
|
||||
isUsageRecordFailed,
|
||||
isUsageUpstreamStream,
|
||||
resolveDisplayRequestStatus
|
||||
resolveDisplayRequestStatus,
|
||||
resolveUsageStreamLabelSegments
|
||||
} from '../utils/status'
|
||||
import { useRowClick } from '@/composables/useRowClick'
|
||||
import { formatApiFormat } from '@/api/endpoints/types/api-format'
|
||||
@@ -763,6 +786,10 @@ function getStreamModeLabel(record: UsageRecord): string {
|
||||
return formatUsageStreamLabel(record)
|
||||
}
|
||||
|
||||
function getStreamModeSegments(record: UsageRecord) {
|
||||
return resolveUsageStreamLabelSegments(record)
|
||||
}
|
||||
|
||||
function getUpstreamStream(record: UsageRecord): boolean {
|
||||
return isUsageUpstreamStream(record)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user