@firebase/ai 1.2.2-canary.f92069a21 → 1.2.2-eap-ai-hybridinference.58d92df33
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ai-public.d.ts +214 -3
- package/dist/ai.d.ts +214 -3
- package/dist/esm/index.esm2017.js +328 -16
- package/dist/esm/index.esm2017.js.map +1 -1
- package/dist/esm/src/api.d.ts +2 -2
- package/dist/esm/src/methods/chat-session.d.ts +3 -1
- package/dist/esm/src/methods/chrome-adapter.d.ts +116 -0
- package/dist/esm/src/methods/count-tokens.d.ts +3 -1
- package/dist/esm/src/methods/generate-content.d.ts +3 -2
- package/dist/esm/src/models/generative-model.d.ts +7 -1
- package/dist/esm/src/types/index.d.ts +1 -0
- package/dist/esm/src/types/language-model.d.ts +63 -0
- package/dist/esm/src/types/requests.d.ts +29 -0
- package/dist/index.cjs.js +328 -16
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.node.cjs.js +328 -16
- package/dist/index.node.cjs.js.map +1 -1
- package/dist/index.node.mjs +328 -16
- package/dist/index.node.mjs.map +1 -1
- package/dist/src/api.d.ts +2 -2
- package/dist/src/methods/chat-session.d.ts +3 -1
- package/dist/src/methods/chrome-adapter.d.ts +116 -0
- package/dist/src/methods/count-tokens.d.ts +3 -1
- package/dist/src/methods/generate-content.d.ts +3 -2
- package/dist/src/models/generative-model.d.ts +7 -1
- package/dist/src/types/index.d.ts +1 -0
- package/dist/src/types/language-model.d.ts +63 -0
- package/dist/src/types/requests.d.ts +29 -0
- package/package.json +8 -8
package/dist/index.cjs.js
CHANGED
|
@@ -9,7 +9,7 @@ var logger$1 = require('@firebase/logger');
|
|
|
9
9
|
var tslib = require('tslib');
|
|
10
10
|
|
|
11
11
|
var name = "@firebase/ai";
|
|
12
|
-
var version = "1.2.2-
|
|
12
|
+
var version = "1.2.2-eap-ai-hybridinference.58d92df33";
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* @license
|
|
@@ -1628,20 +1628,38 @@ function aggregateResponses(responses) {
|
|
|
1628
1628
|
* See the License for the specific language governing permissions and
|
|
1629
1629
|
* limitations under the License.
|
|
1630
1630
|
*/
|
|
1631
|
-
async function
|
|
1631
|
+
async function generateContentStreamOnCloud(apiSettings, model, params, requestOptions) {
|
|
1632
1632
|
if (apiSettings.backend.backendType === BackendType.GOOGLE_AI) {
|
|
1633
1633
|
params = mapGenerateContentRequest(params);
|
|
1634
1634
|
}
|
|
1635
|
-
|
|
1635
|
+
return makeRequest(model, Task.STREAM_GENERATE_CONTENT, apiSettings,
|
|
1636
1636
|
/* stream */ true, JSON.stringify(params), requestOptions);
|
|
1637
|
+
}
|
|
1638
|
+
async function generateContentStream(apiSettings, model, params, chromeAdapter, requestOptions) {
|
|
1639
|
+
let response;
|
|
1640
|
+
if (await chromeAdapter.isAvailable(params)) {
|
|
1641
|
+
response = await chromeAdapter.generateContentStream(params);
|
|
1642
|
+
}
|
|
1643
|
+
else {
|
|
1644
|
+
response = await generateContentStreamOnCloud(apiSettings, model, params, requestOptions);
|
|
1645
|
+
}
|
|
1637
1646
|
return processStream(response, apiSettings); // TODO: Map streaming responses
|
|
1638
1647
|
}
|
|
1639
|
-
async function
|
|
1648
|
+
async function generateContentOnCloud(apiSettings, model, params, requestOptions) {
|
|
1640
1649
|
if (apiSettings.backend.backendType === BackendType.GOOGLE_AI) {
|
|
1641
1650
|
params = mapGenerateContentRequest(params);
|
|
1642
1651
|
}
|
|
1643
|
-
|
|
1652
|
+
return makeRequest(model, Task.GENERATE_CONTENT, apiSettings,
|
|
1644
1653
|
/* stream */ false, JSON.stringify(params), requestOptions);
|
|
1654
|
+
}
|
|
1655
|
+
async function generateContent(apiSettings, model, params, chromeAdapter, requestOptions) {
|
|
1656
|
+
let response;
|
|
1657
|
+
if (await chromeAdapter.isAvailable(params)) {
|
|
1658
|
+
response = await chromeAdapter.generateContent(params);
|
|
1659
|
+
}
|
|
1660
|
+
else {
|
|
1661
|
+
response = await generateContentOnCloud(apiSettings, model, params, requestOptions);
|
|
1662
|
+
}
|
|
1645
1663
|
const generateContentResponse = await processGenerateContentResponse(response, apiSettings);
|
|
1646
1664
|
const enhancedResponse = createEnhancedContentResponse(generateContentResponse);
|
|
1647
1665
|
return {
|
|
@@ -1898,8 +1916,9 @@ const SILENT_ERROR = 'SILENT_ERROR';
|
|
|
1898
1916
|
* @public
|
|
1899
1917
|
*/
|
|
1900
1918
|
class ChatSession {
|
|
1901
|
-
constructor(apiSettings, model, params, requestOptions) {
|
|
1919
|
+
constructor(apiSettings, model, chromeAdapter, params, requestOptions) {
|
|
1902
1920
|
this.model = model;
|
|
1921
|
+
this.chromeAdapter = chromeAdapter;
|
|
1903
1922
|
this.params = params;
|
|
1904
1923
|
this.requestOptions = requestOptions;
|
|
1905
1924
|
this._history = [];
|
|
@@ -1938,7 +1957,7 @@ class ChatSession {
|
|
|
1938
1957
|
let finalResult = {};
|
|
1939
1958
|
// Add onto the chain.
|
|
1940
1959
|
this._sendPromise = this._sendPromise
|
|
1941
|
-
.then(() => generateContent(this._apiSettings, this.model, generateContentRequest, this.requestOptions))
|
|
1960
|
+
.then(() => generateContent(this._apiSettings, this.model, generateContentRequest, this.chromeAdapter, this.requestOptions))
|
|
1942
1961
|
.then(result => {
|
|
1943
1962
|
var _a, _b;
|
|
1944
1963
|
if (result.response.candidates &&
|
|
@@ -1979,7 +1998,7 @@ class ChatSession {
|
|
|
1979
1998
|
systemInstruction: (_e = this.params) === null || _e === void 0 ? void 0 : _e.systemInstruction,
|
|
1980
1999
|
contents: [...this._history, newContent]
|
|
1981
2000
|
};
|
|
1982
|
-
const streamPromise = generateContentStream(this._apiSettings, this.model, generateContentRequest, this.requestOptions);
|
|
2001
|
+
const streamPromise = generateContentStream(this._apiSettings, this.model, generateContentRequest, this.chromeAdapter, this.requestOptions);
|
|
1983
2002
|
// Add onto the chain.
|
|
1984
2003
|
this._sendPromise = this._sendPromise
|
|
1985
2004
|
.then(() => streamPromise)
|
|
@@ -2036,7 +2055,7 @@ class ChatSession {
|
|
|
2036
2055
|
* See the License for the specific language governing permissions and
|
|
2037
2056
|
* limitations under the License.
|
|
2038
2057
|
*/
|
|
2039
|
-
async function
|
|
2058
|
+
async function countTokensOnCloud(apiSettings, model, params, requestOptions) {
|
|
2040
2059
|
let body = '';
|
|
2041
2060
|
if (apiSettings.backend.backendType === BackendType.GOOGLE_AI) {
|
|
2042
2061
|
const mappedParams = mapCountTokensRequest(params, model);
|
|
@@ -2048,6 +2067,12 @@ async function countTokens(apiSettings, model, params, requestOptions) {
|
|
|
2048
2067
|
const response = await makeRequest(model, Task.COUNT_TOKENS, apiSettings, false, body, requestOptions);
|
|
2049
2068
|
return response.json();
|
|
2050
2069
|
}
|
|
2070
|
+
async function countTokens(apiSettings, model, params, chromeAdapter, requestOptions) {
|
|
2071
|
+
if (await chromeAdapter.isAvailable(params)) {
|
|
2072
|
+
return (await chromeAdapter.countTokens(params)).json();
|
|
2073
|
+
}
|
|
2074
|
+
return countTokensOnCloud(apiSettings, model, params, requestOptions);
|
|
2075
|
+
}
|
|
2051
2076
|
|
|
2052
2077
|
/**
|
|
2053
2078
|
* @license
|
|
@@ -2070,8 +2095,9 @@ async function countTokens(apiSettings, model, params, requestOptions) {
|
|
|
2070
2095
|
* @public
|
|
2071
2096
|
*/
|
|
2072
2097
|
class GenerativeModel extends AIModel {
|
|
2073
|
-
constructor(ai, modelParams, requestOptions) {
|
|
2098
|
+
constructor(ai, modelParams, chromeAdapter, requestOptions) {
|
|
2074
2099
|
super(ai, modelParams.model);
|
|
2100
|
+
this.chromeAdapter = chromeAdapter;
|
|
2075
2101
|
this.generationConfig = modelParams.generationConfig || {};
|
|
2076
2102
|
this.safetySettings = modelParams.safetySettings || [];
|
|
2077
2103
|
this.tools = modelParams.tools;
|
|
@@ -2085,7 +2111,7 @@ class GenerativeModel extends AIModel {
|
|
|
2085
2111
|
*/
|
|
2086
2112
|
async generateContent(request) {
|
|
2087
2113
|
const formattedParams = formatGenerateContentInput(request);
|
|
2088
|
-
return generateContent(this._apiSettings, this.model, Object.assign({ generationConfig: this.generationConfig, safetySettings: this.safetySettings, tools: this.tools, toolConfig: this.toolConfig, systemInstruction: this.systemInstruction }, formattedParams), this.requestOptions);
|
|
2114
|
+
return generateContent(this._apiSettings, this.model, Object.assign({ generationConfig: this.generationConfig, safetySettings: this.safetySettings, tools: this.tools, toolConfig: this.toolConfig, systemInstruction: this.systemInstruction }, formattedParams), this.chromeAdapter, this.requestOptions);
|
|
2089
2115
|
}
|
|
2090
2116
|
/**
|
|
2091
2117
|
* Makes a single streaming call to the model
|
|
@@ -2095,23 +2121,27 @@ class GenerativeModel extends AIModel {
|
|
|
2095
2121
|
*/
|
|
2096
2122
|
async generateContentStream(request) {
|
|
2097
2123
|
const formattedParams = formatGenerateContentInput(request);
|
|
2098
|
-
return generateContentStream(this._apiSettings, this.model, Object.assign({ generationConfig: this.generationConfig, safetySettings: this.safetySettings, tools: this.tools, toolConfig: this.toolConfig, systemInstruction: this.systemInstruction }, formattedParams), this.requestOptions);
|
|
2124
|
+
return generateContentStream(this._apiSettings, this.model, Object.assign({ generationConfig: this.generationConfig, safetySettings: this.safetySettings, tools: this.tools, toolConfig: this.toolConfig, systemInstruction: this.systemInstruction }, formattedParams), this.chromeAdapter, this.requestOptions);
|
|
2099
2125
|
}
|
|
2100
2126
|
/**
|
|
2101
2127
|
* Gets a new {@link ChatSession} instance which can be used for
|
|
2102
2128
|
* multi-turn chats.
|
|
2103
2129
|
*/
|
|
2104
2130
|
startChat(startChatParams) {
|
|
2105
|
-
return new ChatSession(this._apiSettings, this.model, Object.assign({ tools: this.tools, toolConfig: this.toolConfig, systemInstruction: this.systemInstruction, generationConfig: this.generationConfig, safetySettings: this.safetySettings }, startChatParams), this.requestOptions);
|
|
2131
|
+
return new ChatSession(this._apiSettings, this.model, this.chromeAdapter, Object.assign({ tools: this.tools, toolConfig: this.toolConfig, systemInstruction: this.systemInstruction, generationConfig: this.generationConfig, safetySettings: this.safetySettings }, startChatParams), this.requestOptions);
|
|
2106
2132
|
}
|
|
2107
2133
|
/**
|
|
2108
2134
|
* Counts the tokens in the provided request.
|
|
2109
2135
|
*/
|
|
2110
2136
|
async countTokens(request) {
|
|
2111
2137
|
const formattedParams = formatGenerateContentInput(request);
|
|
2112
|
-
return countTokens(this._apiSettings, this.model, formattedParams);
|
|
2138
|
+
return countTokens(this._apiSettings, this.model, formattedParams, this.chromeAdapter);
|
|
2113
2139
|
}
|
|
2114
2140
|
}
|
|
2141
|
+
/**
|
|
2142
|
+
* Defines the name of the default in-cloud model to use for hybrid inference.
|
|
2143
|
+
*/
|
|
2144
|
+
GenerativeModel.DEFAULT_HYBRID_IN_CLOUD_MODEL = 'gemini-2.0-flash-lite';
|
|
2115
2145
|
|
|
2116
2146
|
/**
|
|
2117
2147
|
* @license
|
|
@@ -2220,6 +2250,277 @@ class ImagenModel extends AIModel {
|
|
|
2220
2250
|
}
|
|
2221
2251
|
}
|
|
2222
2252
|
|
|
2253
|
+
var Availability;
|
|
2254
|
+
(function (Availability) {
|
|
2255
|
+
Availability["unavailable"] = "unavailable";
|
|
2256
|
+
Availability["downloadable"] = "downloadable";
|
|
2257
|
+
Availability["downloading"] = "downloading";
|
|
2258
|
+
Availability["available"] = "available";
|
|
2259
|
+
})(Availability || (Availability = {}));
|
|
2260
|
+
|
|
2261
|
+
/**
|
|
2262
|
+
* @license
|
|
2263
|
+
* Copyright 2025 Google LLC
|
|
2264
|
+
*
|
|
2265
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
2266
|
+
* you may not use this file except in compliance with the License.
|
|
2267
|
+
* You may obtain a copy of the License at
|
|
2268
|
+
*
|
|
2269
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
2270
|
+
*
|
|
2271
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
2272
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
2273
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
2274
|
+
* See the License for the specific language governing permissions and
|
|
2275
|
+
* limitations under the License.
|
|
2276
|
+
*/
|
|
2277
|
+
/**
|
|
2278
|
+
* Defines an inference "backend" that uses Chrome's on-device model,
|
|
2279
|
+
* and encapsulates logic for detecting when on-device is possible.
|
|
2280
|
+
*/
|
|
2281
|
+
class ChromeAdapter {
|
|
2282
|
+
constructor(languageModelProvider, mode, onDeviceParams = {
|
|
2283
|
+
createOptions: {
|
|
2284
|
+
// Defaults to support image inputs for convenience.
|
|
2285
|
+
expectedInputs: [{ type: 'image' }]
|
|
2286
|
+
}
|
|
2287
|
+
}) {
|
|
2288
|
+
this.languageModelProvider = languageModelProvider;
|
|
2289
|
+
this.mode = mode;
|
|
2290
|
+
this.onDeviceParams = onDeviceParams;
|
|
2291
|
+
this.isDownloading = false;
|
|
2292
|
+
}
|
|
2293
|
+
/**
|
|
2294
|
+
* Checks if a given request can be made on-device.
|
|
2295
|
+
*
|
|
2296
|
+
* <ol>Encapsulates a few concerns:
|
|
2297
|
+
* <li>the mode</li>
|
|
2298
|
+
* <li>API existence</li>
|
|
2299
|
+
* <li>prompt formatting</li>
|
|
2300
|
+
* <li>model availability, including triggering download if necessary</li>
|
|
2301
|
+
* </ol>
|
|
2302
|
+
*
|
|
2303
|
+
* <p>Pros: callers needn't be concerned with details of on-device availability.</p>
|
|
2304
|
+
* <p>Cons: this method spans a few concerns and splits request validation from usage.
|
|
2305
|
+
* If instance variables weren't already part of the API, we could consider a better
|
|
2306
|
+
* separation of concerns.</p>
|
|
2307
|
+
*/
|
|
2308
|
+
async isAvailable(request) {
|
|
2309
|
+
if (!this.mode) {
|
|
2310
|
+
logger.debug(`On-device inference unavailable because mode is undefined.`);
|
|
2311
|
+
return false;
|
|
2312
|
+
}
|
|
2313
|
+
if (this.mode === 'only_in_cloud') {
|
|
2314
|
+
logger.debug(`On-device inference unavailable because mode is "only_in_cloud".`);
|
|
2315
|
+
return false;
|
|
2316
|
+
}
|
|
2317
|
+
// Triggers out-of-band download so model will eventually become available.
|
|
2318
|
+
const availability = await this.downloadIfAvailable();
|
|
2319
|
+
if (this.mode === 'only_on_device') {
|
|
2320
|
+
return true;
|
|
2321
|
+
}
|
|
2322
|
+
// Applies prefer_on_device logic.
|
|
2323
|
+
if (availability !== Availability.available) {
|
|
2324
|
+
logger.debug(`On-device inference unavailable because availability is "${availability}".`);
|
|
2325
|
+
return false;
|
|
2326
|
+
}
|
|
2327
|
+
if (!ChromeAdapter.isOnDeviceRequest(request)) {
|
|
2328
|
+
logger.debug(`On-device inference unavailable because request is incompatible.`);
|
|
2329
|
+
return false;
|
|
2330
|
+
}
|
|
2331
|
+
return true;
|
|
2332
|
+
}
|
|
2333
|
+
/**
|
|
2334
|
+
* Generates content on device.
|
|
2335
|
+
*
|
|
2336
|
+
* <p>This is comparable to {@link GenerativeModel.generateContent} for generating content in
|
|
2337
|
+
* Cloud.</p>
|
|
2338
|
+
* @param request - a standard Vertex {@link GenerateContentRequest}
|
|
2339
|
+
* @returns {@link Response}, so we can reuse common response formatting.
|
|
2340
|
+
*/
|
|
2341
|
+
async generateContent(request) {
|
|
2342
|
+
const session = await this.createSession();
|
|
2343
|
+
const contents = await Promise.all(request.contents.map(ChromeAdapter.toLanguageModelMessage));
|
|
2344
|
+
const text = await session.prompt(contents, this.onDeviceParams.promptOptions);
|
|
2345
|
+
return ChromeAdapter.toResponse(text);
|
|
2346
|
+
}
|
|
2347
|
+
/**
|
|
2348
|
+
* Generates content stream on device.
|
|
2349
|
+
*
|
|
2350
|
+
* <p>This is comparable to {@link GenerativeModel.generateContentStream} for generating content in
|
|
2351
|
+
* Cloud.</p>
|
|
2352
|
+
* @param request - a standard Vertex {@link GenerateContentRequest}
|
|
2353
|
+
* @returns {@link Response}, so we can reuse common response formatting.
|
|
2354
|
+
*/
|
|
2355
|
+
async generateContentStream(request) {
|
|
2356
|
+
const session = await this.createSession();
|
|
2357
|
+
const contents = await Promise.all(request.contents.map(ChromeAdapter.toLanguageModelMessage));
|
|
2358
|
+
const stream = await session.promptStreaming(contents, this.onDeviceParams.promptOptions);
|
|
2359
|
+
return ChromeAdapter.toStreamResponse(stream);
|
|
2360
|
+
}
|
|
2361
|
+
async countTokens(_request) {
|
|
2362
|
+
throw new AIError("request-error" /* AIErrorCode.REQUEST_ERROR */, 'Count Tokens is not yet available for on-device model.');
|
|
2363
|
+
}
|
|
2364
|
+
/**
|
|
2365
|
+
* Asserts inference for the given request can be performed by an on-device model.
|
|
2366
|
+
*/
|
|
2367
|
+
static isOnDeviceRequest(request) {
|
|
2368
|
+
// Returns false if the prompt is empty.
|
|
2369
|
+
if (request.contents.length === 0) {
|
|
2370
|
+
logger.debug('Empty prompt rejected for on-device inference.');
|
|
2371
|
+
return false;
|
|
2372
|
+
}
|
|
2373
|
+
for (const content of request.contents) {
|
|
2374
|
+
if (content.role === 'function') {
|
|
2375
|
+
logger.debug(`"Function" role rejected for on-device inference.`);
|
|
2376
|
+
return false;
|
|
2377
|
+
}
|
|
2378
|
+
// Returns false if request contains an image with an unsupported mime type.
|
|
2379
|
+
for (const part of content.parts) {
|
|
2380
|
+
if (part.inlineData &&
|
|
2381
|
+
ChromeAdapter.SUPPORTED_MIME_TYPES.indexOf(part.inlineData.mimeType) === -1) {
|
|
2382
|
+
logger.debug(`Unsupported mime type "${part.inlineData.mimeType}" rejected for on-device inference.`);
|
|
2383
|
+
return false;
|
|
2384
|
+
}
|
|
2385
|
+
}
|
|
2386
|
+
}
|
|
2387
|
+
return true;
|
|
2388
|
+
}
|
|
2389
|
+
/**
|
|
2390
|
+
* Encapsulates logic to get availability and download a model if one is downloadable.
|
|
2391
|
+
*/
|
|
2392
|
+
async downloadIfAvailable() {
|
|
2393
|
+
var _a;
|
|
2394
|
+
const availability = await ((_a = this.languageModelProvider) === null || _a === void 0 ? void 0 : _a.availability(this.onDeviceParams.createOptions));
|
|
2395
|
+
if (availability === Availability.downloadable) {
|
|
2396
|
+
this.download();
|
|
2397
|
+
}
|
|
2398
|
+
return availability;
|
|
2399
|
+
}
|
|
2400
|
+
/**
|
|
2401
|
+
* Triggers out-of-band download of an on-device model.
|
|
2402
|
+
*
|
|
2403
|
+
* <p>Chrome only downloads models as needed. Chrome knows a model is needed when code calls
|
|
2404
|
+
* LanguageModel.create.</p>
|
|
2405
|
+
*
|
|
2406
|
+
* <p>Since Chrome manages the download, the SDK can only avoid redundant download requests by
|
|
2407
|
+
* tracking if a download has previously been requested.</p>
|
|
2408
|
+
*/
|
|
2409
|
+
download() {
|
|
2410
|
+
var _a;
|
|
2411
|
+
if (this.isDownloading) {
|
|
2412
|
+
return;
|
|
2413
|
+
}
|
|
2414
|
+
this.isDownloading = true;
|
|
2415
|
+
this.downloadPromise = (_a = this.languageModelProvider) === null || _a === void 0 ? void 0 : _a.create(this.onDeviceParams.createOptions).then(() => {
|
|
2416
|
+
this.isDownloading = false;
|
|
2417
|
+
});
|
|
2418
|
+
}
|
|
2419
|
+
/**
|
|
2420
|
+
* Converts Vertex {@link Content} object to a Chrome {@link LanguageModelMessage} object.
|
|
2421
|
+
*/
|
|
2422
|
+
static async toLanguageModelMessage(content) {
|
|
2423
|
+
const languageModelMessageContents = await Promise.all(content.parts.map(ChromeAdapter.toLanguageModelMessageContent));
|
|
2424
|
+
return {
|
|
2425
|
+
role: ChromeAdapter.toLanguageModelMessageRole(content.role),
|
|
2426
|
+
content: languageModelMessageContents
|
|
2427
|
+
};
|
|
2428
|
+
}
|
|
2429
|
+
/**
|
|
2430
|
+
* Converts a Vertex Part object to a Chrome LanguageModelMessageContent object.
|
|
2431
|
+
*/
|
|
2432
|
+
static async toLanguageModelMessageContent(part) {
|
|
2433
|
+
if (part.text) {
|
|
2434
|
+
return {
|
|
2435
|
+
type: 'text',
|
|
2436
|
+
value: part.text
|
|
2437
|
+
};
|
|
2438
|
+
}
|
|
2439
|
+
else if (part.inlineData) {
|
|
2440
|
+
const formattedImageContent = await fetch(`data:${part.inlineData.mimeType};base64,${part.inlineData.data}`);
|
|
2441
|
+
const imageBlob = await formattedImageContent.blob();
|
|
2442
|
+
const imageBitmap = await createImageBitmap(imageBlob);
|
|
2443
|
+
return {
|
|
2444
|
+
type: 'image',
|
|
2445
|
+
value: imageBitmap
|
|
2446
|
+
};
|
|
2447
|
+
}
|
|
2448
|
+
// Assumes contents have been verified to contain only a single TextPart.
|
|
2449
|
+
// TODO: support other input types
|
|
2450
|
+
throw new Error('Not yet implemented');
|
|
2451
|
+
}
|
|
2452
|
+
/**
|
|
2453
|
+
* Converts a Vertex {@link Role} string to a {@link LanguageModelMessageRole} string.
|
|
2454
|
+
*/
|
|
2455
|
+
static toLanguageModelMessageRole(role) {
|
|
2456
|
+
// Assumes 'function' rule has been filtered by isOnDeviceRequest
|
|
2457
|
+
return role === 'model' ? 'assistant' : 'user';
|
|
2458
|
+
}
|
|
2459
|
+
/**
|
|
2460
|
+
* Abstracts Chrome session creation.
|
|
2461
|
+
*
|
|
2462
|
+
* <p>Chrome uses a multi-turn session for all inference. Vertex uses single-turn for all
|
|
2463
|
+
* inference. To map the Vertex API to Chrome's API, the SDK creates a new session for all
|
|
2464
|
+
* inference.</p>
|
|
2465
|
+
*
|
|
2466
|
+
* <p>Chrome will remove a model from memory if it's no longer in use, so this method ensures a
|
|
2467
|
+
* new session is created before an old session is destroyed.</p>
|
|
2468
|
+
*/
|
|
2469
|
+
async createSession() {
|
|
2470
|
+
if (!this.languageModelProvider) {
|
|
2471
|
+
throw new AIError("request-error" /* AIErrorCode.REQUEST_ERROR */, 'Chrome AI requested for unsupported browser version.');
|
|
2472
|
+
}
|
|
2473
|
+
const newSession = await this.languageModelProvider.create(this.onDeviceParams.createOptions);
|
|
2474
|
+
if (this.oldSession) {
|
|
2475
|
+
this.oldSession.destroy();
|
|
2476
|
+
}
|
|
2477
|
+
// Holds session reference, so model isn't unloaded from memory.
|
|
2478
|
+
this.oldSession = newSession;
|
|
2479
|
+
return newSession;
|
|
2480
|
+
}
|
|
2481
|
+
/**
|
|
2482
|
+
* Formats string returned by Chrome as a {@link Response} returned by Vertex.
|
|
2483
|
+
*/
|
|
2484
|
+
static toResponse(text) {
|
|
2485
|
+
return {
|
|
2486
|
+
json: async () => ({
|
|
2487
|
+
candidates: [
|
|
2488
|
+
{
|
|
2489
|
+
content: {
|
|
2490
|
+
parts: [{ text }]
|
|
2491
|
+
}
|
|
2492
|
+
}
|
|
2493
|
+
]
|
|
2494
|
+
})
|
|
2495
|
+
};
|
|
2496
|
+
}
|
|
2497
|
+
/**
|
|
2498
|
+
* Formats string stream returned by Chrome as SSE returned by Vertex.
|
|
2499
|
+
*/
|
|
2500
|
+
static toStreamResponse(stream) {
|
|
2501
|
+
const encoder = new TextEncoder();
|
|
2502
|
+
return {
|
|
2503
|
+
body: stream.pipeThrough(new TransformStream({
|
|
2504
|
+
transform(chunk, controller) {
|
|
2505
|
+
const json = JSON.stringify({
|
|
2506
|
+
candidates: [
|
|
2507
|
+
{
|
|
2508
|
+
content: {
|
|
2509
|
+
role: 'model',
|
|
2510
|
+
parts: [{ text: chunk }]
|
|
2511
|
+
}
|
|
2512
|
+
}
|
|
2513
|
+
]
|
|
2514
|
+
});
|
|
2515
|
+
controller.enqueue(encoder.encode(`data: ${json}\n\n`));
|
|
2516
|
+
}
|
|
2517
|
+
}))
|
|
2518
|
+
};
|
|
2519
|
+
}
|
|
2520
|
+
}
|
|
2521
|
+
// Visible for testing
|
|
2522
|
+
ChromeAdapter.SUPPORTED_MIME_TYPES = ['image/jpeg', 'image/png'];
|
|
2523
|
+
|
|
2223
2524
|
/**
|
|
2224
2525
|
* @license
|
|
2225
2526
|
* Copyright 2024 Google LLC
|
|
@@ -2575,10 +2876,21 @@ function getAI(app$1 = app.getApp(), options = { backend: new GoogleAIBackend()
|
|
|
2575
2876
|
* @public
|
|
2576
2877
|
*/
|
|
2577
2878
|
function getGenerativeModel(ai, modelParams, requestOptions) {
|
|
2578
|
-
|
|
2879
|
+
// Uses the existence of HybridParams.mode to clarify the type of the modelParams input.
|
|
2880
|
+
const hybridParams = modelParams;
|
|
2881
|
+
let inCloudParams;
|
|
2882
|
+
if (hybridParams.mode) {
|
|
2883
|
+
inCloudParams = hybridParams.inCloudParams || {
|
|
2884
|
+
model: GenerativeModel.DEFAULT_HYBRID_IN_CLOUD_MODEL
|
|
2885
|
+
};
|
|
2886
|
+
}
|
|
2887
|
+
else {
|
|
2888
|
+
inCloudParams = modelParams;
|
|
2889
|
+
}
|
|
2890
|
+
if (!inCloudParams.model) {
|
|
2579
2891
|
throw new AIError("no-model" /* AIErrorCode.NO_MODEL */, `Must provide a model name. Example: getGenerativeModel({ model: 'my-model-name' })`);
|
|
2580
2892
|
}
|
|
2581
|
-
return new GenerativeModel(ai,
|
|
2893
|
+
return new GenerativeModel(ai, inCloudParams, new ChromeAdapter(window.LanguageModel, hybridParams.mode, hybridParams.onDeviceParams), requestOptions);
|
|
2582
2894
|
}
|
|
2583
2895
|
/**
|
|
2584
2896
|
* Returns an {@link ImagenModel} class with methods for using Imagen.
|