Skip to content

Commit 010e26d

Browse files
AlinaVarkkidevtools-frontend-scoped@luci-project-accounts.iam.gserviceaccount.com
authored andcommitted
[AI] Add support for Document Latency, Dom Size and Duplicate JS insights widgets
https://screenshot.googleplex.com/37Lsgi8sMuwTdr2 https://screenshot.googleplex.com/8nFto4SB4wJyXYi Bug: 503296282 Change-Id: I3a715bb2fd1b9b32e52d17e5c6b091a2034cdac7 Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/7806627 Reviewed-by: Jack Franklin <jacktfranklin@chromium.org> Auto-Submit: Alina Varkki <alinavarkki@chromium.org> Commit-Queue: Jack Franklin <jacktfranklin@chromium.org>
1 parent 3230dbb commit 010e26d

4 files changed

Lines changed: 137 additions & 1 deletion

File tree

front_end/models/ai_assistance/agents/PerformanceAgent.test.ts

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1069,6 +1069,94 @@ code
10691069
assert.strictEqual(widget?.data.insight, Trace.Insights.Types.InsightKeys.INP_BREAKDOWN);
10701070
assert.strictEqual(widget?.data.insightData, insightSet.model.INPBreakdown);
10711071
});
1072+
1073+
it('yields a PERF_INSIGHT widget for DocumentLatency', async function() {
1074+
insightSet.model.DocumentLatency = {
1075+
insightKey: 'DocumentLatency',
1076+
state: 'fail',
1077+
} as unknown as Trace.Insights.Types.InsightModels['DocumentLatency'];
1078+
1079+
const agent = createAgentForConversation({
1080+
aidaClient: mockAidaClient([
1081+
[{
1082+
explanation: '',
1083+
functionCalls: [
1084+
{name: 'getInsightDetails', args: {insightSetId: insightSet.id, insightName: 'DocumentLatency'}},
1085+
]
1086+
}],
1087+
[{explanation: 'done'}]
1088+
])
1089+
});
1090+
1091+
const responses = await Array.fromAsync(agent.run('test', {selected: context}));
1092+
const actions = responses.filter(r => r.type === AiAgent.ResponseType.ACTION);
1093+
assert.lengthOf(actions, 1);
1094+
assert.exists(actions[0].widgets);
1095+
const widget = actions[0].widgets?.find(w => w.name === 'PERF_INSIGHT');
1096+
assert.exists(widget);
1097+
assert.strictEqual(widget?.data.insight, Trace.Insights.Types.InsightKeys.DOCUMENT_LATENCY);
1098+
assert.strictEqual(widget?.data.insightData, insightSet.model.DocumentLatency);
1099+
});
1100+
1101+
it('yields a PERF_INSIGHT widget for DOMSize', async function() {
1102+
insightSet.model.DOMSize = {
1103+
insightKey: 'DOMSize',
1104+
state: 'fail',
1105+
largeLayoutUpdates: [],
1106+
largeStyleRecalcs: [],
1107+
} as unknown as Trace.Insights.Types.InsightModels['DOMSize'];
1108+
1109+
const agent = createAgentForConversation({
1110+
aidaClient: mockAidaClient([
1111+
[{
1112+
explanation: '',
1113+
functionCalls: [
1114+
{name: 'getInsightDetails', args: {insightSetId: insightSet.id, insightName: 'DOMSize'}},
1115+
]
1116+
}],
1117+
[{explanation: 'done'}]
1118+
])
1119+
});
1120+
1121+
const responses = await Array.fromAsync(agent.run('test', {selected: context}));
1122+
const actions = responses.filter(r => r.type === AiAgent.ResponseType.ACTION);
1123+
assert.lengthOf(actions, 1);
1124+
assert.exists(actions[0].widgets);
1125+
const widget = actions[0].widgets?.find(w => w.name === 'PERF_INSIGHT');
1126+
assert.exists(widget);
1127+
assert.strictEqual(widget?.data.insight, Trace.Insights.Types.InsightKeys.DOM_SIZE);
1128+
assert.strictEqual(widget?.data.insightData, insightSet.model.DOMSize);
1129+
});
1130+
1131+
it('yields a PERF_INSIGHT widget for DuplicatedJavaScript', async function() {
1132+
insightSet.model.DuplicatedJavaScript = {
1133+
insightKey: 'DuplicatedJavaScript',
1134+
state: 'fail',
1135+
duplicationGroupedByNodeModules: new Map(),
1136+
wastedBytes: 0,
1137+
} as unknown as Trace.Insights.Types.InsightModels['DuplicatedJavaScript'];
1138+
1139+
const agent = createAgentForConversation({
1140+
aidaClient: mockAidaClient([
1141+
[{
1142+
explanation: '',
1143+
functionCalls: [
1144+
{name: 'getInsightDetails', args: {insightSetId: insightSet.id, insightName: 'DuplicatedJavaScript'}},
1145+
]
1146+
}],
1147+
[{explanation: 'done'}]
1148+
])
1149+
});
1150+
1151+
const responses = await Array.fromAsync(agent.run('test', {selected: context}));
1152+
const actions = responses.filter(r => r.type === AiAgent.ResponseType.ACTION);
1153+
assert.lengthOf(actions, 1);
1154+
assert.exists(actions[0].widgets);
1155+
const widget = actions[0].widgets?.find(w => w.name === 'PERF_INSIGHT');
1156+
assert.exists(widget);
1157+
assert.strictEqual(widget?.data.insight, Trace.Insights.Types.InsightKeys.DUPLICATE_JAVASCRIPT);
1158+
assert.strictEqual(widget?.data.insightData, insightSet.model.DuplicatedJavaScript);
1159+
});
10721160
});
10731161

