fix(web-probe): classify sse timing separately (#715)
Co-authored-by: Codex <codex@noreply.local>
This commit is contained in:
@@ -2093,7 +2093,8 @@ console.log(JSON.stringify({
|
||||
excessiveIncreaseSeconds: item.excessiveIncreaseSeconds ?? null,
|
||||
anomaly: item.anomaly ?? null,
|
||||
})),
|
||||
pagePerformanceSlowApi: recentWindow.pagePerformance.sameOriginApiByPath.filter((item) => item.overFiveSecondCount > 0).slice(0, 5).map((item) => ({ path: item.path, route: item.route, sampleCount: item.sampleCount, p95Ms: item.p95Ms, maxMs: item.maxMs, overFiveSecondCount: item.overFiveSecondCount, slowSamples: item.slowSamples })),
|
||||
pagePerformanceSlowApi: recentWindow.pagePerformance.sameOriginApiByPath.filter((item) => item.isLongLivedStream !== true && item.overFiveSecondCount > 0).slice(0, 5).map((item) => ({ path: item.path, route: item.route, sampleCount: item.sampleCount, p95Ms: item.p95Ms, maxMs: item.maxMs, overFiveSecondCount: item.overFiveSecondCount, slowSamples: item.slowSamples })),
|
||||
pagePerformanceSseStreams: recentWindow.pagePerformance.sameOriginApiByPath.filter((item) => item.isLongLivedStream === true).slice(0, 5).map((item) => ({ path: item.path, route: item.route, sampleCount: item.sampleCount, streamOpenSampleCount: item.streamOpenSampleCount, streamOpenP95Ms: item.streamOpenP95Ms, streamOpenMaxMs: item.streamOpenMaxMs, streamOpenOverFiveSecondCount: item.streamOpenOverFiveSecondCount, streamLifetimeOverFiveSecondCount: item.streamLifetimeOverFiveSecondCount, slowSamples: item.slowSamples })),
|
||||
findings: prioritizeFindings(recentWindow.findings).slice(0, 8).map((item) => ({ kind: item.id ?? item.kind ?? item.code, severity: item.severity, count: item.count ?? item.sampleCount ?? null, summary: String(item.summary ?? item.message ?? "").slice(0, 180) })),
|
||||
valuesRedacted: true,
|
||||
}));
|
||||
@@ -2395,9 +2396,12 @@ function buildFindings(samples, control, network, errors, sampleMetrics, promptN
|
||||
if (traceMessageDuplicates.length > 0) findings.push({ id: "trace-assistant-message-duplicates-final-response", severity: "amber", summary: "trace rendered assistant message rows that duplicate the sealed final response", count: traceMessageDuplicates.length, samples: traceMessageDuplicates.slice(0, 20) });
|
||||
const turnTraceMissing = detectTurnTraceIdMissing(samples);
|
||||
if (turnTraceMissing.length > 0) findings.push({ id: "turn-trace-id-missing", severity: "red", summary: "Code Agent turn/card was visible without a trace id, so historical trace hydration cannot be reliable", count: turnTraceMissing.length, samples: turnTraceMissing.slice(0, 20) });
|
||||
const slowApi = Array.isArray(pagePerformance?.sameOriginApiByPath) ? pagePerformance.sameOriginApiByPath.filter((item) => item.overFiveSecondCount > 0) : [];
|
||||
const pagePerformanceItems = Array.isArray(pagePerformance?.sameOriginApiByPath) ? pagePerformance.sameOriginApiByPath : [];
|
||||
const slowApi = pagePerformanceItems.filter((item) => item.isLongLivedStream !== true && item.overFiveSecondCount > 0);
|
||||
if (slowApi.length > 0) findings.push({ id: "page-performance-slow-same-origin-api", severity: "red", summary: "same-origin API resource timing exceeded 5s usability budget", count: slowApi.length, groups: slowApi.slice(0, 20) });
|
||||
const longLivedStreams = Array.isArray(pagePerformance?.sameOriginApiByPath) ? pagePerformance.sameOriginApiByPath.filter((item) => item.isLongLivedStream) : [];
|
||||
const longLivedStreams = pagePerformanceItems.filter((item) => item.isLongLivedStream);
|
||||
const slowStreamOpen = longLivedStreams.filter((item) => Number(item.streamOpenOverFiveSecondCount ?? 0) > 0);
|
||||
if (slowStreamOpen.length > 0) findings.push({ id: "page-performance-slow-long-lived-stream-open", severity: "red", summary: "long-lived stream open latency exceeded 5s usability budget; stream lifetime is still reported separately", count: slowStreamOpen.length, groups: slowStreamOpen.slice(0, 20) });
|
||||
if (longLivedStreams.length > 0) findings.push({ id: "page-performance-long-lived-streams", severity: "info", summary: "same-origin long-lived streams are reported separately; lifetime is not treated as API load latency", count: longLivedStreams.length, groups: longLivedStreams.slice(0, 20) });
|
||||
if ((pageProvenance?.summary?.segmentCount ?? 0) > 1) findings.push({ id: "page-provenance-segments", severity: "info", summary: "observer crossed page asset provenance segments; interpret runtime findings by segment", segmentCount: pageProvenance.summary.segmentCount, segments: pageProvenance.segments.slice(0, 20) });
|
||||
const naturalApi = network.filter((item) => item.observerInitiated === false && item.type === "response" && /\/v1\/|\/auth\//u.test(String(item.url || "")));
|
||||
@@ -2620,8 +2624,10 @@ function buildPagePerformanceReport(samples, manifest) {
|
||||
valuesRedacted: true
|
||||
};
|
||||
}).sort((a, b) => (b.overFiveSecondCount - a.overFiveSecondCount) || (Number(b.p95Ms ?? 0) - Number(a.p95Ms ?? 0)) || a.path.localeCompare(b.path));
|
||||
const slow = sameOriginApiByPath.filter((item) => item.overFiveSecondCount > 0);
|
||||
const longLivedStreams = sameOriginApiByPath.filter((item) => item.isLongLivedStream);
|
||||
const ordinaryApi = sameOriginApiByPath.filter((item) => item.isLongLivedStream !== true);
|
||||
const slow = ordinaryApi.filter((item) => item.overFiveSecondCount > 0);
|
||||
const slowStreamOpen = longLivedStreams.filter((item) => Number(item.streamOpenOverFiveSecondCount ?? 0) > 0);
|
||||
const budgetP95Values = sameOriginApiByPath
|
||||
.map((item) => Number(item.isLongLivedStream ? (item.streamOpenP95Ms ?? 0) : (item.p95Ms ?? 0)))
|
||||
.filter((value) => Number.isFinite(value));
|
||||
@@ -2632,6 +2638,9 @@ function buildPagePerformanceReport(samples, manifest) {
|
||||
sameOriginApiSampleCount: sameOriginApiByPath.reduce((sum, item) => sum + item.sampleCount, 0),
|
||||
longLivedStreamPathCount: longLivedStreams.length,
|
||||
longLivedStreamSampleCount: longLivedStreams.reduce((sum, item) => sum + item.sampleCount, 0),
|
||||
longLivedStreamOpenOverFiveSecondPathCount: slowStreamOpen.length,
|
||||
longLivedStreamOpenOverFiveSecondSampleCount: slowStreamOpen.reduce((sum, item) => sum + Number(item.streamOpenOverFiveSecondCount ?? 0), 0),
|
||||
longLivedStreamLifetimeOverFiveSecondSampleCount: longLivedStreams.reduce((sum, item) => sum + Number(item.streamLifetimeOverFiveSecondCount ?? 0), 0),
|
||||
slowPathCount: slow.length,
|
||||
slowSampleCount: slow.reduce((sum, item) => sum + item.overFiveSecondCount, 0),
|
||||
worstP95Ms: budgetP95Values.length > 0 ? Math.max(...budgetP95Values) : null,
|
||||
@@ -4702,9 +4711,14 @@ function renderMarkdown(report) {
|
||||
const provenanceLines = Array.isArray(report.pageProvenance?.segments) && report.pageProvenance.segments.length > 0
|
||||
? report.pageProvenance.segments.slice(0, 40).map((item) => "- fingerprint=" + (item.assetFingerprint || "-") + " samples=" + item.sampleCount + " seq=" + (item.firstSeq ?? "-") + ".." + (item.lastSeq ?? "-") + " ts=" + (item.firstAt || "-") + ".." + (item.lastAt || "-") + " scripts=" + (item.scriptCount ?? "-") + " styles=" + (item.stylesheetCount ?? "-") + " urlPaths=" + (Array.isArray(item.urlPaths) ? item.urlPaths.slice(0, 4).join(",") : "-")).join("\n")
|
||||
: "- 无页面 provenance segment。";
|
||||
const performanceLines = Array.isArray(report.pagePerformance?.sameOriginApiByPath) && report.pagePerformance.sameOriginApiByPath.length > 0
|
||||
? report.pagePerformance.sameOriginApiByPath.slice(0, 80).map((item) => "- " + item.path + " kind=" + (item.routeKind || "same-origin-api") + " budgetMetric=" + (item.budgetMetric || "durationMs") + " samples=" + item.sampleCount + " p50=" + (item.p50Ms ?? "-") + "ms p75=" + (item.p75Ms ?? "-") + "ms p95=" + (item.p95Ms ?? "-") + "ms max=" + (item.maxMs ?? "-") + "ms >5s=" + (item.overFiveSecondCount ?? 0) + " streamOpenP95=" + (item.streamOpenP95Ms ?? "-") + "ms streamLifetime>5s=" + (item.streamLifetimeOverFiveSecondCount ?? 0) + " window=" + (item.firstAt || "-") + ".." + (item.lastAt || "-")).join("\n")
|
||||
const ordinaryPerformanceItems = Array.isArray(report.pagePerformance?.sameOriginApiByPath) ? report.pagePerformance.sameOriginApiByPath.filter((item) => item.isLongLivedStream !== true) : [];
|
||||
const streamPerformanceItems = Array.isArray(report.pagePerformance?.sameOriginApiByPath) ? report.pagePerformance.sameOriginApiByPath.filter((item) => item.isLongLivedStream === true) : [];
|
||||
const performanceLines = ordinaryPerformanceItems.length > 0
|
||||
? ordinaryPerformanceItems.slice(0, 80).map((item) => "- " + item.path + " kind=" + (item.routeKind || "same-origin-api") + " budgetMetric=" + (item.budgetMetric || "durationMs") + " samples=" + item.sampleCount + " p50=" + (item.p50Ms ?? "-") + "ms p75=" + (item.p75Ms ?? "-") + "ms p95=" + (item.p95Ms ?? "-") + "ms max=" + (item.maxMs ?? "-") + "ms >5s=" + (item.overFiveSecondCount ?? 0) + " window=" + (item.firstAt || "-") + ".." + (item.lastAt || "-")).join("\n")
|
||||
: "- 无同源 API Resource Timing 样本。";
|
||||
const streamPerformanceLines = streamPerformanceItems.length > 0
|
||||
? streamPerformanceItems.slice(0, 80).map((item) => "- " + item.path + " kind=" + (item.routeKind || "same-origin-api-stream") + " samples=" + item.sampleCount + " streamOpenP50=" + (item.streamOpenP50Ms ?? "-") + "ms streamOpenP75=" + (item.streamOpenP75Ms ?? "-") + "ms streamOpenP95=" + (item.streamOpenP95Ms ?? "-") + "ms streamOpenMax=" + (item.streamOpenMaxMs ?? "-") + "ms streamOpen>5s=" + (item.streamOpenOverFiveSecondCount ?? 0) + " streamLifetime>5s=" + (item.streamLifetimeOverFiveSecondCount ?? 0) + " lifetimeMax=" + (item.maxMs ?? "-") + "ms window=" + (item.firstAt || "-") + ".." + (item.lastAt || "-")).join("\n")
|
||||
: "- 无同源长连接 Resource Timing 样本。";
|
||||
const metricLines = Array.isArray(report.sampleMetrics?.timeline) && report.sampleMetrics.timeline.length > 0
|
||||
? report.sampleMetrics.timeline.slice(0, 120).map((item) => "- #" + item.seq + " " + item.ts + " prompt=" + item.promptIndex + " loadingCount=" + (item.loadingCount ?? 0) + " loadingOwners=" + (item.loadingOwnerCount ?? 0) + " totalElapsedSeconds=" + (item.totalElapsedSeconds ?? "-") + " recentUpdateSeconds=" + (item.recentUpdateSeconds ?? "-") + " terminal=" + item.terminalSeen + " finalText=" + item.finalResultTextSeen + " diagnostic=" + item.diagnosticSeen).join("\n")
|
||||
: "- 无采样指标。";
|
||||
@@ -4785,10 +4799,16 @@ function renderMarkdown(report) {
|
||||
+ "- sameOriginApiSampleCount: " + (report.pagePerformance?.summary?.sameOriginApiSampleCount ?? 0) + "\n"
|
||||
+ "- longLivedStreamPathCount: " + (report.pagePerformance?.summary?.longLivedStreamPathCount ?? 0) + "\n"
|
||||
+ "- longLivedStreamSampleCount: " + (report.pagePerformance?.summary?.longLivedStreamSampleCount ?? 0) + "\n"
|
||||
+ "- longLivedStreamOpenOverFiveSecondPathCount: " + (report.pagePerformance?.summary?.longLivedStreamOpenOverFiveSecondPathCount ?? 0) + "\n"
|
||||
+ "- longLivedStreamOpenOverFiveSecondSampleCount: " + (report.pagePerformance?.summary?.longLivedStreamOpenOverFiveSecondSampleCount ?? 0) + "\n"
|
||||
+ "- longLivedStreamLifetimeOverFiveSecondSampleCount: " + (report.pagePerformance?.summary?.longLivedStreamLifetimeOverFiveSecondSampleCount ?? 0) + "\n"
|
||||
+ "- slowPathCount: " + (report.pagePerformance?.summary?.slowPathCount ?? 0) + "\n"
|
||||
+ "- slowSampleCount: " + (report.pagePerformance?.summary?.slowSampleCount ?? 0) + "\n"
|
||||
+ "- worstP95Ms: " + (report.pagePerformance?.summary?.worstP95Ms ?? "-") + "\n\n"
|
||||
+ performanceLines + "\n\n"
|
||||
+ "### Page performance: long-lived streams\n\n"
|
||||
+ "- policy: SSE/long-lived stream lifetime is not ordinary API load latency; only stream open latency is compared with the 5s usability budget, while disconnects remain runtime alerts.\n\n"
|
||||
+ streamPerformanceLines + "\n\n"
|
||||
+ "### Prompt network\n\n" + promptNetworkLines + "\n\n"
|
||||
+ "### Runtime alerts\n\n"
|
||||
+ "- httpErrorCount: " + (alertSummary.httpErrorCount ?? 0) + "\n"
|
||||
|
||||
Reference in New Issue
Block a user