Skip to content

Commit 5afafe9

Browse files
ergunshDevtools-frontend LUCI CQ
authored andcommitted
[AiAgent] Include explanation in function call history entry
When the model provides both a text explanation and a function call in the same response, ensure both are added to the conversation history as a single message with multiple parts. Previously, the explanation was treated as a standalone answer which caused the explanation from the function calls to be not included in the history. This CL: * Modifies AiAgent.ts to avoid pushing a separate text-only history entry when a function call is also present. * Passes the explanation text to `#callFunction`. * Updates #callFunction to combine the explanation (if present) and the function call into a single multi-part history entry. * Adds a unit test in AiAgent.test.ts to verify that the history is correctly constructed with both the explanation and the function call. Fixed: 464443653 Change-Id: I2b8e47eb2be7329efe78cd10a0c6b873123f6f23 Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/7209254 Reviewed-by: Alex Rudenko <alexrudenko@chromium.org> Auto-Submit: Ergün Erdoğmuş <ergunsh@chromium.org> Commit-Queue: Nikolay Vitkov <nvitkov@chromium.org> Reviewed-by: Nikolay Vitkov <nvitkov@chromium.org>
1 parent 9b261e7 commit 5afafe9

4 files changed

Lines changed: 77 additions & 16 deletions

File tree

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

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -473,5 +473,46 @@ describeWithEnvironment('AiAgent', () => {
473473
}],
474474
);
475475
});
476+
477+
it('should add explanation to history when function call is present', async () => {
478+
const agent = new AgentWithFunction({
479+
aidaClient: mockAidaClient([
480+
[
481+
{
482+
explanation: 'This is the explanation',
483+
functionCalls: [{
484+
name: 'testFn',
485+
args: {arg: 'test'},
486+
}],
487+
},
488+
],
489+
[{
490+
explanation: 'Final answer',
491+
}]
492+
]),
493+
});
494+
495+
await Array.fromAsync(agent.run('query', {selected: mockConversationContext()}));
496+
497+
const request = agent.buildRequest({text: 'test input'}, Host.AidaClient.Role.USER);
498+
// History should contain:
499+
// 1. User query
500+
// 2. Model explanation + function call
501+
// 3. Function result (user role)
502+
// 4. Model final answer
503+
assert.lengthOf(request.historical_contexts ?? [], 4);
504+
assert.deepEqual(request.historical_contexts?.[1], {
505+
role: Host.AidaClient.Role.MODEL,
506+
parts: [
507+
{text: 'This is the explanation'},
508+
{
509+
functionCall: {
510+
name: 'testFn',
511+
args: {arg: 'test'},
512+
},
513+
},
514+
],
515+
});
516+
});
476517
});
477518
});

front_end/models/ai_assistance/agents/AiAgent.ts

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -573,12 +573,14 @@ export abstract class AiAgent<T> {
573573
if (!('answer' in parsedResponse)) {
574574
throw new Error('Expected a completed response to have an answer');
575575
}
576-
this.#history.push({
577-
parts: [{
578-
text: parsedResponse.answer,
579-
}],
580-
role: Host.AidaClient.Role.MODEL,
581-
});
576+
if (!functionCall) {
577+
this.#history.push({
578+
parts: [{
579+
text: parsedResponse.answer,
580+
}],
581+
role: Host.AidaClient.Role.MODEL,
582+
});
583+
}
582584
Host.userMetrics.actionTaken(Host.UserMetrics.Action.AiAssistanceAnswerReceived);
583585
yield {
584586
type: ResponseType.ANSWER,
@@ -587,12 +589,17 @@ export abstract class AiAgent<T> {
587589
complete: true,
588590
rpcId,
589591
};
590-
break;
592+
if (!functionCall) {
593+
break;
594+
}
591595
}
592596

593597
if (functionCall) {
594598
try {
595-
const result = yield* this.#callFunction(functionCall.name, functionCall.args, options);
599+
const result = yield* this.#callFunction(functionCall.name, functionCall.args, {
600+
...options,
601+
explanation: textResponse,
602+
});
596603
if (options.signal?.aborted) {
597604
yield this.#createErrorResponse(ErrorType.ABORT);
598605
break;
@@ -623,19 +630,26 @@ export abstract class AiAgent<T> {
623630
#callFunction(
624631
name: string,
625632
args: Record<string, unknown>,
626-
options?: FunctionHandlerOptions,
633+
options?: FunctionHandlerOptions&{explanation?: string},
627634
): AsyncGenerator<FunctionCallResponseData, {result: unknown}> {
628635
const call = this.#functionDeclarations.get(name);
629636
if (!call) {
630637
throw new Error(`Function ${name} is not found.`);
631638
}
639+
const parts: Host.AidaClient.Part[] = [];
640+
if (options?.explanation) {
641+
parts.push({
642+
text: options.explanation,
643+
});
644+
}
645+
parts.push({
646+
functionCall: {
647+
name,
648+
args,
649+
},
650+
});
632651
this.#history.push({
633-
parts: [{
634-
functionCall: {
635-
name,
636-
args,
637-
},
638-
}],
652+
parts,
639653
role: Host.AidaClient.Role.MODEL,
640654
});
641655

@@ -744,6 +758,7 @@ export abstract class AiAgent<T> {
744758
rpcId,
745759
functionCall: aidaResponse.functionCalls[0],
746760
completed: true,
761+
text: aidaResponse.explanation,
747762
};
748763
break;
749764
}

front_end/models/ai_assistance/agents/StylingAgent.snapshot.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,11 @@ Content:
398398
{
399399
"type": "querying"
400400
},
401+
{
402+
"type": "answer",
403+
"text": "this is my text before the actual answer",
404+
"complete": true
405+
},
401406
{
402407
"type": "thought",
403408
"thought": "I am thinking."

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -530,7 +530,7 @@ describeWithEnvironment('StylingAgent', function() {
530530
name: 'executeJavaScript',
531531
args: {thought: 'I am thinking.', code: 'console.log(\'hello\');'},
532532
}],
533-
explanation: 'this is the answer',
533+
explanation: 'this is my text before the actual answer',
534534
}],
535535
[{
536536
explanation: 'this is the actual answer',

0 commit comments

Comments
 (0)