fix(web-probe): classify sse timing separately (#715)
Co-authored-by: Codex <codex@noreply.local>
This commit is contained in:
@@ -8061,10 +8061,12 @@ function runNodeWebProbeObserveAnalyze(options: NodeWebProbeObserveOptions, spec
|
||||
"const runtimeAlerts = srcRuntimeAlerts ? { httpErrorCount: srcRuntimeAlerts.httpErrorCount ?? null, requestFailedCount: srcRuntimeAlerts.requestFailedCount ?? null, domDiagnosticSampleCount: srcRuntimeAlerts.domDiagnosticSampleCount ?? null, consoleAlertCount: srcRuntimeAlerts.consoleAlertCount ?? null } : null;",
|
||||
"const srcPagePerformance = objectOrNull(source?.pagePerformance);",
|
||||
"const srcPagePerformanceSummary = objectOrNull(srcPagePerformance?.summary);",
|
||||
"const pagePerformance = srcPagePerformance ? { sameOriginApiPaths: srcPagePerformance.sameOriginApiPaths ?? srcPagePerformanceSummary?.sameOriginApiPathCount ?? null, slowPathCount: srcPagePerformance.slowPathCount ?? srcPagePerformanceSummary?.slowPathCount ?? null, slowSampleCount: srcPagePerformance.slowSampleCount ?? srcPagePerformanceSummary?.slowSampleCount ?? null, worstP95Ms: srcPagePerformance.worstP95Ms ?? srcPagePerformanceSummary?.worstP95Ms ?? null } : null;",
|
||||
"const pagePerformance = srcPagePerformance ? { sameOriginApiPaths: srcPagePerformance.sameOriginApiPaths ?? srcPagePerformanceSummary?.sameOriginApiPathCount ?? null, longLivedStreamPathCount: srcPagePerformance.longLivedStreamPathCount ?? srcPagePerformanceSummary?.longLivedStreamPathCount ?? null, longLivedStreamOpenOverFiveSecondSampleCount: srcPagePerformance.longLivedStreamOpenOverFiveSecondSampleCount ?? srcPagePerformanceSummary?.longLivedStreamOpenOverFiveSecondSampleCount ?? null, slowPathCount: srcPagePerformance.slowPathCount ?? srcPagePerformanceSummary?.slowPathCount ?? null, slowSampleCount: srcPagePerformance.slowSampleCount ?? srcPagePerformanceSummary?.slowSampleCount ?? null, worstP95Ms: srcPagePerformance.worstP95Ms ?? srcPagePerformanceSummary?.worstP95Ms ?? null } : null;",
|
||||
"const fullRecentWindow = objectOrNull(fullSource?.windows?.recent);",
|
||||
"const fullPagePerformance = objectOrNull(fullRecentWindow?.pagePerformance) || objectOrNull(fullSource?.pagePerformance);",
|
||||
"const sourceSlowApi = Array.isArray(source?.pagePerformanceSlowApi) ? source.pagePerformanceSlowApi : (Array.isArray(fullPagePerformance?.sameOriginApiByPath) ? fullPagePerformance.sameOriginApiByPath.filter((item) => Number(item?.overFiveSecondCount ?? 0) > 0) : []);",
|
||||
"const isLongLivedApi = (item) => item?.isLongLivedStream === true || item?.routeKind === 'same-origin-api-stream' || item?.budgetMetric === 'streamOpenMs';",
|
||||
"const sourceSlowApi = Array.isArray(source?.pagePerformanceSlowApi) ? source.pagePerformanceSlowApi.filter((item) => !isLongLivedApi(item) && Number(item?.overFiveSecondCount ?? 0) > 0) : (Array.isArray(fullPagePerformance?.sameOriginApiByPath) ? fullPagePerformance.sameOriginApiByPath.filter((item) => !isLongLivedApi(item) && Number(item?.overFiveSecondCount ?? 0) > 0) : []);",
|
||||
"const sourceSseStreams = Array.isArray(source?.pagePerformanceSseStreams) ? source.pagePerformanceSseStreams : (Array.isArray(fullPagePerformance?.sameOriginApiByPath) ? fullPagePerformance.sameOriginApiByPath.filter((item) => isLongLivedApi(item)) : []);",
|
||||
"const combinedFindings = sortFindings(firstArray(source?.findings, fullRecentWindow?.findings, fullSource?.findings));",
|
||||
"const srcPromptNetwork = objectOrNull(source?.promptNetwork);",
|
||||
"const promptNetwork = srcPromptNetwork ? { promptSegments: srcPromptNetwork.promptSegments ?? null } : null;",
|
||||
@@ -8080,6 +8082,7 @@ function runNodeWebProbeObserveAnalyze(options: NodeWebProbeObserveOptions, spec
|
||||
" pagePerformance,",
|
||||
" promptNetwork,",
|
||||
" pagePerformanceSlowApi: takeHead(sourceSlowApi, 4).map(slimSlowApi),",
|
||||
" pagePerformanceSseStreams: takeHead(sourceSseStreams, 4).map((item) => ({ path: item?.path ?? item?.route ?? null, route: item?.route ?? null, sampleCount: item?.sampleCount ?? null, streamOpenSampleCount: item?.streamOpenSampleCount ?? null, streamOpenP95Ms: item?.streamOpenP95Ms ?? null, streamOpenMaxMs: item?.streamOpenMaxMs ?? null, streamOpenOverFiveSecondCount: item?.streamOpenOverFiveSecondCount ?? null, streamLifetimeOverFiveSecondCount: item?.streamLifetimeOverFiveSecondCount ?? null })),",
|
||||
" findings: takeHead(combinedFindings, 8).map(slimFinding),",
|
||||
" httpErrorGroups: takeHead(firstArray(source.httpErrorGroups, fullRecentWindow?.runtimeAlerts?.networkHttpErrorsByPath, fullSource?.httpErrorGroups, fullSource?.runtimeAlerts?.networkHttpErrorsByPath), 4).map(slimNetworkGroup),",
|
||||
" requestFailedGroups: takeHead(firstArray(source.requestFailedGroups, fullRecentWindow?.runtimeAlerts?.networkRequestFailedByPath, fullSource?.requestFailedGroups, fullSource?.runtimeAlerts?.networkRequestFailedByPath), 5).map(slimNetworkGroup),",
|
||||
@@ -8159,6 +8162,7 @@ function runNodeWebProbeObserveAnalyze(options: NodeWebProbeObserveOptions, spec
|
||||
" promptNetwork: compact.promptNetwork ?? null,",
|
||||
" findings: Array.isArray(compact.findings) ? compact.findings.slice(0, 4) : [],",
|
||||
" pagePerformanceSlowApi: Array.isArray(compact.pagePerformanceSlowApi) ? compact.pagePerformanceSlowApi.slice(0, 2).map((item) => ({ ...item, slowSamples: Array.isArray(item.slowSamples) ? item.slowSamples.slice(0, 1) : [] })) : [],",
|
||||
" pagePerformanceSseStreams: Array.isArray(compact.pagePerformanceSseStreams) ? compact.pagePerformanceSseStreams.slice(0, 2).map((item) => ({ path: item.path ?? item.route ?? null, route: item.route ?? item.path ?? null, sampleCount: item.sampleCount ?? null, streamOpenP95Ms: item.streamOpenP95Ms ?? null, streamOpenMaxMs: item.streamOpenMaxMs ?? null, streamOpenOverFiveSecondCount: item.streamOpenOverFiveSecondCount ?? null, streamLifetimeOverFiveSecondCount: item.streamLifetimeOverFiveSecondCount ?? null })) : [],",
|
||||
" httpErrorGroups: Array.isArray(compact.httpErrorGroups) ? compact.httpErrorGroups.slice(0, 3) : [],",
|
||||
" requestFailedGroups: Array.isArray(compact.requestFailedGroups) ? compact.requestFailedGroups.slice(0, 3) : [],",
|
||||
" domDiagnosticGroups: Array.isArray(compact.domDiagnosticGroups) ? compact.domDiagnosticGroups.slice(0, 2) : [],",
|
||||
@@ -8211,6 +8215,7 @@ function runNodeWebProbeObserveAnalyze(options: NodeWebProbeObserveOptions, spec
|
||||
" pagePerformance: compact.pagePerformance ?? null,",
|
||||
" findings: Array.isArray(compact.findings) ? compact.findings.slice(0, 4) : [],",
|
||||
" pagePerformanceSlowApi: Array.isArray(compact.pagePerformanceSlowApi) ? compact.pagePerformanceSlowApi.slice(0, 2).map((item) => ({ path: item.path ?? item.route ?? null, route: item.route ?? item.path ?? null, sampleCount: item.sampleCount ?? null, p95Ms: item.p95Ms ?? item.p95 ?? null, maxMs: item.maxMs ?? item.max ?? null, overFiveSecondCount: item.overFiveSecondCount ?? null, slowSamples: Array.isArray(item.slowSamples) ? item.slowSamples.slice(0, 1) : [] })) : [],",
|
||||
" pagePerformanceSseStreams: Array.isArray(compact.pagePerformanceSseStreams) ? compact.pagePerformanceSseStreams.slice(0, 2).map((item) => ({ path: item.path ?? item.route ?? null, route: item.route ?? item.path ?? null, sampleCount: item.sampleCount ?? null, streamOpenP95Ms: item.streamOpenP95Ms ?? null, streamOpenMaxMs: item.streamOpenMaxMs ?? null, streamOpenOverFiveSecondCount: item.streamOpenOverFiveSecondCount ?? null, streamLifetimeOverFiveSecondCount: item.streamLifetimeOverFiveSecondCount ?? null })) : [],",
|
||||
" httpErrorGroups: Array.isArray(compact.httpErrorGroups) ? compact.httpErrorGroups.slice(0, 2) : [],",
|
||||
" requestFailedGroups: Array.isArray(compact.requestFailedGroups) ? compact.requestFailedGroups.slice(0, 2) : [],",
|
||||
" consoleAlertGroups: Array.isArray(compact.consoleAlertGroups) ? compact.consoleAlertGroups.slice(0, 2) : [],",
|
||||
@@ -8282,6 +8287,7 @@ function renderWebObserveAnalyzeTable(value: Record<string, unknown>): string {
|
||||
const roundCount = Array.isArray(sampleMetrics?.rounds) ? sampleMetrics.rounds.length : sampleMetrics?.rounds;
|
||||
const turnColumnCount = Array.isArray(sampleMetrics?.turnColumns) ? sampleMetrics.turnColumns.length : sampleMetrics?.turnColumns;
|
||||
const slowApis = Array.isArray(analysis?.pagePerformanceSlowApi) ? analysis.pagePerformanceSlowApi.map(record).filter((item): item is Record<string, unknown> => item !== null).slice(0, 8) : [];
|
||||
const sseStreams = Array.isArray(analysis?.pagePerformanceSseStreams) ? analysis.pagePerformanceSseStreams.map(record).filter((item): item is Record<string, unknown> => item !== null).slice(0, 8) : [];
|
||||
const findings = Array.isArray(analysis?.findings) ? analysis.findings.map(record).filter((item): item is Record<string, unknown> => item !== null).slice(0, 8) : [];
|
||||
const httpErrorGroups = Array.isArray(analysis?.httpErrorGroups) ? analysis.httpErrorGroups.map(record).filter((item): item is Record<string, unknown> => item !== null).slice(0, 8) : [];
|
||||
const requestFailedGroups = Array.isArray(analysis?.requestFailedGroups) ? analysis.requestFailedGroups.map(record).filter((item): item is Record<string, unknown> => item !== null).slice(0, 8) : [];
|
||||
@@ -8602,6 +8608,16 @@ function renderWebObserveAnalyzeTable(value: Record<string, unknown>): string {
|
||||
return rows.length > 0 ? rows : [["-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-"]];
|
||||
})()),
|
||||
"",
|
||||
"Long-lived streams (SSE):",
|
||||
webObserveTable(["PATH", "SAMPLES", "OPEN_P95", "OPEN_MAX", "OPEN>5S", "LIFE>5S"], sseStreams.length > 0 ? sseStreams.map((item) => [
|
||||
webObserveShort(webObserveText(item.path ?? item.route), 52),
|
||||
webObserveText(item.sampleCount),
|
||||
webObserveText(item.streamOpenP95Ms),
|
||||
webObserveText(item.streamOpenMaxMs),
|
||||
webObserveText(item.streamOpenOverFiveSecondCount),
|
||||
webObserveText(item.streamLifetimeOverFiveSecondCount),
|
||||
]) : [["-", "-", "-", "-", "-", "-"]]),
|
||||
"",
|
||||
"Archive page performance:",
|
||||
webObserveTable(["SAME_ORIGIN_API", "SLOW_PATHS", "SLOW_SAMPLES", "WORST_P95"], [[
|
||||
webObserveText(archivePagePerformance?.sameOriginApiPathCount ?? archivePagePerformance?.sameOriginApiPaths),
|
||||
@@ -9063,6 +9079,7 @@ function renderWebObserveAnalyzeResult(result: Record<string, unknown>): Record<
|
||||
const domDiagnostics = webObserveArray(runtimeAlerts.domDiagnostics).slice(-12).map((item) => record(item));
|
||||
const jsonlReadIssues = webObserveArray(analysis.jsonlReadIssues).slice(0, 8).map((item) => record(item));
|
||||
const slowApis = webObserveArray(analysis.pagePerformanceSlowApi).slice(0, 8).map((item) => record(item));
|
||||
const sseStreams = webObserveArray(analysis.pagePerformanceSseStreams).slice(0, 8).map((item) => record(item));
|
||||
const findings = webObserveArray(analysis.findings).slice(0, 8).map((item) => record(item));
|
||||
const id = result.id ?? "-";
|
||||
const renderedText = [
|
||||
@@ -9173,6 +9190,8 @@ function renderWebObserveAnalyzeResult(result: Record<string, unknown>): Record<
|
||||
["PERF", "VALUE"],
|
||||
[
|
||||
["sameOriginApiPaths", pagePerformance.sameOriginApiPathCount],
|
||||
["longLivedStreamPaths", pagePerformance.longLivedStreamPathCount],
|
||||
["streamOpenOver5Samples", pagePerformance.longLivedStreamOpenOverFiveSecondSampleCount],
|
||||
["slowPathCount", pagePerformance.slowPathCount],
|
||||
["slowSampleCount", pagePerformance.slowSampleCount],
|
||||
["worstP95Ms", pagePerformance.worstP95Ms],
|
||||
@@ -9186,6 +9205,13 @@ function renderWebObserveAnalyzeResult(result: Record<string, unknown>): Record<
|
||||
slowApis.map((item) => [item.path, item.p50Ms, item.p75Ms, item.p95Ms, item.overFiveSecondCount, item.sampleCount]),
|
||||
),
|
||||
"",
|
||||
sseStreams.length === 0
|
||||
? "SSE_STREAMS\n-"
|
||||
: webObserveTable(
|
||||
["SSE_STREAM", "OPEN_P95", "OPEN_MAX", "OPEN>5S", "LIFE>5S", "COUNT"],
|
||||
sseStreams.map((item) => [item.path, item.streamOpenP95Ms, item.streamOpenMaxMs, item.streamOpenOverFiveSecondCount, item.streamLifetimeOverFiveSecondCount, item.sampleCount]),
|
||||
),
|
||||
"",
|
||||
findings.length === 0
|
||||
? "FINDINGS\n-"
|
||||
: webObserveTable(
|
||||
|
||||
@@ -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