Skip to content

Commit e839743

Browse files
authored
VinF Hybrid Inference: throw if only_on_device and model is unavailable (#8965)
1 parent 5fa83b3 commit e839743

File tree

3 files changed

+43
-79
lines changed

3 files changed

+43
-79
lines changed

e2e/sample-apps/modular.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,7 @@ async function callVertexAI(app) {
314314
console.log('[VERTEXAI] start');
315315
const vertexAI = getVertexAI(app);
316316
const model = getGenerativeModel(vertexAI, {
317-
mode: 'prefer_on_device'
317+
mode: 'only_on_device'
318318
});
319319
const singleResult = await model.generateContent([
320320
{ text: 'describe the following:' },

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

+21-60
Original file line numberDiff line numberDiff line change
@@ -61,19 +61,8 @@ describe('ChromeAdapter', () => {
6161
})
6262
).to.be.false;
6363
});
64-
it('returns false if AI API is undefined', async () => {
65-
const adapter = new ChromeAdapter(undefined, 'prefer_on_device');
66-
expect(
67-
await adapter.isAvailable({
68-
contents: []
69-
})
70-
).to.be.false;
71-
});
7264
it('returns false if LanguageModel API is undefined', async () => {
73-
const adapter = new ChromeAdapter(
74-
{} as LanguageModel,
75-
'prefer_on_device'
76-
);
65+
const adapter = new ChromeAdapter(undefined, 'prefer_on_device');
7766
expect(
7867
await adapter.isAvailable({
7968
contents: []
@@ -82,7 +71,9 @@ describe('ChromeAdapter', () => {
8271
});
8372
it('returns false if request contents empty', async () => {
8473
const adapter = new ChromeAdapter(
85-
{} as LanguageModel,
74+
{
75+
availability: async () => Availability.available
76+
} as LanguageModel,
8677
'prefer_on_device'
8778
);
8879
expect(
@@ -93,7 +84,9 @@ describe('ChromeAdapter', () => {
9384
});
9485
it('returns false if request content has function role', async () => {
9586
const adapter = new ChromeAdapter(
96-
{} as LanguageModel,
87+
{
88+
availability: async () => Availability.available
89+
} as LanguageModel,
9790
'prefer_on_device'
9891
);
9992
expect(
@@ -107,51 +100,6 @@ describe('ChromeAdapter', () => {
107100
})
108101
).to.be.false;
109102
});
110-
it('returns false if request system instruction has function role', async () => {
111-
const adapter = new ChromeAdapter(
112-
{} as LanguageModel,
113-
'prefer_on_device'
114-
);
115-
expect(
116-
await adapter.isAvailable({
117-
contents: [],
118-
systemInstruction: {
119-
role: 'function',
120-
parts: []
121-
}
122-
})
123-
).to.be.false;
124-
});
125-
it('returns false if request system instruction has multiple parts', async () => {
126-
const adapter = new ChromeAdapter(
127-
{} as LanguageModel,
128-
'prefer_on_device'
129-
);
130-
expect(
131-
await adapter.isAvailable({
132-
contents: [],
133-
systemInstruction: {
134-
role: 'function',
135-
parts: [{ text: 'a' }, { text: 'b' }]
136-
}
137-
})
138-
).to.be.false;
139-
});
140-
it('returns false if request system instruction has non-text part', async () => {
141-
const adapter = new ChromeAdapter(
142-
{} as LanguageModel,
143-
'prefer_on_device'
144-
);
145-
expect(
146-
await adapter.isAvailable({
147-
contents: [],
148-
systemInstruction: {
149-
role: 'function',
150-
parts: [{ inlineData: { mimeType: 'a', data: 'b' } }]
151-
}
152-
})
153-
).to.be.false;
154-
});
155103
it('returns true if model is readily available', async () => {
156104
const languageModelProvider = {
157105
availability: () => Promise.resolve(Availability.available)
@@ -246,7 +194,20 @@ describe('ChromeAdapter', () => {
246194
).to.be.false;
247195
});
248196
});
249-
describe('generateContentOnDevice', () => {
197+
describe('generateContent', () => {
198+
it('throws if Chrome API is undefined', async () => {
199+
const adapter = new ChromeAdapter(undefined, 'only_on_device');
200+
await expect(
201+
adapter.generateContent({
202+
contents: []
203+
})
204+
)
205+
.to.eventually.be.rejectedWith(
206+
VertexAIError,
207+
'Chrome AI requested for unsupported browser version.'
208+
)
209+
.and.have.property('code', VertexAIErrorCode.REQUEST_ERROR);
210+
});
250211
it('generates content', async () => {
251212
const languageModelProvider = {
252213
create: () => Promise.resolve({})

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

+21-18
Original file line numberDiff line numberDiff line change
@@ -60,29 +60,26 @@ export class ChromeAdapter {
6060
* separation of concerns.</p>
6161
*/
6262
async isAvailable(request: GenerateContentRequest): Promise<boolean> {
63-
// Returns false if we should only use in-cloud inference.
6463
if (this.mode === 'only_in_cloud') {
6564
return false;
6665
}
67-
// Returns false if the on-device inference API is undefined.;
68-
if (!this.languageModelProvider) {
69-
return false;
70-
}
71-
// Returns false if the request can't be run on-device.
72-
if (!ChromeAdapter.isOnDeviceRequest(request)) {
73-
return false;
66+
67+
const availability = await this.languageModelProvider?.availability();
68+
69+
// Triggers async model download so it'll be available next time.
70+
if (availability === Availability.downloadable) {
71+
this.download();
7472
}
75-
const availability = await this.languageModelProvider.availability();
76-
switch (availability) {
77-
case Availability.available:
78-
// Returns true only if a model is immediately available.
79-
return true;
80-
case Availability.downloadable:
81-
// Triggers async download if model is downloadable.
82-
this.download();
83-
default:
84-
return false;
73+
74+
if (this.mode === 'only_on_device') {
75+
return true;
8576
}
77+
78+
// Applies prefer_on_device logic.
79+
return (
80+
availability === Availability.available &&
81+
ChromeAdapter.isOnDeviceRequest(request)
82+
);
8683
}
8784

8885
/**
@@ -221,6 +218,12 @@ export class ChromeAdapter {
221218
// TODO: define a default value, since these are optional.
222219
options: LanguageModelCreateOptions
223220
): Promise<LanguageModel> {
221+
if (!this.languageModelProvider) {
222+
throw new VertexAIError(
223+
VertexAIErrorCode.REQUEST_ERROR,
224+
'Chrome AI requested for unsupported browser version.'
225+
);
226+
}
224227
// TODO: could we use this.onDeviceParams instead of passing in options?
225228
ChromeAdapter.addImageTypeAsExpectedInput(options);
226229
const newSession = await this.languageModelProvider!.create(options);

0 commit comments

Comments
 (0)