Skip to content

Commit 333602e

Browse files
jackfranklinDevtools-frontend LUCI CQ
authored andcommitted
RPP: add getMainThreadActivity function to Insights AI
This CL enables the LLM to query for relevant main thread activity for the given insight. Note that we may need to experiment with alternative versions of this that are leaner in order to avoid eating up context windows, but this is a good starting point. Bug: 394552594 Change-Id: I2017f190f783743f813abea57e9dfca92c9f6855 Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/6275957 Auto-Submit: Jack Franklin <[email protected]> Reviewed-by: Alex Rudenko <[email protected]> Commit-Queue: Jack Franklin <[email protected]>
1 parent 510fef6 commit 333602e

File tree

2 files changed

+70
-0
lines changed

2 files changed

+70
-0
lines changed

front_end/panels/ai_assistance/agents/PerformanceInsightsAgent.test.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,5 +131,30 @@ What is this?`;
131131
assert.deepEqual(
132132
action, {type: 'action' as ActionResponse['type'], output: expectedOutput, code: undefined, canceled: false});
133133
});
134+
135+
it('calls getMainThreadActivity', async function() {
136+
const {parsedTrace, insights} = await TraceLoader.traceEngine(this, 'lcp-discovery-delay.json.gz');
137+
assert.isOk(insights);
138+
const [firstNav] = parsedTrace.Meta.mainFrameNavigations;
139+
const lcpPhases = getInsightOrError('LCPPhases', insights, firstNav);
140+
const agent = new PerformanceInsightsAgent({
141+
aidaClient: mockAidaClient(
142+
[[{explanation: '', functionCalls: [{name: 'getMainThreadActivity', args: {}}]}], [{explanation: 'done'}]])
143+
});
144+
const activeInsight = new TimelineUtils.InsightAIContext.ActiveInsight(lcpPhases, parsedTrace);
145+
const context = new InsightContext(activeInsight);
146+
147+
const responses = await Array.fromAsync(agent.run('test', {selected: context}));
148+
const action = responses.find(response => response.type === ResponseType.ACTION);
149+
150+
const expectedTree = TimelineUtils.InsightAIContext.AIQueries.mainThreadActivity(lcpPhases, parsedTrace);
151+
assert.isOk(expectedTree);
152+
153+
const expectedOutput = JSON.stringify({activity: expectedTree.serialize()});
154+
155+
assert.exists(action);
156+
assert.deepEqual(
157+
action, {type: 'action' as ActionResponse['type'], output: expectedOutput, code: undefined, canceled: false});
158+
});
134159
});
135160
});

front_end/panels/ai_assistance/agents/PerformanceInsightsAgent.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,51 @@ export class PerformanceInsightsAgent extends AiAgent<TimelineUtils.InsightAICon
135135
return {result: {requests: formatted}};
136136
},
137137
});
138+
139+
this.declareFunction<Record<never, unknown>, {activity: string}>('getMainThreadActivity', {
140+
description: `Returns the main thread activity for the selected insight.
141+
The tree is represented as a call frame with a root task and a series of children.
142+
The format of each callframe is:
143+
144+
Node: $id – $name
145+
Selected: true
146+
dur: $duration
147+
self: $self
148+
URL #: $url_number
149+
Children:
150+
* $child.id – $child.name
151+
152+
The fields are:
153+
154+
* name: A short string naming the callframe (e.g. 'Evaluate Script' or the JS function name 'InitializeApp')
155+
* id: A numerical identifier for the callframe
156+
* Selected: Set to true if this callframe is the one the user selected.
157+
* url_number: The number of the URL referenced in the "All URLs" list
158+
* dur: The total duration of the callframe (includes time spent in its descendants), in milliseconds.
159+
* self: The self duration of the callframe (excludes time spent in its descendants), in milliseconds. If omitted, assume the value is 0.
160+
* children: An list of child callframes, each denoted by their id and name`,
161+
parameters: {
162+
type: Host.AidaClient.ParametersTypes.OBJECT,
163+
description: '',
164+
nullable: true,
165+
properties: {},
166+
},
167+
handler: async () => {
168+
if (!this.#insight) {
169+
return {error: 'No insight available'};
170+
}
171+
const activeInsight = this.#insight.getItem();
172+
const tree = TimelineUtils.InsightAIContext.AIQueries.mainThreadActivity(
173+
activeInsight.insight,
174+
activeInsight.parsedTrace,
175+
);
176+
if (!tree) {
177+
return {error: 'No main thread activity found'};
178+
}
179+
return {result: {activity: tree.serialize()}};
180+
},
181+
182+
});
138183
}
139184

140185
override async enhanceQuery(

0 commit comments

Comments
 (0)