Skip to content

Commit ca3fc07

Browse files
committed
change back to private
1 parent 5df0f21 commit ca3fc07

File tree

10 files changed

+113
-59
lines changed

10 files changed

+113
-59
lines changed

common/api-review/ai.api.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,8 @@ export interface ChromeAdapter {
143143
generateContent(request: GenerateContentRequest): Promise<Response>;
144144
generateContentStream(request: GenerateContentRequest): Promise<Response>;
145145
isAvailable(request: GenerateContentRequest): Promise<boolean>;
146+
// (undocumented)
147+
mode: InferenceMode;
146148
}
147149

148150
// @public

docs-devsite/ai.chromeadapter.md

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,12 @@ These methods should not be called directly by the user.
2222
export interface ChromeAdapter
2323
```
2424

25+
## Properties
26+
27+
| Property | Type | Description |
28+
| --- | --- | --- |
29+
| [mode](./ai.chromeadapter.md#chromeadaptermode) | [InferenceMode](./ai.md#inferencemode) | |
30+
2531
## Methods
2632

2733
| Method | Description |
@@ -31,6 +37,14 @@ export interface ChromeAdapter
3137
| [generateContentStream(request)](./ai.chromeadapter.md#chromeadaptergeneratecontentstream) | Generates content stream on device.<p>This is comparable to [GenerativeModel.generateContentStream()](./ai.generativemodel.md#generativemodelgeneratecontentstream) for generating content in Cloud.</p> |
3238
| [isAvailable(request)](./ai.chromeadapter.md#chromeadapterisavailable) | Checks if a given request can be made on-device.<ol>Encapsulates a few concerns: <li>the mode</li> <li>API existence</li> <li>prompt formatting</li> <li>model availability, including triggering download if necessary</li> </ol><p>Pros: callers needn't be concerned with details of on-device availability.</p> <p>Cons: this method spans a few concerns and splits request validation from usage. If instance variables weren't already part of the API, we could consider a better separation of concerns.</p> |
3339

40+
## ChromeAdapter.mode
41+
42+
<b>Signature:</b>
43+
44+
```typescript
45+
mode: InferenceMode;
46+
```
47+
3448
## ChromeAdapter.countTokens()
3549

3650
Stub - not yet available for on-device.
@@ -73,7 +87,7 @@ generateContent(request: GenerateContentRequest): Promise<Response>;
7387

7488
Promise&lt;Response&gt;
7589

76-
, so we can reuse common response formatting.
90+
Response, so we can reuse common response formatting.
7791

7892
## ChromeAdapter.generateContentStream()
7993

@@ -97,7 +111,7 @@ generateContentStream(request: GenerateContentRequest): Promise<Response>;
97111

98112
Promise&lt;Response&gt;
99113

100-
, so we can reuse common response formatting.
114+
Response, so we can reuse common response formatting.
101115

102116
## ChromeAdapter.isAvailable()
103117

packages/ai/src/methods/chat-session.test.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import { match, restore, stub, useFakeTimers } from 'sinon';
2020
import sinonChai from 'sinon-chai';
2121
import chaiAsPromised from 'chai-as-promised';
2222
import * as generateContentMethods from './generate-content';
23-
import { GenerateContentStreamResult } from '../types';
23+
import { GenerateContentStreamResult, InferenceMode } from '../types';
2424
import { ChatSession } from './chat-session';
2525
import { ApiSettings } from '../types/internal';
2626
import { VertexAIBackend } from '../backend';
@@ -37,6 +37,12 @@ const fakeApiSettings: ApiSettings = {
3737
backend: new VertexAIBackend()
3838
};
3939

40+
const fakeChromeAdapter = new ChromeAdapterImpl(
41+
// @ts-expect-error
42+
undefined,
43+
InferenceMode.PREFER_ON_DEVICE
44+
);
45+
4046
describe('ChatSession', () => {
4147
afterEach(() => {
4248
restore();
@@ -50,7 +56,7 @@ describe('ChatSession', () => {
5056
const chatSession = new ChatSession(
5157
fakeApiSettings,
5258
'a-model',
53-
new ChromeAdapterImpl()
59+
fakeChromeAdapter
5460
);
5561
await expect(chatSession.sendMessage('hello')).to.be.rejected;
5662
expect(generateContentStub).to.be.calledWith(
@@ -71,7 +77,7 @@ describe('ChatSession', () => {
7177
const chatSession = new ChatSession(
7278
fakeApiSettings,
7379
'a-model',
74-
new ChromeAdapterImpl()
80+
fakeChromeAdapter
7581
);
7682
await expect(chatSession.sendMessageStream('hello')).to.be.rejected;
7783
expect(generateContentStreamStub).to.be.calledWith(
@@ -94,7 +100,7 @@ describe('ChatSession', () => {
94100
const chatSession = new ChatSession(
95101
fakeApiSettings,
96102
'a-model',
97-
new ChromeAdapterImpl()
103+
fakeChromeAdapter
98104
);
99105
await chatSession.sendMessageStream('hello');
100106
expect(generateContentStreamStub).to.be.calledWith(

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

Lines changed: 18 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,12 @@ describe('ChromeAdapter', () => {
5656
describe('constructor', () => {
5757
it('sets image as expected input type by default', async () => {
5858
const languageModelProvider = {
59-
availability: () => Promise.resolve(Availability.available)
59+
availability: () => Promise.resolve(Availability.AVAILABLE)
6060
} as LanguageModel;
6161
const availabilityStub = stub(
6262
languageModelProvider,
6363
'availability'
64-
).resolves(Availability.available);
64+
).resolves(Availability.AVAILABLE);
6565
const adapter = new ChromeAdapterImpl(
6666
languageModelProvider,
6767
InferenceMode.PREFER_ON_DEVICE
@@ -80,12 +80,12 @@ describe('ChromeAdapter', () => {
8080
});
8181
it('honors explicitly set expected inputs', async () => {
8282
const languageModelProvider = {
83-
availability: () => Promise.resolve(Availability.available)
83+
availability: () => Promise.resolve(Availability.AVAILABLE)
8484
} as LanguageModel;
8585
const availabilityStub = stub(
8686
languageModelProvider,
8787
'availability'
88-
).resolves(Availability.available);
88+
).resolves(Availability.AVAILABLE);
8989
const createOptions = {
9090
// Explicitly sets expected inputs.
9191
expectedInputs: [{ type: 'text' }]
@@ -109,16 +109,11 @@ describe('ChromeAdapter', () => {
109109
});
110110
});
111111
describe('isAvailable', () => {
112-
it('returns false if mode is undefined', async () => {
113-
const adapter = new ChromeAdapterImpl();
114-
expect(
115-
await adapter.isAvailable({
116-
contents: []
117-
})
118-
).to.be.false;
119-
});
120112
it('returns false if mode is only cloud', async () => {
121-
const adapter = new ChromeAdapterImpl(undefined, 'only_in_cloud');
113+
const adapter = new ChromeAdapterImpl(
114+
{} as LanguageModel,
115+
'only_in_cloud'
116+
);
122117
expect(
123118
await adapter.isAvailable({
124119
contents: []
@@ -127,6 +122,7 @@ describe('ChromeAdapter', () => {
127122
});
128123
it('returns false if LanguageModel API is undefined', async () => {
129124
const adapter = new ChromeAdapterImpl(
125+
// @ts-expect-error
130126
undefined,
131127
InferenceMode.PREFER_ON_DEVICE
132128
);
@@ -139,7 +135,7 @@ describe('ChromeAdapter', () => {
139135
it('returns false if request contents empty', async () => {
140136
const adapter = new ChromeAdapterImpl(
141137
{
142-
availability: async () => Availability.available
138+
availability: async () => Availability.AVAILABLE
143139
} as LanguageModel,
144140
InferenceMode.PREFER_ON_DEVICE
145141
);
@@ -152,7 +148,7 @@ describe('ChromeAdapter', () => {
152148
it('returns false if request content has "function" role', async () => {
153149
const adapter = new ChromeAdapterImpl(
154150
{
155-
availability: async () => Availability.available
151+
availability: async () => Availability.AVAILABLE
156152
} as LanguageModel,
157153
InferenceMode.PREFER_ON_DEVICE
158154
);
@@ -170,7 +166,7 @@ describe('ChromeAdapter', () => {
170166
it('returns true if request has image with supported mime type', async () => {
171167
const adapter = new ChromeAdapterImpl(
172168
{
173-
availability: async () => Availability.available
169+
availability: async () => Availability.AVAILABLE
174170
} as LanguageModel,
175171
InferenceMode.PREFER_ON_DEVICE
176172
);
@@ -196,7 +192,7 @@ describe('ChromeAdapter', () => {
196192
});
197193
it('returns true if model is readily available', async () => {
198194
const languageModelProvider = {
199-
availability: () => Promise.resolve(Availability.available)
195+
availability: () => Promise.resolve(Availability.AVAILABLE)
200196
} as LanguageModel;
201197
const adapter = new ChromeAdapterImpl(
202198
languageModelProvider,
@@ -218,7 +214,7 @@ describe('ChromeAdapter', () => {
218214
});
219215
it('returns false and triggers download when model is available after download', async () => {
220216
const languageModelProvider = {
221-
availability: () => Promise.resolve(Availability.downloadable),
217+
availability: () => Promise.resolve(Availability.DOWNLOADABLE),
222218
create: () => Promise.resolve({})
223219
} as LanguageModel;
224220
const createStub = stub(languageModelProvider, 'create').resolves(
@@ -241,7 +237,7 @@ describe('ChromeAdapter', () => {
241237
});
242238
it('avoids redundant downloads', async () => {
243239
const languageModelProvider = {
244-
availability: () => Promise.resolve(Availability.downloadable),
240+
availability: () => Promise.resolve(Availability.DOWNLOADABLE),
245241
create: () => Promise.resolve({})
246242
} as LanguageModel;
247243
const downloadPromise = new Promise<LanguageModel>(() => {
@@ -264,7 +260,7 @@ describe('ChromeAdapter', () => {
264260
});
265261
it('clears state when download completes', async () => {
266262
const languageModelProvider = {
267-
availability: () => Promise.resolve(Availability.downloadable),
263+
availability: () => Promise.resolve(Availability.DOWNLOADABLE),
268264
create: () => Promise.resolve({})
269265
} as LanguageModel;
270266
let resolveDownload;
@@ -289,7 +285,7 @@ describe('ChromeAdapter', () => {
289285
});
290286
it('returns false when model is never available', async () => {
291287
const languageModelProvider = {
292-
availability: () => Promise.resolve(Availability.unavailable),
288+
availability: () => Promise.resolve(Availability.UNAVAILABLE),
293289
create: () => Promise.resolve({})
294290
} as LanguageModel;
295291
const adapter = new ChromeAdapterImpl(
@@ -305,6 +301,7 @@ describe('ChromeAdapter', () => {
305301
});
306302
describe('generateContent', () => {
307303
it('throws if Chrome API is undefined', async () => {
304+
// @ts-expect-error
308305
const adapter = new ChromeAdapterImpl(undefined, 'only_on_device');
309306
await expect(
310307
adapter.generateContent({

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

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,8 @@ export class ChromeAdapterImpl implements ChromeAdapter {
4747
private downloadPromise: Promise<LanguageModel | void> | undefined;
4848
private oldSession: LanguageModel | undefined;
4949
constructor(
50-
private languageModelProvider?: LanguageModel,
51-
private mode?: InferenceMode,
50+
private languageModelProvider: LanguageModel,
51+
private mode: InferenceMode,
5252
private onDeviceParams: OnDeviceParams = {
5353
createOptions: {
5454
// Defaults to support image inputs for convenience.
@@ -79,7 +79,7 @@ export class ChromeAdapterImpl implements ChromeAdapter {
7979
);
8080
return false;
8181
}
82-
if (this.mode === 'only_in_cloud') {
82+
if (this.mode === InferenceMode.ONLY_IN_CLOUD) {
8383
logger.debug(
8484
`On-device inference unavailable because mode is "only_in_cloud".`
8585
);
@@ -89,12 +89,27 @@ export class ChromeAdapterImpl implements ChromeAdapter {
8989
// Triggers out-of-band download so model will eventually become available.
9090
const availability = await this.downloadIfAvailable();
9191

92-
if (this.mode === 'only_on_device') {
92+
if (this.mode === InferenceMode.ONLY_ON_DEVICE) {
93+
// If it will never be available due to API inavailability, throw.
94+
if (availability === Availability.UNAVAILABLE) {
95+
throw new AIError(
96+
AIErrorCode.API_NOT_ENABLED,
97+
'Local LanguageModel API not available in this environment.'
98+
);
99+
} else if (
100+
availability === Availability.DOWNLOADABLE ||
101+
availability === Availability.DOWNLOADING
102+
) {
103+
// TODO(chholland): Better user experience during download - progress?
104+
logger.debug(`Waiting for download of LanguageModel to complete.`);
105+
await this.downloadPromise;
106+
return true;
107+
}
93108
return true;
94109
}
95110

96111
// Applies prefer_on_device logic.
97-
if (availability !== Availability.available) {
112+
if (availability !== Availability.AVAILABLE) {
98113
logger.debug(
99114
`On-device inference unavailable because availability is "${availability}".`
100115
);
@@ -202,7 +217,7 @@ export class ChromeAdapterImpl implements ChromeAdapter {
202217
this.onDeviceParams.createOptions
203218
);
204219

205-
if (availability === Availability.downloadable) {
220+
if (availability === Availability.DOWNLOADABLE) {
206221
this.download();
207222
}
208223

packages/ai/src/methods/count-tokens.test.ts

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import chaiAsPromised from 'chai-as-promised';
2222
import { getMockResponse } from '../../test-utils/mock-response';
2323
import * as request from '../requests/request';
2424
import { countTokens } from './count-tokens';
25-
import { CountTokensRequest } from '../types';
25+
import { CountTokensRequest, InferenceMode } from '../types';
2626
import { ApiSettings } from '../types/internal';
2727
import { Task } from '../requests/request';
2828
import { mapCountTokensRequest } from '../googleai-mappers';
@@ -52,6 +52,12 @@ const fakeRequestParams: CountTokensRequest = {
5252
contents: [{ parts: [{ text: 'hello' }], role: 'user' }]
5353
};
5454

55+
const fakeChromeAdapter = new ChromeAdapterImpl(
56+
// @ts-expect-error
57+
undefined,
58+
InferenceMode.PREFER_ON_DEVICE
59+
);
60+
5561
describe('countTokens()', () => {
5662
afterEach(() => {
5763
restore();
@@ -68,7 +74,7 @@ describe('countTokens()', () => {
6874
fakeApiSettings,
6975
'model',
7076
fakeRequestParams,
71-
new ChromeAdapterImpl()
77+
fakeChromeAdapter
7278
);
7379
expect(result.totalTokens).to.equal(6);
7480
expect(result.totalBillableCharacters).to.equal(16);
@@ -95,7 +101,7 @@ describe('countTokens()', () => {
95101
fakeApiSettings,
96102
'model',
97103
fakeRequestParams,
98-
new ChromeAdapterImpl()
104+
fakeChromeAdapter
99105
);
100106
expect(result.totalTokens).to.equal(1837);
101107
expect(result.totalBillableCharacters).to.equal(117);
@@ -124,7 +130,7 @@ describe('countTokens()', () => {
124130
fakeApiSettings,
125131
'model',
126132
fakeRequestParams,
127-
new ChromeAdapterImpl()
133+
fakeChromeAdapter
128134
);
129135
expect(result.totalTokens).to.equal(258);
130136
expect(result).to.not.have.property('totalBillableCharacters');
@@ -154,7 +160,7 @@ describe('countTokens()', () => {
154160
fakeApiSettings,
155161
'model',
156162
fakeRequestParams,
157-
new ChromeAdapterImpl()
163+
fakeChromeAdapter
158164
)
159165
).to.be.rejectedWith(/404.*not found/);
160166
expect(mockFetch).to.be.called;
@@ -177,7 +183,7 @@ describe('countTokens()', () => {
177183
fakeGoogleAIApiSettings,
178184
'model',
179185
fakeRequestParams,
180-
new ChromeAdapterImpl()
186+
fakeChromeAdapter
181187
);
182188

183189
expect(makeRequestStub).to.be.calledWith(
@@ -191,7 +197,7 @@ describe('countTokens()', () => {
191197
});
192198
});
193199
it('on-device', async () => {
194-
const chromeAdapter = new ChromeAdapterImpl();
200+
const chromeAdapter = fakeChromeAdapter;
195201
const isAvailableStub = stub(chromeAdapter, 'isAvailable').resolves(true);
196202
const mockResponse = getMockResponse(
197203
'vertexAI',

0 commit comments

Comments
 (0)