Skip to content

Commit 02a09c2

Browse files
jackfranklinDevtools-frontend LUCI CQ
authored andcommitted
AI: add support for facts to AidaClient
Change-Id: Ic8d73f09931b823c9faabc3d383ce9e01a8e8fca Bug: 408172181 Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/6426044 Commit-Queue: Alex Rudenko <[email protected]> Reviewed-by: Alex Rudenko <[email protected]> Auto-Submit: Jack Franklin <[email protected]>
1 parent e9a63bb commit 02a09c2

File tree

6 files changed

+90
-1
lines changed

6 files changed

+90
-1
lines changed

front_end/core/host/AidaClient.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,25 @@ export enum UserTier {
145145
PUBLIC = 3,
146146
}
147147

148+
// Googlers: see the Aida `retrieval` proto; this type is based on that.
149+
export interface RequestFactMetadata {
150+
/**
151+
* A description of where the fact comes from.
152+
*/
153+
source: string;
154+
/**
155+
* Optional: a score to give this fact. Used because if there are more facts than space in the context window, higher scoring facts will be prioritised.
156+
*/
157+
score?: number;
158+
}
159+
export interface RequestFact {
160+
/**
161+
* Content of the fact.
162+
*/
163+
text: string;
164+
metadata: RequestFactMetadata;
165+
}
166+
148167
export type RpcGlobalId = string|number;
149168

150169
export interface AidaRequest {
@@ -156,6 +175,7 @@ export interface AidaRequest {
156175
historical_contexts?: Content[];
157176
// eslint-disable-next-line @typescript-eslint/naming-convention
158177
function_declarations?: FunctionDeclaration[];
178+
facts?: RequestFact[];
159179
options?: {
160180
temperature?: number,
161181
// eslint-disable-next-line @typescript-eslint/naming-convention

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

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,47 @@ describeWithEnvironment('AiAgent', () => {
185185
assert.isUndefined(request.historical_contexts);
186186
});
187187

188+
it('builds a request with a fact', async () => {
189+
const agent = new AiAgentMock({
190+
aidaClient: mockAidaClient([[{
191+
explanation: 'answer',
192+
}]]),
193+
serverSideLoggingEnabled: true,
194+
});
195+
const fact: Host.AidaClient.RequestFact = {text: 'This is a fact', metadata: {source: 'devtools'}};
196+
agent.addFact(fact);
197+
await Array.fromAsync(agent.run('question', {selected: null}));
198+
const request = agent.buildRequest({text: 'test input'}, Host.AidaClient.Role.USER);
199+
assert.deepEqual(request.facts, [fact]);
200+
});
201+
202+
it('can manage multiple facts and remove them', async () => {
203+
const agent = new AiAgentMock({
204+
aidaClient: mockAidaClient([[{
205+
explanation: 'answer',
206+
}]]),
207+
serverSideLoggingEnabled: true,
208+
});
209+
const f1: Host.AidaClient.RequestFact = {text: 'f1', metadata: {source: 'devtools'}};
210+
const f2 = {text: 'f2', metadata: {source: 'devtools'}};
211+
agent.addFact(f1);
212+
agent.addFact(f2);
213+
214+
await Array.fromAsync(agent.run('question', {selected: null}));
215+
const request1 = agent.buildRequest({text: 'test input'}, Host.AidaClient.Role.USER);
216+
assert.deepEqual(request1.facts, [f1, f2]);
217+
218+
agent.removeFact(f1);
219+
await Array.fromAsync(agent.run('question', {selected: null}));
220+
const request2 = agent.buildRequest({text: 'test input'}, Host.AidaClient.Role.USER);
221+
assert.deepEqual(request2.facts, [f2]);
222+
223+
agent.clearFacts();
224+
await Array.fromAsync(agent.run('question', {selected: null}));
225+
const request3 = agent.buildRequest({text: 'test input'}, Host.AidaClient.Role.USER);
226+
assert.isUndefined(request3.facts);
227+
});
228+
188229
it('builds a request with chat history', async () => {
189230
const agent = new AiAgentMock({
190231
aidaClient: mockAidaClient([[{

front_end/models/ai_assistance/agents/AiAgent.ts

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,8 @@ export abstract class AiAgent<T> {
257257
#id: string = crypto.randomUUID();
258258
#history: Host.AidaClient.Content[] = [];
259259

260+
#facts: Set<Host.AidaClient.RequestFact> = new Set<Host.AidaClient.RequestFact>();
261+
260262
constructor(opts: AgentOptions) {
261263
this.#aidaClient = opts.aidaClient;
262264
this.#serverSideLoggingEnabled = opts.serverSideLoggingEnabled ?? false;
@@ -268,6 +270,28 @@ export abstract class AiAgent<T> {
268270
return query;
269271
}
270272

273+
currentFacts(): ReadonlySet<Host.AidaClient.RequestFact> {
274+
return this.#facts;
275+
}
276+
277+
/**
278+
* Add a fact which will be sent for any subsequent requests.
279+
* Returns the new list of all facts.
280+
* Facts are never automatically removed.
281+
*/
282+
addFact(fact: Host.AidaClient.RequestFact): ReadonlySet<Host.AidaClient.RequestFact> {
283+
this.#facts.add(fact);
284+
return this.#facts;
285+
}
286+
287+
removeFact(fact: Host.AidaClient.RequestFact): boolean {
288+
return this.#facts.delete(fact);
289+
}
290+
291+
clearFacts(): void {
292+
this.#facts.clear();
293+
}
294+
271295
buildRequest(
272296
part: Host.AidaClient.Part|Host.AidaClient.Part[],
273297
role: Host.AidaClient.Role.USER|Host.AidaClient.Role.ROLE_UNSPECIFIED): Host.AidaClient.AidaRequest {
@@ -291,13 +315,14 @@ export abstract class AiAgent<T> {
291315
const enableAidaFunctionCalling = declarations.length && !this.functionCallEmulationEnabled;
292316
const userTier = Host.AidaClient.convertToUserTierEnum(this.userTier);
293317
const premable = userTier === Host.AidaClient.UserTier.TESTERS ? this.preamble : undefined;
318+
const facts = Array.from(this.#facts);
294319
const request: Host.AidaClient.AidaRequest = {
295320
client: Host.AidaClient.CLIENT_NAME,
296-
297321
current_message: currentMessage,
298322
preamble: premable,
299323

300324
historical_contexts: history.length ? history : undefined,
325+
facts: facts.length ? facts : undefined,
301326

302327
...(enableAidaFunctionCalling ? {function_declarations: declarations} : {}),
303328
options: {

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ describeWithMockConnection('FileAgent', () => {
9494
parts: [{text: 'answer'}],
9595
},
9696
],
97+
facts: undefined,
9798
metadata: {
9899
disable_user_content_logging: false,
99100
string_session_id: 'sessionId',

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ describeWithEnvironment('PerformanceAgent', () => {
109109
parts: [{text: 'answer'}],
110110
},
111111
],
112+
facts: undefined,
112113
metadata: {
113114
disable_user_content_logging: false,
114115
string_session_id: 'sessionId',

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -545,6 +545,7 @@ c`;
545545
parts: [{text: 'ANSWER: answer'}],
546546
},
547547
],
548+
facts: undefined,
548549
metadata: {
549550
disable_user_content_logging: false,
550551
string_session_id: 'sessionId',

0 commit comments

Comments
 (0)