10741162
it('yields a BOTTOM_UP_TREE widget when getDetailedCallTree is called', async function() {

front_end/models/ai_assistance/agents/PerformanceAgent.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ Note: if the user asks a specific question about the trace (such as "What is my
171171
- \`nav-to-lcp\` (navigation to LCP)
172172
- \`lcp-ttfb\` (LCP TTFB phase)
173173
- \`lcp-render-delay\` (LCP render delay phase)
174-
- Insight names: \`LCPBreakdown\`, \`CLSCulprits\`, \`RenderBlocking\`, \`NetworkDependencyTree\`, \`ImageDelivery\`, \`FontDisplay\`, \`ThirdParties\`, \`ForcedReflow\`, \`Cache\`, \`DOMSize\`, \`INPBreakdown\`
174+
- Insight names: \`LCPBreakdown\`, \`INPBreakdown\`, \`CLSCulprits\`, \`ThirdParties\`, \`DocumentLatency\`, \`DOMSize\`, \`DuplicatedJavaScript\`, \`FontDisplay\`, \`ForcedReflow\`, \`ImageDelivery\`, \`LCPDiscovery\`, \`LegacyJavaScript\`, \`NetworkDependencyTree\`, \`RenderBlocking\`, \`SlowCSSSelector\`, \`Viewport\`, \`ModernHTTP\`, \`Cache\`, \`CharacterSet\`
175175
- Navigation IDs: \`NAVIGATION_0\`, \`NAVIGATION_1\`, etc.
176176
- Use \`getEventByKey\` to get data on a specific trace event. This is great for root-cause analysis or validating any assumptions.
177177
- Provide clear, actionable recommendations. Avoid technical jargon unless necessary, and explain any technical terms used.
@@ -232,6 +232,9 @@ const SUPPORTED_INSIGHT_WIDGETS = new Set<Trace.Insights.Types.InsightKeys>([
232232
Trace.Insights.Types.InsightKeys.FORCED_REFLOW,
233233
Trace.Insights.Types.InsightKeys.CACHE,
234234
Trace.Insights.Types.InsightKeys.INP_BREAKDOWN,
235+
Trace.Insights.Types.InsightKeys.DOCUMENT_LATENCY,
236+
Trace.Insights.Types.InsightKeys.DOM_SIZE,
237+
Trace.Insights.Types.InsightKeys.DUPLICATE_JAVASCRIPT,
235238
]);
236239

237240
export class PerformanceTraceContext extends ConversationContext<AgentFocus> {

front_end/panels/ai_assistance/components/ChatMessage.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,30 @@ const UIStringsNotTranslate = {
303303
* @description Title for the INP breakdown widget.
304304
*/
305305
inpBreakdown: 'INP breakdown',
306+
/**
307+
* @description Accessible label for the reveal button in the document latency widget.
308+
*/
309+
revealDocumentLatency: 'Reveal document latency',
310+
/**
311+
* @description Title for the document latency widget.
312+
*/
313+
documentLatency: 'Document latency',
314+
/**
315+
* @description Accessible label for the reveal button in the DOM size widget.
316+
*/
317+
revealDomSize: 'Reveal DOM size',
318+
/**
319+
* @description Title for the DOM size widget.
320+
*/
321+
domSize: 'DOM size',
322+
/**
323+
* @description Accessible label for the reveal button in the duplicated JavaScript widget.
324+
*/
325+
revealDuplicateJavaScript: 'Reveal duplicated JavaScript',
326+
/**
327+
* @description Title for the duplicated JavaScript widget.
328+
*/
329+
duplicateJavaScript: 'Duplicated JavaScript',
306330
} as const;
307331

308332
export interface Step {
@@ -1040,6 +1064,24 @@ const INSIGHT_METADATA: Record<string, {
10401064
title: UIStringsNotTranslate.inpBreakdown,
10411065
jslog: 'inp-breakdown-widget',
10421066
},
1067+
[Trace.Insights.Types.InsightKeys.DOCUMENT_LATENCY]: {
1068+
component: TimelineInsights.DocumentLatency.DocumentLatency,
1069+
accessibleLabel: UIStringsNotTranslate.revealDocumentLatency,
1070+
title: UIStringsNotTranslate.documentLatency,
1071+
jslog: 'document-latency-widget',
1072+
},
1073+
[Trace.Insights.Types.InsightKeys.DOM_SIZE]: {
1074+
component: TimelineInsights.DOMSize.DOMSize,
1075+
accessibleLabel: UIStringsNotTranslate.revealDomSize,
1076+
title: UIStringsNotTranslate.domSize,
1077+
jslog: 'dom-size-widget',
1078+
},
1079+
[Trace.Insights.Types.InsightKeys.DUPLICATE_JAVASCRIPT]: {
1080+
component: TimelineInsights.DuplicatedJavaScript.DuplicatedJavaScript,
1081+
accessibleLabel: UIStringsNotTranslate.revealDuplicateJavaScript,
1082+
title: UIStringsNotTranslate.duplicateJavaScript,
1083+
jslog: 'duplicate-javascript-widget',
1084+
},
10431085
};
10441086

10451087
function renderInsightWidget<T extends Trace.Insights.Types.InsightModel>(

front_end/ui/visual_logging/KnownContextValues.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1370,6 +1370,7 @@ export const knownContextValues = new Set([
13701370
'distance',
13711371
'dock-side',
13721372
'document',
1373+
'document-latency-widget',
13731374
'document.write',
13741375
'documentation',
13751376
'documents',
@@ -1385,6 +1386,7 @@ export const knownContextValues = new Set([
13851386
'dom-node-inserted-into-document',
13861387
'dom-node-removed',
13871388
'dom-node-removed-from-document',
1389+
'dom-size-widget',
13881390
'dom-snapshot',
13891391
'dom-subtree-modified',
13901392
'dom-window.close',
@@ -1423,6 +1425,7 @@ export const knownContextValues = new Set([
14231425
'drjones.sources-panel-context.performance',
14241426
'drjones.sources-panel-context.script',
14251427
'drop',
1428+
'duplicate-javascript-widget',
14261429
'durable-messages',
14271430
'duration',
14281431
'durationchange',

0 commit comments

Comments
 (0)