Skip to content

Commit 2b66c51

Browse files
Connor ClarkDevtools-frontend LUCI CQ
authored andcommitted
[AI] Use response suggestions UX in performance agent
Bug: 425270067 Change-Id: Ia7866ad0159ff8cfc4a32699ea6f3ae3d1625bd6 Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/6968920 Auto-Submit: Connor Clark <cjamcl@chromium.org> Reviewed-by: Paul Irish <paulirish@chromium.org> Commit-Queue: Connor Clark <cjamcl@chromium.org>
1 parent 7e62229 commit 2b66c51

1 file changed

Lines changed: 60 additions & 9 deletions

File tree

front_end/models/ai_assistance/agents/PerformanceAgent.ts

Lines changed: 60 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,8 @@ When referring to a trace event that has a corresponding \`eventKey\`, annotate
134134
- When referring to an event that is a long task: [Long task](#r-123)
135135
- When referring to a URL for which you know the eventKey of: [https://www.example.com](#s-1827)
136136
- Never show the eventKey (like "eventKey: s-1852"); instead, use a markdown link as described above.
137+
138+
When asking the user to make a choice between multiple options, output a list of choices at the end of your text response. The format is \`SUGGESTIONS: ["suggestion1", "suggestion2", "suggestion3"]\`. This MUST start on a newline, and be a single line.
137139
`;
138140

139141
const callFrameDataFormatDescription = `Each call frame is presented in the following format:
@@ -418,23 +420,72 @@ export class PerformanceAgent extends AiAgent<AgentFocus> {
418420
});
419421
}
420422

421-
override parseTextResponse(response: string): ParsedResponse {
422-
response = this.#parseForKnownUrls(response);
423+
#parseSuggestions(text: string): ParsedResponse {
424+
if (!text) {
425+
return {answer: ''};
426+
}
427+
428+
const lines = text.split('\n');
429+
const answerLines: string[] = [];
430+
let suggestions: [string, ...string[]]|undefined;
431+
432+
for (const line of lines) {
433+
const trimmed = line.trim();
434+
if (trimmed.startsWith('SUGGESTIONS:')) {
435+
try {
436+
// TODO: Do basic validation this is an array with strings
437+
suggestions = JSON.parse(trimmed.substring('SUGGESTIONS:'.length).trim());
438+
} catch {
439+
}
440+
} else {
441+
answerLines.push(line);
442+
}
443+
}
444+
445+
// Sometimes the model fails to put the SUGGESTIONS text on its own line. Handle
446+
// the case where the suggestions are part of the last line of the answer.
447+
if (!suggestions && answerLines.at(-1)?.includes('SUGGESTIONS:')) {
448+
const [answer, suggestionsText] = answerLines[answerLines.length - 1].split('SUGGESTIONS:', 2);
449+
try {
450+
// TODO: Do basic validation this is an array with strings
451+
suggestions = JSON.parse(suggestionsText.trim().substring('SUGGESTIONS:'.length).trim());
452+
} catch {
453+
}
454+
answerLines[answerLines.length - 1] = answer;
455+
}
423456

457+
const response: ParsedResponse = {
458+
// If we could not parse the parts, consider the response to be an
459+
// answer.
460+
answer: answerLines.join('\n'),
461+
};
462+
463+
if (suggestions) {
464+
response.suggestions = suggestions;
465+
}
466+
467+
return response;
468+
}
469+
470+
#parseMarkdown(response: string): string {
424471
/**
425472
* Sometimes the LLM responds with code chunks that wrap a text based markdown response.
426473
* If this happens, we want to remove those before continuing.
427474
* See b/405054694 for more details.
428475
*/
429-
const trimmed = response.trim();
430476
const FIVE_BACKTICKS = '`````';
431-
if (trimmed.startsWith(FIVE_BACKTICKS) && trimmed.endsWith(FIVE_BACKTICKS)) {
432-
// Purposefully use the trimmed text here; we might as well remove any
433-
// newlines that are at the very start or end.
434-
const stripped = trimmed.slice(FIVE_BACKTICKS.length, -FIVE_BACKTICKS.length);
435-
return super.parseTextResponse(stripped);
477+
if (response.startsWith(FIVE_BACKTICKS) && response.endsWith(FIVE_BACKTICKS)) {
478+
return response.slice(FIVE_BACKTICKS.length, -FIVE_BACKTICKS.length);
436479
}
437-
return super.parseTextResponse(response);
480+
481+
return response;
482+
}
483+
484+
override parseTextResponse(response: string): ParsedResponse {
485+
response = response.trim();
486+
response = this.#parseForKnownUrls(response);
487+
response = this.#parseMarkdown(response);
488+
return this.#parseSuggestions(response);
438489
}
439490

440491
override async enhanceQuery(query: string, context: PerformanceTraceContext|null): Promise<string> {

0 commit comments

Comments
 (0)