@firebase/ai 2.6.1 → 2.7.0-20260114192924
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 +104 -11
- package/dist/ai.d.ts +105 -12
- package/dist/esm/index.esm.js +157 -77
- package/dist/esm/index.esm.js.map +1 -1
- package/dist/esm/src/constants.d.ts +1 -1
- package/dist/esm/src/methods/chat-session.d.ts +7 -3
- package/dist/esm/src/methods/count-tokens.d.ts +2 -2
- package/dist/esm/src/methods/generate-content.d.ts +5 -5
- package/dist/esm/src/models/generative-model.d.ts +4 -4
- package/dist/esm/src/models/imagen-model.d.ts +3 -3
- package/dist/esm/src/models/template-generative-model.d.ts +3 -3
- package/dist/esm/src/models/template-imagen-model.d.ts +2 -2
- package/dist/esm/src/requests/request.d.ts +4 -2
- package/dist/esm/src/requests/stream-reader.d.ts +1 -3
- package/dist/esm/src/types/enums.d.ts +21 -0
- package/dist/esm/src/types/imagen/internal.d.ts +1 -1
- package/dist/esm/src/types/requests.d.ts +68 -3
- package/dist/index.cjs.js +157 -76
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.node.cjs.js +157 -76
- package/dist/index.node.cjs.js.map +1 -1
- package/dist/index.node.mjs +157 -77
- package/dist/index.node.mjs.map +1 -1
- package/dist/src/constants.d.ts +1 -1
- package/dist/src/methods/chat-session.d.ts +7 -3
- package/dist/src/methods/count-tokens.d.ts +2 -2
- package/dist/src/methods/generate-content.d.ts +5 -5
- package/dist/src/models/generative-model.d.ts +4 -4
- package/dist/src/models/imagen-model.d.ts +3 -3
- package/dist/src/models/template-generative-model.d.ts +3 -3
- package/dist/src/models/template-imagen-model.d.ts +2 -2
- package/dist/src/requests/request.d.ts +4 -2
- package/dist/src/requests/stream-reader.d.ts +1 -3
- package/dist/src/types/enums.d.ts +21 -0
- package/dist/src/types/imagen/internal.d.ts +1 -1
- package/dist/src/types/requests.d.ts +68 -3
- package/package.json +1 -1
package/dist/index.node.cjs.js
CHANGED
|
@@ -8,7 +8,7 @@ var util = require('@firebase/util');
|
|
|
8
8
|
var logger$1 = require('@firebase/logger');
|
|
9
9
|
|
|
10
10
|
var name = "@firebase/ai";
|
|
11
|
-
var version = "2.
|
|
11
|
+
var version = "2.7.0-20260114192924";
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* @license
|
|
@@ -36,7 +36,7 @@ const DEFAULT_FETCH_TIMEOUT_MS = 180 * 1000;
|
|
|
36
36
|
/**
|
|
37
37
|
* Defines the name of the default in-cloud model to use for hybrid inference.
|
|
38
38
|
*/
|
|
39
|
-
const DEFAULT_HYBRID_IN_CLOUD_MODEL = 'gemini-2.
|
|
39
|
+
const DEFAULT_HYBRID_IN_CLOUD_MODEL = 'gemini-2.5-flash-lite';
|
|
40
40
|
|
|
41
41
|
/**
|
|
42
42
|
* @license
|
|
@@ -412,6 +412,19 @@ const Language = {
|
|
|
412
412
|
UNSPECIFIED: 'LANGUAGE_UNSPECIFIED',
|
|
413
413
|
PYTHON: 'PYTHON'
|
|
414
414
|
};
|
|
415
|
+
/**
|
|
416
|
+
* A preset that controls the model's "thinking" process. Use
|
|
417
|
+
* `ThinkingLevel.LOW` for faster responses on less complex tasks, and
|
|
418
|
+
* `ThinkingLevel.HIGH` for better reasoning on more complex tasks.
|
|
419
|
+
*
|
|
420
|
+
* @public
|
|
421
|
+
*/
|
|
422
|
+
const ThinkingLevel = {
|
|
423
|
+
MINIMAL: 'MINIMAL',
|
|
424
|
+
LOW: 'LOW',
|
|
425
|
+
MEDIUM: 'MEDIUM',
|
|
426
|
+
HIGH: 'HIGH'
|
|
427
|
+
};
|
|
415
428
|
|
|
416
429
|
/**
|
|
417
430
|
* @license
|
|
@@ -1137,6 +1150,8 @@ const logger = new logger$1.Logger('@firebase/vertexai');
|
|
|
1137
1150
|
* See the License for the specific language governing permissions and
|
|
1138
1151
|
* limitations under the License.
|
|
1139
1152
|
*/
|
|
1153
|
+
const TIMEOUT_EXPIRED_MESSAGE = 'Timeout has expired.';
|
|
1154
|
+
const ABORT_ERROR_NAME = 'AbortError';
|
|
1140
1155
|
class RequestURL {
|
|
1141
1156
|
constructor(params) {
|
|
1142
1157
|
this.params = params;
|
|
@@ -1159,7 +1174,7 @@ class RequestURL {
|
|
|
1159
1174
|
}
|
|
1160
1175
|
}
|
|
1161
1176
|
get baseUrl() {
|
|
1162
|
-
return this.params.
|
|
1177
|
+
return (this.params.singleRequestOptions?.baseUrl ?? `https://${DEFAULT_DOMAIN}`);
|
|
1163
1178
|
}
|
|
1164
1179
|
get queryParams() {
|
|
1165
1180
|
const params = new URLSearchParams();
|
|
@@ -1227,21 +1242,32 @@ async function getHeaders(url) {
|
|
|
1227
1242
|
async function makeRequest(requestUrlParams, body) {
|
|
1228
1243
|
const url = new RequestURL(requestUrlParams);
|
|
1229
1244
|
let response;
|
|
1230
|
-
|
|
1245
|
+
const externalSignal = requestUrlParams.singleRequestOptions?.signal;
|
|
1246
|
+
const timeoutMillis = requestUrlParams.singleRequestOptions?.timeout != null &&
|
|
1247
|
+
requestUrlParams.singleRequestOptions.timeout >= 0
|
|
1248
|
+
? requestUrlParams.singleRequestOptions.timeout
|
|
1249
|
+
: DEFAULT_FETCH_TIMEOUT_MS;
|
|
1250
|
+
const internalAbortController = new AbortController();
|
|
1251
|
+
const fetchTimeoutId = setTimeout(() => {
|
|
1252
|
+
internalAbortController.abort(new DOMException(TIMEOUT_EXPIRED_MESSAGE, ABORT_ERROR_NAME));
|
|
1253
|
+
logger.debug(`Aborting request to ${url} due to timeout (${timeoutMillis}ms)`);
|
|
1254
|
+
}, timeoutMillis);
|
|
1255
|
+
// Used to abort the fetch if either the user-defined `externalSignal` is aborted, or if the
|
|
1256
|
+
// internal signal (triggered by timeouts) is aborted.
|
|
1257
|
+
const combinedSignal = AbortSignal.any(externalSignal
|
|
1258
|
+
? [externalSignal, internalAbortController.signal]
|
|
1259
|
+
: [internalAbortController.signal]);
|
|
1260
|
+
if (externalSignal && externalSignal.aborted) {
|
|
1261
|
+
clearTimeout(fetchTimeoutId);
|
|
1262
|
+
throw new DOMException(externalSignal.reason ?? 'Aborted externally before fetch', ABORT_ERROR_NAME);
|
|
1263
|
+
}
|
|
1231
1264
|
try {
|
|
1232
1265
|
const fetchOptions = {
|
|
1233
1266
|
method: 'POST',
|
|
1234
1267
|
headers: await getHeaders(url),
|
|
1268
|
+
signal: combinedSignal,
|
|
1235
1269
|
body
|
|
1236
1270
|
};
|
|
1237
|
-
// Timeout is 180s by default.
|
|
1238
|
-
const timeoutMillis = requestUrlParams.requestOptions?.timeout != null &&
|
|
1239
|
-
requestUrlParams.requestOptions.timeout >= 0
|
|
1240
|
-
? requestUrlParams.requestOptions.timeout
|
|
1241
|
-
: DEFAULT_FETCH_TIMEOUT_MS;
|
|
1242
|
-
const abortController = new AbortController();
|
|
1243
|
-
fetchTimeoutId = setTimeout(() => abortController.abort(), timeoutMillis);
|
|
1244
|
-
fetchOptions.signal = abortController.signal;
|
|
1245
1271
|
response = await fetch(url.toString(), fetchOptions);
|
|
1246
1272
|
if (!response.ok) {
|
|
1247
1273
|
let message = '';
|
|
@@ -1284,16 +1310,18 @@ async function makeRequest(requestUrlParams, body) {
|
|
|
1284
1310
|
let err = e;
|
|
1285
1311
|
if (e.code !== AIErrorCode.FETCH_ERROR &&
|
|
1286
1312
|
e.code !== AIErrorCode.API_NOT_ENABLED &&
|
|
1287
|
-
e instanceof Error
|
|
1313
|
+
e instanceof Error &&
|
|
1314
|
+
e.name !== ABORT_ERROR_NAME) {
|
|
1288
1315
|
err = new AIError(AIErrorCode.ERROR, `Error fetching from ${url.toString()}: ${e.message}`);
|
|
1289
1316
|
err.stack = e.stack;
|
|
1290
1317
|
}
|
|
1291
1318
|
throw err;
|
|
1292
1319
|
}
|
|
1293
1320
|
finally {
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1321
|
+
// When doing streaming requests, this will clear the timeout once the stream begins.
|
|
1322
|
+
// If a timeout it 3000ms, and the stream starts after 300ms and ends after 5000ms, the
|
|
1323
|
+
// timeout will be cleared after 300ms, so it won't abort the request.
|
|
1324
|
+
clearTimeout(fetchTimeoutId);
|
|
1297
1325
|
}
|
|
1298
1326
|
return response;
|
|
1299
1327
|
}
|
|
@@ -1731,6 +1759,8 @@ const responseLineRE = /^data\: (.*)(?:\n\n|\r\r|\r\n\r\n)/;
|
|
|
1731
1759
|
function processStream(response, apiSettings, inferenceSource) {
|
|
1732
1760
|
const inputStream = response.body.pipeThrough(new TextDecoderStream('utf8', { fatal: true }));
|
|
1733
1761
|
const responseStream = getResponseStream(inputStream);
|
|
1762
|
+
// We split the stream so the user can iterate over partial results (stream1)
|
|
1763
|
+
// while we aggregate the full result for history/final response (stream2).
|
|
1734
1764
|
const [stream1, stream2] = responseStream.tee();
|
|
1735
1765
|
return {
|
|
1736
1766
|
stream: generateResponseSequence(stream1, apiSettings, inferenceSource),
|
|
@@ -1767,7 +1797,6 @@ async function* generateResponseSequence(stream, apiSettings, inferenceSource) {
|
|
|
1767
1797
|
enhancedResponse = createEnhancedContentResponse(value, inferenceSource);
|
|
1768
1798
|
}
|
|
1769
1799
|
const firstCandidate = enhancedResponse.candidates?.[0];
|
|
1770
|
-
// Don't yield a response with no useful data for the developer.
|
|
1771
1800
|
if (!firstCandidate?.content?.parts &&
|
|
1772
1801
|
!firstCandidate?.finishReason &&
|
|
1773
1802
|
!firstCandidate?.citationMetadata &&
|
|
@@ -1778,9 +1807,7 @@ async function* generateResponseSequence(stream, apiSettings, inferenceSource) {
|
|
|
1778
1807
|
}
|
|
1779
1808
|
}
|
|
1780
1809
|
/**
|
|
1781
|
-
* Reads a raw stream
|
|
1782
|
-
* chunks, returning a new stream that provides a single complete
|
|
1783
|
-
* GenerateContentResponse in each iteration.
|
|
1810
|
+
* Reads a raw string stream, buffers incomplete chunks, and yields parsed JSON objects.
|
|
1784
1811
|
*/
|
|
1785
1812
|
function getResponseStream(inputStream) {
|
|
1786
1813
|
const reader = inputStream.getReader();
|
|
@@ -1799,6 +1826,8 @@ function getResponseStream(inputStream) {
|
|
|
1799
1826
|
return;
|
|
1800
1827
|
}
|
|
1801
1828
|
currentText += value;
|
|
1829
|
+
// SSE events may span chunk boundaries, so we buffer until we match
|
|
1830
|
+
// the full "data: {json}\n\n" pattern.
|
|
1802
1831
|
let match = currentText.match(responseLineRE);
|
|
1803
1832
|
let parsedResponse;
|
|
1804
1833
|
while (match) {
|
|
@@ -1832,8 +1861,7 @@ function aggregateResponses(responses) {
|
|
|
1832
1861
|
for (const response of responses) {
|
|
1833
1862
|
if (response.candidates) {
|
|
1834
1863
|
for (const candidate of response.candidates) {
|
|
1835
|
-
//
|
|
1836
|
-
// See: https://github.com/firebase/firebase-js-sdk/issues/8566
|
|
1864
|
+
// Use 0 if index is undefined (protobuf default value omission).
|
|
1837
1865
|
const i = candidate.index || 0;
|
|
1838
1866
|
if (!aggregatedResponse.candidates) {
|
|
1839
1867
|
aggregatedResponse.candidates = [];
|
|
@@ -1843,7 +1871,7 @@ function aggregateResponses(responses) {
|
|
|
1843
1871
|
index: candidate.index
|
|
1844
1872
|
};
|
|
1845
1873
|
}
|
|
1846
|
-
//
|
|
1874
|
+
// Overwrite with the latest metadata
|
|
1847
1875
|
aggregatedResponse.candidates[i].citationMetadata =
|
|
1848
1876
|
candidate.citationMetadata;
|
|
1849
1877
|
aggregatedResponse.candidates[i].finishReason = candidate.finishReason;
|
|
@@ -1864,12 +1892,7 @@ function aggregateResponses(responses) {
|
|
|
1864
1892
|
aggregatedResponse.candidates[i].urlContextMetadata =
|
|
1865
1893
|
urlContextMetadata;
|
|
1866
1894
|
}
|
|
1867
|
-
/**
|
|
1868
|
-
* Candidates should always have content and parts, but this handles
|
|
1869
|
-
* possible malformed responses.
|
|
1870
|
-
*/
|
|
1871
1895
|
if (candidate.content) {
|
|
1872
|
-
// Skip a candidate without parts.
|
|
1873
1896
|
if (!candidate.content.parts) {
|
|
1874
1897
|
continue;
|
|
1875
1898
|
}
|
|
@@ -2001,7 +2024,7 @@ async function callCloudOrDevice(request, chromeAdapter, onDeviceCall, inCloudCa
|
|
|
2001
2024
|
* See the License for the specific language governing permissions and
|
|
2002
2025
|
* limitations under the License.
|
|
2003
2026
|
*/
|
|
2004
|
-
async function generateContentStreamOnCloud(apiSettings, model, params,
|
|
2027
|
+
async function generateContentStreamOnCloud(apiSettings, model, params, singleRequestOptions) {
|
|
2005
2028
|
if (apiSettings.backend.backendType === BackendType.GOOGLE_AI) {
|
|
2006
2029
|
params = mapGenerateContentRequest(params);
|
|
2007
2030
|
}
|
|
@@ -2010,14 +2033,14 @@ async function generateContentStreamOnCloud(apiSettings, model, params, requestO
|
|
|
2010
2033
|
model,
|
|
2011
2034
|
apiSettings,
|
|
2012
2035
|
stream: true,
|
|
2013
|
-
|
|
2036
|
+
singleRequestOptions
|
|
2014
2037
|
}, JSON.stringify(params));
|
|
2015
2038
|
}
|
|
2016
|
-
async function generateContentStream(apiSettings, model, params, chromeAdapter,
|
|
2017
|
-
const callResult = await callCloudOrDevice(params, chromeAdapter, () => chromeAdapter.generateContentStream(params), () => generateContentStreamOnCloud(apiSettings, model, params,
|
|
2039
|
+
async function generateContentStream(apiSettings, model, params, chromeAdapter, singleRequestOptions) {
|
|
2040
|
+
const callResult = await callCloudOrDevice(params, chromeAdapter, () => chromeAdapter.generateContentStream(params), () => generateContentStreamOnCloud(apiSettings, model, params, singleRequestOptions));
|
|
2018
2041
|
return processStream(callResult.response, apiSettings, callResult.inferenceSource);
|
|
2019
2042
|
}
|
|
2020
|
-
async function generateContentOnCloud(apiSettings, model, params,
|
|
2043
|
+
async function generateContentOnCloud(apiSettings, model, params, singleRequestOptions) {
|
|
2021
2044
|
if (apiSettings.backend.backendType === BackendType.GOOGLE_AI) {
|
|
2022
2045
|
params = mapGenerateContentRequest(params);
|
|
2023
2046
|
}
|
|
@@ -2026,16 +2049,16 @@ async function generateContentOnCloud(apiSettings, model, params, requestOptions
|
|
|
2026
2049
|
task: "generateContent" /* Task.GENERATE_CONTENT */,
|
|
2027
2050
|
apiSettings,
|
|
2028
2051
|
stream: false,
|
|
2029
|
-
|
|
2052
|
+
singleRequestOptions
|
|
2030
2053
|
}, JSON.stringify(params));
|
|
2031
2054
|
}
|
|
2032
|
-
async function templateGenerateContent(apiSettings, templateId, templateParams,
|
|
2055
|
+
async function templateGenerateContent(apiSettings, templateId, templateParams, singleRequestOptions) {
|
|
2033
2056
|
const response = await makeRequest({
|
|
2034
2057
|
task: "templateGenerateContent" /* ServerPromptTemplateTask.TEMPLATE_GENERATE_CONTENT */,
|
|
2035
2058
|
templateId,
|
|
2036
2059
|
apiSettings,
|
|
2037
2060
|
stream: false,
|
|
2038
|
-
|
|
2061
|
+
singleRequestOptions
|
|
2039
2062
|
}, JSON.stringify(templateParams));
|
|
2040
2063
|
const generateContentResponse = await processGenerateContentResponse(response, apiSettings);
|
|
2041
2064
|
const enhancedResponse = createEnhancedContentResponse(generateContentResponse);
|
|
@@ -2043,18 +2066,18 @@ async function templateGenerateContent(apiSettings, templateId, templateParams,
|
|
|
2043
2066
|
response: enhancedResponse
|
|
2044
2067
|
};
|
|
2045
2068
|
}
|
|
2046
|
-
async function templateGenerateContentStream(apiSettings, templateId, templateParams,
|
|
2069
|
+
async function templateGenerateContentStream(apiSettings, templateId, templateParams, singleRequestOptions) {
|
|
2047
2070
|
const response = await makeRequest({
|
|
2048
2071
|
task: "templateStreamGenerateContent" /* ServerPromptTemplateTask.TEMPLATE_STREAM_GENERATE_CONTENT */,
|
|
2049
2072
|
templateId,
|
|
2050
2073
|
apiSettings,
|
|
2051
2074
|
stream: true,
|
|
2052
|
-
|
|
2075
|
+
singleRequestOptions
|
|
2053
2076
|
}, JSON.stringify(templateParams));
|
|
2054
2077
|
return processStream(response, apiSettings);
|
|
2055
2078
|
}
|
|
2056
|
-
async function generateContent(apiSettings, model, params, chromeAdapter,
|
|
2057
|
-
const callResult = await callCloudOrDevice(params, chromeAdapter, () => chromeAdapter.generateContent(params), () => generateContentOnCloud(apiSettings, model, params,
|
|
2079
|
+
async function generateContent(apiSettings, model, params, chromeAdapter, singleRequestOptions) {
|
|
2080
|
+
const callResult = await callCloudOrDevice(params, chromeAdapter, () => chromeAdapter.generateContent(params), () => generateContentOnCloud(apiSettings, model, params, singleRequestOptions));
|
|
2058
2081
|
const generateContentResponse = await processGenerateContentResponse(callResult.response, apiSettings);
|
|
2059
2082
|
const enhancedResponse = createEnhancedContentResponse(generateContentResponse, callResult.inferenceSource);
|
|
2060
2083
|
return {
|
|
@@ -2308,7 +2331,8 @@ function validateChatHistory(history) {
|
|
|
2308
2331
|
* limitations under the License.
|
|
2309
2332
|
*/
|
|
2310
2333
|
/**
|
|
2311
|
-
*
|
|
2334
|
+
* Used to break the internal promise chain when an error is already handled
|
|
2335
|
+
* by the user, preventing duplicate console logs.
|
|
2312
2336
|
*/
|
|
2313
2337
|
const SILENT_ERROR = 'SILENT_ERROR';
|
|
2314
2338
|
/**
|
|
@@ -2324,6 +2348,10 @@ class ChatSession {
|
|
|
2324
2348
|
this.params = params;
|
|
2325
2349
|
this.requestOptions = requestOptions;
|
|
2326
2350
|
this._history = [];
|
|
2351
|
+
/**
|
|
2352
|
+
* Ensures sequential execution of chat messages to maintain history order.
|
|
2353
|
+
* Each call waits for the previous one to settle before proceeding.
|
|
2354
|
+
*/
|
|
2327
2355
|
this._sendPromise = Promise.resolve();
|
|
2328
2356
|
this._apiSettings = apiSettings;
|
|
2329
2357
|
if (params?.history) {
|
|
@@ -2344,7 +2372,7 @@ class ChatSession {
|
|
|
2344
2372
|
* Sends a chat message and receives a non-streaming
|
|
2345
2373
|
* {@link GenerateContentResult}
|
|
2346
2374
|
*/
|
|
2347
|
-
async sendMessage(request) {
|
|
2375
|
+
async sendMessage(request, singleRequestOptions) {
|
|
2348
2376
|
await this._sendPromise;
|
|
2349
2377
|
const newContent = formatNewContent(request);
|
|
2350
2378
|
const generateContentRequest = {
|
|
@@ -2356,16 +2384,20 @@ class ChatSession {
|
|
|
2356
2384
|
contents: [...this._history, newContent]
|
|
2357
2385
|
};
|
|
2358
2386
|
let finalResult = {};
|
|
2359
|
-
// Add onto the chain.
|
|
2360
2387
|
this._sendPromise = this._sendPromise
|
|
2361
|
-
.then(() => generateContent(this._apiSettings, this.model, generateContentRequest, this.chromeAdapter,
|
|
2388
|
+
.then(() => generateContent(this._apiSettings, this.model, generateContentRequest, this.chromeAdapter, {
|
|
2389
|
+
...this.requestOptions,
|
|
2390
|
+
...singleRequestOptions
|
|
2391
|
+
}))
|
|
2362
2392
|
.then(result => {
|
|
2393
|
+
// TODO: Make this update atomic. If creating `responseContent` throws,
|
|
2394
|
+
// history will contain the user message but not the response, causing
|
|
2395
|
+
// validation errors on the next request.
|
|
2363
2396
|
if (result.response.candidates &&
|
|
2364
2397
|
result.response.candidates.length > 0) {
|
|
2365
2398
|
this._history.push(newContent);
|
|
2366
2399
|
const responseContent = {
|
|
2367
2400
|
parts: result.response.candidates?.[0].content.parts || [],
|
|
2368
|
-
// Response seems to come back without a role set.
|
|
2369
2401
|
role: result.response.candidates?.[0].content.role || 'model'
|
|
2370
2402
|
};
|
|
2371
2403
|
this._history.push(responseContent);
|
|
@@ -2386,7 +2418,7 @@ class ChatSession {
|
|
|
2386
2418
|
* {@link GenerateContentStreamResult} containing an iterable stream
|
|
2387
2419
|
* and a response promise.
|
|
2388
2420
|
*/
|
|
2389
|
-
async sendMessageStream(request) {
|
|
2421
|
+
async sendMessageStream(request, singleRequestOptions) {
|
|
2390
2422
|
await this._sendPromise;
|
|
2391
2423
|
const newContent = formatNewContent(request);
|
|
2392
2424
|
const generateContentRequest = {
|
|
@@ -2397,21 +2429,29 @@ class ChatSession {
|
|
|
2397
2429
|
systemInstruction: this.params?.systemInstruction,
|
|
2398
2430
|
contents: [...this._history, newContent]
|
|
2399
2431
|
};
|
|
2400
|
-
const streamPromise = generateContentStream(this._apiSettings, this.model, generateContentRequest, this.chromeAdapter,
|
|
2401
|
-
|
|
2432
|
+
const streamPromise = generateContentStream(this._apiSettings, this.model, generateContentRequest, this.chromeAdapter, {
|
|
2433
|
+
...this.requestOptions,
|
|
2434
|
+
...singleRequestOptions
|
|
2435
|
+
});
|
|
2436
|
+
// We hook into the chain to update history, but we don't block the
|
|
2437
|
+
// return of `streamPromise` to the user.
|
|
2402
2438
|
this._sendPromise = this._sendPromise
|
|
2403
2439
|
.then(() => streamPromise)
|
|
2404
|
-
// This must be handled to avoid unhandled rejection, but jump
|
|
2405
|
-
// to the final catch block with a label to not log this error.
|
|
2406
2440
|
.catch(_ignored => {
|
|
2441
|
+
// If the initial fetch fails, the user's `streamPromise` rejects.
|
|
2442
|
+
// We swallow the error here to prevent double logging in the final catch.
|
|
2407
2443
|
throw new Error(SILENT_ERROR);
|
|
2408
2444
|
})
|
|
2409
2445
|
.then(streamResult => streamResult.response)
|
|
2410
2446
|
.then(response => {
|
|
2447
|
+
// This runs after the stream completes. Runtime errors here cannot be
|
|
2448
|
+
// caught by the user because their promise has likely already resolved.
|
|
2449
|
+
// TODO: Move response validation logic upstream to `stream-reader` so
|
|
2450
|
+
// errors propagate to the user's `result.response` promise.
|
|
2411
2451
|
if (response.candidates && response.candidates.length > 0) {
|
|
2412
2452
|
this._history.push(newContent);
|
|
2453
|
+
// TODO: Validate that `response.candidates[0].content` is not null.
|
|
2413
2454
|
const responseContent = { ...response.candidates[0].content };
|
|
2414
|
-
// Response seems to come back without a role set.
|
|
2415
2455
|
if (!responseContent.role) {
|
|
2416
2456
|
responseContent.role = 'model';
|
|
2417
2457
|
}
|
|
@@ -2425,12 +2465,8 @@ class ChatSession {
|
|
|
2425
2465
|
}
|
|
2426
2466
|
})
|
|
2427
2467
|
.catch(e => {
|
|
2428
|
-
//
|
|
2429
|
-
|
|
2430
|
-
// Avoid duplicating the error message in logs.
|
|
2431
|
-
if (e.message !== SILENT_ERROR) {
|
|
2432
|
-
// Users do not have access to _sendPromise to catch errors
|
|
2433
|
-
// downstream from streamPromise, so they should not throw.
|
|
2468
|
+
// Filter out errors already handled by the user or initiated by them.
|
|
2469
|
+
if (e.message !== SILENT_ERROR && e.name !== 'AbortError') {
|
|
2434
2470
|
logger.error(e);
|
|
2435
2471
|
}
|
|
2436
2472
|
});
|
|
@@ -2454,7 +2490,7 @@ class ChatSession {
|
|
|
2454
2490
|
* See the License for the specific language governing permissions and
|
|
2455
2491
|
* limitations under the License.
|
|
2456
2492
|
*/
|
|
2457
|
-
async function countTokensOnCloud(apiSettings, model, params,
|
|
2493
|
+
async function countTokensOnCloud(apiSettings, model, params, singleRequestOptions) {
|
|
2458
2494
|
let body = '';
|
|
2459
2495
|
if (apiSettings.backend.backendType === BackendType.GOOGLE_AI) {
|
|
2460
2496
|
const mappedParams = mapCountTokensRequest(params, model);
|
|
@@ -2468,7 +2504,7 @@ async function countTokensOnCloud(apiSettings, model, params, requestOptions) {
|
|
|
2468
2504
|
task: "countTokens" /* Task.COUNT_TOKENS */,
|
|
2469
2505
|
apiSettings,
|
|
2470
2506
|
stream: false,
|
|
2471
|
-
|
|
2507
|
+
singleRequestOptions
|
|
2472
2508
|
}, body);
|
|
2473
2509
|
return response.json();
|
|
2474
2510
|
}
|
|
@@ -2504,6 +2540,7 @@ class GenerativeModel extends AIModel {
|
|
|
2504
2540
|
super(ai, modelParams.model);
|
|
2505
2541
|
this.chromeAdapter = chromeAdapter;
|
|
2506
2542
|
this.generationConfig = modelParams.generationConfig || {};
|
|
2543
|
+
validateGenerationConfig(this.generationConfig);
|
|
2507
2544
|
this.safetySettings = modelParams.safetySettings || [];
|
|
2508
2545
|
this.tools = modelParams.tools;
|
|
2509
2546
|
this.toolConfig = modelParams.toolConfig;
|
|
@@ -2514,7 +2551,7 @@ class GenerativeModel extends AIModel {
|
|
|
2514
2551
|
* Makes a single non-streaming call to the model
|
|
2515
2552
|
* and returns an object containing a single {@link GenerateContentResponse}.
|
|
2516
2553
|
*/
|
|
2517
|
-
async generateContent(request) {
|
|
2554
|
+
async generateContent(request, singleRequestOptions) {
|
|
2518
2555
|
const formattedParams = formatGenerateContentInput(request);
|
|
2519
2556
|
return generateContent(this._apiSettings, this.model, {
|
|
2520
2557
|
generationConfig: this.generationConfig,
|
|
@@ -2523,7 +2560,12 @@ class GenerativeModel extends AIModel {
|
|
|
2523
2560
|
toolConfig: this.toolConfig,
|
|
2524
2561
|
systemInstruction: this.systemInstruction,
|
|
2525
2562
|
...formattedParams
|
|
2526
|
-
}, this.chromeAdapter,
|
|
2563
|
+
}, this.chromeAdapter,
|
|
2564
|
+
// Merge request options
|
|
2565
|
+
{
|
|
2566
|
+
...this.requestOptions,
|
|
2567
|
+
...singleRequestOptions
|
|
2568
|
+
});
|
|
2527
2569
|
}
|
|
2528
2570
|
/**
|
|
2529
2571
|
* Makes a single streaming call to the model
|
|
@@ -2531,7 +2573,7 @@ class GenerativeModel extends AIModel {
|
|
|
2531
2573
|
* over all chunks in the streaming response as well as
|
|
2532
2574
|
* a promise that returns the final aggregated response.
|
|
2533
2575
|
*/
|
|
2534
|
-
async generateContentStream(request) {
|
|
2576
|
+
async generateContentStream(request, singleRequestOptions) {
|
|
2535
2577
|
const formattedParams = formatGenerateContentInput(request);
|
|
2536
2578
|
return generateContentStream(this._apiSettings, this.model, {
|
|
2537
2579
|
generationConfig: this.generationConfig,
|
|
@@ -2540,7 +2582,12 @@ class GenerativeModel extends AIModel {
|
|
|
2540
2582
|
toolConfig: this.toolConfig,
|
|
2541
2583
|
systemInstruction: this.systemInstruction,
|
|
2542
2584
|
...formattedParams
|
|
2543
|
-
}, this.chromeAdapter,
|
|
2585
|
+
}, this.chromeAdapter,
|
|
2586
|
+
// Merge request options
|
|
2587
|
+
{
|
|
2588
|
+
...this.requestOptions,
|
|
2589
|
+
...singleRequestOptions
|
|
2590
|
+
});
|
|
2544
2591
|
}
|
|
2545
2592
|
/**
|
|
2546
2593
|
* Gets a new {@link ChatSession} instance which can be used for
|
|
@@ -2564,9 +2611,26 @@ class GenerativeModel extends AIModel {
|
|
|
2564
2611
|
/**
|
|
2565
2612
|
* Counts the tokens in the provided request.
|
|
2566
2613
|
*/
|
|
2567
|
-
async countTokens(request) {
|
|
2614
|
+
async countTokens(request, singleRequestOptions) {
|
|
2568
2615
|
const formattedParams = formatGenerateContentInput(request);
|
|
2569
|
-
return countTokens(this._apiSettings, this.model, formattedParams, this.chromeAdapter
|
|
2616
|
+
return countTokens(this._apiSettings, this.model, formattedParams, this.chromeAdapter,
|
|
2617
|
+
// Merge request options
|
|
2618
|
+
{
|
|
2619
|
+
...this.requestOptions,
|
|
2620
|
+
...singleRequestOptions
|
|
2621
|
+
});
|
|
2622
|
+
}
|
|
2623
|
+
}
|
|
2624
|
+
/**
|
|
2625
|
+
* Client-side validation of some common `GenerationConfig` pitfalls, in order
|
|
2626
|
+
* to save the developer a wasted request.
|
|
2627
|
+
*/
|
|
2628
|
+
function validateGenerationConfig(generationConfig) {
|
|
2629
|
+
if (
|
|
2630
|
+
// != allows for null and undefined. 0 is considered "set" by the model
|
|
2631
|
+
generationConfig.thinkingConfig?.thinkingBudget != null &&
|
|
2632
|
+
generationConfig.thinkingConfig?.thinkingLevel) {
|
|
2633
|
+
throw new AIError(AIErrorCode.UNSUPPORTED, `Cannot set both thinkingBudget and thinkingLevel in a config.`);
|
|
2570
2634
|
}
|
|
2571
2635
|
}
|
|
2572
2636
|
|
|
@@ -3017,7 +3081,7 @@ class ImagenModel extends AIModel {
|
|
|
3017
3081
|
*
|
|
3018
3082
|
* @public
|
|
3019
3083
|
*/
|
|
3020
|
-
async generateImages(prompt) {
|
|
3084
|
+
async generateImages(prompt, singleRequestOptions) {
|
|
3021
3085
|
const body = createPredictRequestBody(prompt, {
|
|
3022
3086
|
...this.generationConfig,
|
|
3023
3087
|
...this.safetySettings
|
|
@@ -3027,7 +3091,11 @@ class ImagenModel extends AIModel {
|
|
|
3027
3091
|
model: this.model,
|
|
3028
3092
|
apiSettings: this._apiSettings,
|
|
3029
3093
|
stream: false,
|
|
3030
|
-
|
|
3094
|
+
// Merge request options. Single request options overwrite the model's request options.
|
|
3095
|
+
singleRequestOptions: {
|
|
3096
|
+
...this.requestOptions,
|
|
3097
|
+
...singleRequestOptions
|
|
3098
|
+
}
|
|
3031
3099
|
}, JSON.stringify(body));
|
|
3032
3100
|
return handlePredictResponse(response);
|
|
3033
3101
|
}
|
|
@@ -3050,7 +3118,7 @@ class ImagenModel extends AIModel {
|
|
|
3050
3118
|
* returned object will have a `filteredReason` property.
|
|
3051
3119
|
* If all images are filtered, the `images` array will be empty.
|
|
3052
3120
|
*/
|
|
3053
|
-
async generateImagesGCS(prompt, gcsURI) {
|
|
3121
|
+
async generateImagesGCS(prompt, gcsURI, singleRequestOptions) {
|
|
3054
3122
|
const body = createPredictRequestBody(prompt, {
|
|
3055
3123
|
gcsURI,
|
|
3056
3124
|
...this.generationConfig,
|
|
@@ -3061,7 +3129,11 @@ class ImagenModel extends AIModel {
|
|
|
3061
3129
|
model: this.model,
|
|
3062
3130
|
apiSettings: this._apiSettings,
|
|
3063
3131
|
stream: false,
|
|
3064
|
-
|
|
3132
|
+
// Merge request options. Single request options overwrite the model's request options.
|
|
3133
|
+
singleRequestOptions: {
|
|
3134
|
+
...this.requestOptions,
|
|
3135
|
+
...singleRequestOptions
|
|
3136
|
+
}
|
|
3065
3137
|
}, JSON.stringify(body));
|
|
3066
3138
|
return handlePredictResponse(response);
|
|
3067
3139
|
}
|
|
@@ -3255,9 +3327,11 @@ class TemplateGenerativeModel {
|
|
|
3255
3327
|
*
|
|
3256
3328
|
* @beta
|
|
3257
3329
|
*/
|
|
3258
|
-
async generateContent(templateId, templateVariables
|
|
3259
|
-
|
|
3260
|
-
|
|
3330
|
+
async generateContent(templateId, templateVariables, singleRequestOptions) {
|
|
3331
|
+
return templateGenerateContent(this._apiSettings, templateId, { inputs: templateVariables }, {
|
|
3332
|
+
...this.requestOptions,
|
|
3333
|
+
...singleRequestOptions
|
|
3334
|
+
});
|
|
3261
3335
|
}
|
|
3262
3336
|
/**
|
|
3263
3337
|
* Makes a single streaming call to the model and returns an object
|
|
@@ -3271,8 +3345,11 @@ class TemplateGenerativeModel {
|
|
|
3271
3345
|
*
|
|
3272
3346
|
* @beta
|
|
3273
3347
|
*/
|
|
3274
|
-
async generateContentStream(templateId, templateVariables) {
|
|
3275
|
-
return templateGenerateContentStream(this._apiSettings, templateId, { inputs: templateVariables },
|
|
3348
|
+
async generateContentStream(templateId, templateVariables, singleRequestOptions) {
|
|
3349
|
+
return templateGenerateContentStream(this._apiSettings, templateId, { inputs: templateVariables }, {
|
|
3350
|
+
...this.requestOptions,
|
|
3351
|
+
...singleRequestOptions
|
|
3352
|
+
});
|
|
3276
3353
|
}
|
|
3277
3354
|
}
|
|
3278
3355
|
|
|
@@ -3317,13 +3394,16 @@ class TemplateImagenModel {
|
|
|
3317
3394
|
*
|
|
3318
3395
|
* @beta
|
|
3319
3396
|
*/
|
|
3320
|
-
async generateImages(templateId, templateVariables) {
|
|
3397
|
+
async generateImages(templateId, templateVariables, singleRequestOptions) {
|
|
3321
3398
|
const response = await makeRequest({
|
|
3322
3399
|
task: "templatePredict" /* ServerPromptTemplateTask.TEMPLATE_PREDICT */,
|
|
3323
3400
|
templateId,
|
|
3324
3401
|
apiSettings: this._apiSettings,
|
|
3325
3402
|
stream: false,
|
|
3326
|
-
|
|
3403
|
+
singleRequestOptions: {
|
|
3404
|
+
...this.requestOptions,
|
|
3405
|
+
...singleRequestOptions
|
|
3406
|
+
}
|
|
3327
3407
|
}, JSON.stringify({ inputs: templateVariables }));
|
|
3328
3408
|
return handlePredictResponse(response);
|
|
3329
3409
|
}
|
|
@@ -4200,6 +4280,7 @@ exports.SchemaType = SchemaType;
|
|
|
4200
4280
|
exports.StringSchema = StringSchema;
|
|
4201
4281
|
exports.TemplateGenerativeModel = TemplateGenerativeModel;
|
|
4202
4282
|
exports.TemplateImagenModel = TemplateImagenModel;
|
|
4283
|
+
exports.ThinkingLevel = ThinkingLevel;
|
|
4203
4284
|
exports.URLRetrievalStatus = URLRetrievalStatus;
|
|
4204
4285
|
exports.VertexAIBackend = VertexAIBackend;
|
|
4205
4286
|
exports.getAI = getAI;
|