Skip to content

Commit 87fbb9b

Browse files
committed
Test content generation
1 parent d546aec commit 87fbb9b

File tree

2 files changed

+78
-41
lines changed

2 files changed

+78
-41
lines changed

packages/vertexai/src/methods/chrome-adapter.test.ts

Lines changed: 70 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -173,13 +173,18 @@ describe('ChromeAdapter', () => {
173173
const createStub = stub(aiProvider.languageModel, 'create').resolves(
174174
{} as AILanguageModel
175175
);
176-
const adapter = new ChromeAdapter(aiProvider, 'prefer_on_device');
176+
const onDeviceParams = {} as AILanguageModelCreateOptionsWithSystemPrompt;
177+
const adapter = new ChromeAdapter(
178+
aiProvider,
179+
'prefer_on_device',
180+
onDeviceParams
181+
);
177182
expect(
178183
await adapter.isAvailable({
179184
contents: [{ role: 'user', parts: [{ text: 'hi' }] }]
180185
})
181186
).to.be.false;
182-
expect(createStub).to.have.been.calledOnce;
187+
expect(createStub).to.have.been.calledOnceWith(onDeviceParams);
183188
});
184189
it('avoids redundant downloads', async () => {
185190
const aiProvider = {
@@ -250,4 +255,67 @@ describe('ChromeAdapter', () => {
250255
).to.be.false;
251256
});
252257
});
258+
describe('generateContentOnDevice', () => {
259+
it('Extracts and concats initial prompts', async () => {
260+
const aiProvider = {
261+
languageModel: {
262+
create: () => Promise.resolve({})
263+
}
264+
} as AI;
265+
const factoryStub = stub(aiProvider.languageModel, 'create').resolves({
266+
prompt: s => Promise.resolve(s)
267+
} as AILanguageModel);
268+
const text = ['first', 'second', 'third'];
269+
const onDeviceParams = {
270+
initialPrompts: [{ role: 'user', content: text[0] }]
271+
} as AILanguageModelCreateOptionsWithSystemPrompt;
272+
const adapter = new ChromeAdapter(
273+
aiProvider,
274+
'prefer_on_device',
275+
onDeviceParams
276+
);
277+
const response = await adapter.generateContentOnDevice({
278+
contents: [
279+
{ role: 'model', parts: [{ text: text[1] }] },
280+
{ role: 'user', parts: [{ text: text[2] }] }
281+
]
282+
});
283+
expect(factoryStub).to.have.been.calledOnceWith({
284+
initialPrompts: [
285+
{ role: 'user', content: text[0] },
286+
// Asserts tail is passed as initial prompts, and
287+
// role is normalized from model to assistant.
288+
{ role: 'assistant', content: text[1] }
289+
]
290+
});
291+
expect(response.text()).to.equal(text[2]);
292+
});
293+
it('Extracts system prompt', async () => {
294+
const aiProvider = {
295+
languageModel: {
296+
create: () => Promise.resolve({})
297+
}
298+
} as AI;
299+
const factoryStub = stub(aiProvider.languageModel, 'create').resolves({
300+
prompt: s => Promise.resolve(s)
301+
} as AILanguageModel);
302+
const onDeviceParams = {
303+
systemPrompt: 'be yourself'
304+
} as AILanguageModelCreateOptionsWithSystemPrompt;
305+
const adapter = new ChromeAdapter(
306+
aiProvider,
307+
'prefer_on_device',
308+
onDeviceParams
309+
);
310+
const text = 'hi';
311+
const response = await adapter.generateContentOnDevice({
312+
contents: [{ role: 'user', parts: [{ text }] }]
313+
});
314+
expect(factoryStub).to.have.been.calledOnceWith({
315+
initialPrompts: [],
316+
systemPrompt: onDeviceParams.systemPrompt
317+
});
318+
expect(response.text()).to.equal(text);
319+
});
320+
});
253321
});

packages/vertexai/src/methods/chrome-adapter.ts

Lines changed: 8 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import {
2121
EnhancedGenerateContentResponse,
2222
GenerateContentRequest,
2323
InferenceMode,
24-
Part,
2524
Role,
2625
TextPart
2726
} from '../types';
@@ -78,16 +77,13 @@ export class ChromeAdapter {
7877
async generateContentOnDevice(
7978
request: GenerateContentRequest
8079
): Promise<EnhancedGenerateContentResponse> {
81-
const initialPrompts = ChromeAdapter.toInitialPrompts(request.contents);
80+
const createOptions = this.onDeviceParams || {};
81+
createOptions.initialPrompts ??= [];
82+
const extractedInitialPrompts = ChromeAdapter.toInitialPrompts(request.contents);
8283
// Assumes validation asserted there is at least one initial prompt.
83-
const prompt = initialPrompts.pop()!;
84-
const systemPrompt = ChromeAdapter.toSystemPrompt(
85-
request.systemInstruction
86-
);
87-
const session = await this.session({
88-
initialPrompts,
89-
systemPrompt
90-
});
84+
const prompt = extractedInitialPrompts.pop()!;
85+
createOptions.initialPrompts.push(...extractedInitialPrompts);
86+
const session = await this.session(createOptions);
9187
const result = await session.prompt(prompt.content);
9288
return {
9389
text: () => result,
@@ -152,33 +148,6 @@ export class ChromeAdapter {
152148
this.isDownloading = false;
153149
});
154150
}
155-
private static toSystemPrompt(
156-
prompt: string | Content | Part | undefined
157-
): string | undefined {
158-
if (!prompt) {
159-
return undefined;
160-
}
161-
162-
if (typeof prompt === 'string') {
163-
return prompt;
164-
}
165-
166-
const systemContent = prompt as Content;
167-
if (
168-
systemContent.parts &&
169-
systemContent.parts[0] &&
170-
systemContent.parts[0].text
171-
) {
172-
return systemContent.parts[0].text;
173-
}
174-
175-
const systemPart = prompt as Part;
176-
if (systemPart.text) {
177-
return systemPart.text;
178-
}
179-
180-
return undefined;
181-
}
182151
private static toOnDeviceRole(role: Role): AILanguageModelPromptRole {
183152
return role === 'model' ? 'assistant' : 'user';
184153
}
@@ -192,9 +161,9 @@ export class ChromeAdapter {
192161
}));
193162
}
194163
private async session(
195-
opts: AILanguageModelCreateOptionsWithSystemPrompt
164+
options: AILanguageModelCreateOptionsWithSystemPrompt
196165
): Promise<AILanguageModel> {
197-
const newSession = await this.aiProvider!.languageModel.create(opts);
166+
const newSession = await this.aiProvider!.languageModel.create(options);
198167
if (this.oldSession) {
199168
this.oldSession.destroy();
200169
}

0 commit comments

Comments
 (0)