feat(proxy): record tunnel stability metrics

This commit is contained in:
fawney19
2026-05-08 22:03:17 +08:00
parent 9a84a6ff6c
commit a703acd1fe
81 changed files with 7228 additions and 1109 deletions
@@ -99,12 +99,16 @@ function buildTrace(candidates: CandidateRecord[]): RequestTrace {
}
}
function mountTimeline(traceData: RequestTrace) {
function mountTimeline(
traceData: RequestTrace,
extraProps: Record<string, unknown> = {},
) {
const root = document.createElement('div')
document.body.appendChild(root)
const app = createApp(HorizontalRequestTimeline, {
requestId: traceData.request_id,
traceData,
...extraProps,
})
app.mount(root)
mountedApps.push({ app, root })
@@ -269,4 +273,52 @@ describe('HorizontalRequestTimeline', () => {
expect(nodeDots[0].classList.contains('status-success')).toBe(false)
expect(nodeDots[1].classList.contains('status-success')).toBe(true)
})
it('treats 3xx terminal responses as failed for node display', async () => {
const trace = buildTrace([
buildCandidate({
id: 'cand-redirect',
provider_id: 'provider-redirect',
provider_name: 'Provider Redirect',
key_id: 'key-redirect',
key_name: 'Redirect Key',
candidate_index: 0,
status: 'success',
status_code: 302,
}),
])
const root = mountTimeline(trace)
await nextTick()
const nodeDot = root.querySelector<HTMLElement>('.node-dot')
expect(nodeDot?.classList.contains('status-failed')).toBe(true)
expect(nodeDot?.classList.contains('status-success')).toBe(false)
})
it('shows request path from request metadata', async () => {
const trace = buildTrace([
buildCandidate({
id: 'cand-path',
provider_id: 'provider-path',
provider_name: 'Provider Path',
key_id: 'key-path',
key_name: 'Path Key',
candidate_index: 0,
status: 'failed',
}),
])
const root = mountTimeline(trace, {
requestMetadata: {
request_path: '/v1beta/models/gemini-2.5-pro:generateContent',
request_query_string: 'alt=sse',
},
})
await nextTick()
expect(root.textContent).toContain('请求路径')
const requestPathCode = root.querySelector<HTMLElement>('.request-path-code')
expect(requestPathCode?.textContent).toContain('/v1beta/models/gemini-2.5-pro:generateContent?alt=sse')
})
})