Skip to content

Commit d546aec

Browse files
committed
Test request-based availability checks
1 parent ee4d3aa commit d546aec

File tree

2 files changed

+132
-31
lines changed

2 files changed

+132
-31
lines changed

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

Lines changed: 111 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,28 +20,130 @@ import sinonChai from 'sinon-chai';
2020
import chaiAsPromised from 'chai-as-promised';
2121
import { ChromeAdapter } from './chrome-adapter';
2222
import { stub } from 'sinon';
23+
import * as util from '@firebase/util';
2324

2425
use(sinonChai);
2526
use(chaiAsPromised);
2627

2728
describe('ChromeAdapter', () => {
28-
describe('isOnDeviceRequest', () => {
29-
it('returns true for simple text part', async () => {
29+
describe('isAvailable', () => {
30+
it('returns false if mode is only cloud', async () => {
31+
const adapter = new ChromeAdapter(undefined, 'only_in_cloud');
3032
expect(
31-
ChromeAdapter._isOnDeviceRequest({
32-
contents: [{ role: 'user', parts: [{ text: 'hi' }] }]
33+
await adapter.isAvailable({
34+
contents: []
3335
})
34-
).to.be.true;
36+
).to.be.false;
3537
});
36-
it('returns false if contents empty', async () => {
38+
it('returns false if browser is not Chrome', async () => {
39+
const chromeStub = stub(util, 'isChrome').returns(false);
40+
const adapter = new ChromeAdapter(undefined, 'prefer_on_device');
3741
expect(
38-
ChromeAdapter._isOnDeviceRequest({
42+
await adapter.isAvailable({
3943
contents: []
4044
})
4145
).to.be.false;
46+
chromeStub.restore();
47+
});
48+
it('returns false if AI API is undefined', async () => {
49+
const adapter = new ChromeAdapter(undefined, 'prefer_on_device');
50+
expect(
51+
await adapter.isAvailable({
52+
contents: []
53+
})
54+
).to.be.false;
55+
});
56+
it('returns false if LanguageModel API is undefined', async () => {
57+
const adapter = new ChromeAdapter({} as AI, 'prefer_on_device');
58+
expect(
59+
await adapter.isAvailable({
60+
contents: []
61+
})
62+
).to.be.false;
63+
});
64+
it('returns false if request contents empty', async () => {
65+
const adapter = new ChromeAdapter({} as AI, 'prefer_on_device');
66+
expect(
67+
await adapter.isAvailable({
68+
contents: []
69+
})
70+
).to.be.false;
71+
});
72+
it('returns false if request content has function role', async () => {
73+
const adapter = new ChromeAdapter({} as AI, 'prefer_on_device');
74+
expect(
75+
await adapter.isAvailable({
76+
contents: [
77+
{
78+
role: 'function',
79+
parts: []
80+
}
81+
]
82+
})
83+
).to.be.false;
84+
});
85+
it('returns false if request content has multiple parts', async () => {
86+
const adapter = new ChromeAdapter({} as AI, 'prefer_on_device');
87+
expect(
88+
await adapter.isAvailable({
89+
contents: [
90+
{
91+
role: 'user',
92+
parts: [{ text: 'a' }, { text: 'b' }]
93+
}
94+
]
95+
})
96+
).to.be.false;
97+
});
98+
it('returns false if request content has non-text part', async () => {
99+
const adapter = new ChromeAdapter({} as AI, 'prefer_on_device');
100+
expect(
101+
await adapter.isAvailable({
102+
contents: [
103+
{
104+
role: 'user',
105+
parts: [{ inlineData: { mimeType: 'a', data: 'b' } }]
106+
}
107+
]
108+
})
109+
).to.be.false;
110+
});
111+
it('returns false if request system instruction has function role', async () => {
112+
const adapter = new ChromeAdapter({} as AI, 'prefer_on_device');
113+
expect(
114+
await adapter.isAvailable({
115+
contents: [],
116+
systemInstruction: {
117+
role: 'function',
118+
parts: []
119+
}
120+
})
121+
).to.be.false;
122+
});
123+
it('returns false if request system instruction has multiple parts', async () => {
124+
const adapter = new ChromeAdapter({} as AI, 'prefer_on_device');
125+
expect(
126+
await adapter.isAvailable({
127+
contents: [],
128+
systemInstruction: {
129+
role: 'function',
130+
parts: [{ text: 'a' }, { text: 'b' }]
131+
}
132+
})
133+
).to.be.false;
134+
});
135+
it('returns false if request system instruction has non-text part', async () => {
136+
const adapter = new ChromeAdapter({} as AI, 'prefer_on_device');
137+
expect(
138+
await adapter.isAvailable({
139+
contents: [],
140+
systemInstruction: {
141+
role: 'function',
142+
parts: [{ inlineData: { mimeType: 'a', data: 'b' } }]
143+
}
144+
})
145+
).to.be.false;
42146
});
43-
});
44-
describe('isAvailable', () => {
45147
it('returns true if model is readily available', async () => {
46148
const aiProvider = {
47149
languageModel: {

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

Lines changed: 21 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ export class ChromeAdapter {
6060
return false;
6161
}
6262
// Returns false if the request can't be run on-device.
63-
if (!ChromeAdapter._isOnDeviceRequest(request)) {
63+
if (!ChromeAdapter.isOnDeviceRequest(request)) {
6464
return false;
6565
}
6666
switch (await this.availability()) {
@@ -94,27 +94,7 @@ export class ChromeAdapter {
9494
functionCalls: () => undefined
9595
};
9696
}
97-
// Visible for testing
98-
static _isOnDeviceRequest(request: GenerateContentRequest): boolean {
99-
if (request.systemInstruction) {
100-
const systemContent = request.systemInstruction as Content;
101-
// Returns false if the role can't be represented on-device.
102-
if (systemContent.role && systemContent.role === 'function') {
103-
return false;
104-
}
105-
106-
// Returns false if the system prompt is multi-part.
107-
if (systemContent.parts && systemContent.parts.length > 1) {
108-
return false;
109-
}
110-
111-
// Returns false if the system prompt isn't text.
112-
const systemText = request.systemInstruction as TextPart;
113-
if (!systemText.text) {
114-
return false;
115-
}
116-
}
117-
97+
private static isOnDeviceRequest(request: GenerateContentRequest): boolean {
11898
// Returns false if the prompt is empty.
11999
if (request.contents.length === 0) {
120100
return false;
@@ -135,6 +115,25 @@ export class ChromeAdapter {
135115
}
136116
}
137117

118+
if (request.systemInstruction) {
119+
const systemContent = request.systemInstruction as Content;
120+
// Returns false if the role can't be represented on-device.
121+
if (systemContent.role && systemContent.role === 'function') {
122+
return false;
123+
}
124+
125+
// Returns false if the system prompt is multi-part.
126+
if (systemContent.parts && systemContent.parts.length > 1) {
127+
return false;
128+
}
129+
130+
// Returns false if the system prompt isn't text.
131+
const systemText = request.systemInstruction as TextPart;
132+
if (!systemText.text) {
133+
return false;
134+
}
135+
}
136+
138137
return true;
139138
}
140139
private async availability(): Promise<AICapabilityAvailability | undefined> {

0 commit comments

Comments
 (0)