@firebase/ai 2.6.1 → 2.7.0-20260114160934
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.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-20260114160934";
|
|
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
|
|
@@ -1445,6 +1458,8 @@ class AIModel {
|
|
|
1445
1458
|
* See the License for the specific language governing permissions and
|
|
1446
1459
|
* limitations under the License.
|
|
1447
1460
|
*/
|
|
1461
|
+
const TIMEOUT_EXPIRED_MESSAGE = 'Timeout has expired.';
|
|
1462
|
+
const ABORT_ERROR_NAME = 'AbortError';
|
|
1448
1463
|
class RequestURL {
|
|
1449
1464
|
constructor(params) {
|
|
1450
1465
|
this.params = params;
|
|
@@ -1467,7 +1482,7 @@ class RequestURL {
|
|
|
1467
1482
|
}
|
|
1468
1483
|
}
|
|
1469
1484
|
get baseUrl() {
|
|
1470
|
-
return this.params.
|
|
1485
|
+
return (this.params.singleRequestOptions?.baseUrl ?? `https://${DEFAULT_DOMAIN}`);
|
|
1471
1486
|
}
|
|
1472
1487
|
get queryParams() {
|
|
1473
1488
|
const params = new URLSearchParams();
|
|
@@ -1535,21 +1550,32 @@ async function getHeaders(url) {
|
|
|
1535
1550
|
async function makeRequest(requestUrlParams, body) {
|
|
1536
1551
|
const url = new RequestURL(requestUrlParams);
|
|
1537
1552
|
let response;
|
|
1538
|
-
|
|
1553
|
+
const externalSignal = requestUrlParams.singleRequestOptions?.signal;
|
|
1554
|
+
const timeoutMillis = requestUrlParams.singleRequestOptions?.timeout != null &&
|
|
1555
|
+
requestUrlParams.singleRequestOptions.timeout >= 0
|
|
1556
|
+
? requestUrlParams.singleRequestOptions.timeout
|
|
1557
|
+
: DEFAULT_FETCH_TIMEOUT_MS;
|
|
1558
|
+
const internalAbortController = new AbortController();
|
|
1559
|
+
const fetchTimeoutId = setTimeout(() => {
|
|
1560
|
+
internalAbortController.abort(new DOMException(TIMEOUT_EXPIRED_MESSAGE, ABORT_ERROR_NAME));
|
|
1561
|
+
logger.debug(`Aborting request to ${url} due to timeout (${timeoutMillis}ms)`);
|
|
1562
|
+
}, timeoutMillis);
|
|
1563
|
+
// Used to abort the fetch if either the user-defined `externalSignal` is aborted, or if the
|
|
1564
|
+
// internal signal (triggered by timeouts) is aborted.
|
|
1565
|
+
const combinedSignal = AbortSignal.any(externalSignal
|
|
1566
|
+
? [externalSignal, internalAbortController.signal]
|
|
1567
|
+
: [internalAbortController.signal]);
|
|
1568
|
+
if (externalSignal && externalSignal.aborted) {
|
|
1569
|
+
clearTimeout(fetchTimeoutId);
|
|
1570
|
+
throw new DOMException(externalSignal.reason ?? 'Aborted externally before fetch', ABORT_ERROR_NAME);
|
|
1571
|
+
}
|
|
1539
1572
|
try {
|
|
1540
1573
|
const fetchOptions = {
|
|
1541
1574
|
method: 'POST',
|
|
1542
1575
|
headers: await getHeaders(url),
|
|
1576
|
+
signal: combinedSignal,
|
|
1543
1577
|
body
|
|
1544
1578
|
};
|
|
1545
|
-
// Timeout is 180s by default.
|
|
1546
|
-
const timeoutMillis = requestUrlParams.requestOptions?.timeout != null &&
|
|
1547
|
-
requestUrlParams.requestOptions.timeout >= 0
|
|
1548
|
-
? requestUrlParams.requestOptions.timeout
|
|
1549
|
-
: DEFAULT_FETCH_TIMEOUT_MS;
|
|
1550
|
-
const abortController = new AbortController();
|
|
1551
|
-
fetchTimeoutId = setTimeout(() => abortController.abort(), timeoutMillis);
|
|
1552
|
-
fetchOptions.signal = abortController.signal;
|
|
1553
1579
|
response = await fetch(url.toString(), fetchOptions);
|
|
1554
1580
|
if (!response.ok) {
|
|
1555
1581
|
let message = '';
|
|
@@ -1592,16 +1618,18 @@ async function makeRequest(requestUrlParams, body) {
|
|
|
1592
1618
|
let err = e;
|
|
1593
1619
|
if (e.code !== AIErrorCode.FETCH_ERROR &&
|
|
1594
1620
|
e.code !== AIErrorCode.API_NOT_ENABLED &&
|
|
1595
|
-
e instanceof Error
|
|
1621
|
+
e instanceof Error &&
|
|
1622
|
+
e.name !== ABORT_ERROR_NAME) {
|
|
1596
1623
|
err = new AIError(AIErrorCode.ERROR, `Error fetching from ${url.toString()}: ${e.message}`);
|
|
1597
1624
|
err.stack = e.stack;
|
|
1598
1625
|
}
|
|
1599
1626
|
throw err;
|
|
1600
1627
|
}
|
|
1601
1628
|
finally {
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1629
|
+
// When doing streaming requests, this will clear the timeout once the stream begins.
|
|
1630
|
+
// If a timeout it 3000ms, and the stream starts after 300ms and ends after 5000ms, the
|
|
1631
|
+
// timeout will be cleared after 300ms, so it won't abort the request.
|
|
1632
|
+
clearTimeout(fetchTimeoutId);
|
|
1605
1633
|
}
|
|
1606
1634
|
return response;
|
|
1607
1635
|
}
|
|
@@ -2039,6 +2067,8 @@ const responseLineRE = /^data\: (.*)(?:\n\n|\r\r|\r\n\r\n)/;
|
|
|
2039
2067
|
function processStream(response, apiSettings, inferenceSource) {
|
|
2040
2068
|
const inputStream = response.body.pipeThrough(new TextDecoderStream('utf8', { fatal: true }));
|
|
2041
2069
|
const responseStream = getResponseStream(inputStream);
|
|
2070
|
+
// We split the stream so the user can iterate over partial results (stream1)
|
|
2071
|
+
// while we aggregate the full result for history/final response (stream2).
|
|
2042
2072
|
const [stream1, stream2] = responseStream.tee();
|
|
2043
2073
|
return {
|
|
2044
2074
|
stream: generateResponseSequence(stream1, apiSettings, inferenceSource),
|
|
@@ -2075,7 +2105,6 @@ async function* generateResponseSequence(stream, apiSettings, inferenceSource) {
|
|
|
2075
2105
|
enhancedResponse = createEnhancedContentResponse(value, inferenceSource);
|
|
2076
2106
|
}
|
|
2077
2107
|
const firstCandidate = enhancedResponse.candidates?.[0];
|
|
2078
|
-
// Don't yield a response with no useful data for the developer.
|
|
2079
2108
|
if (!firstCandidate?.content?.parts &&
|
|
2080
2109
|
!firstCandidate?.finishReason &&
|
|
2081
2110
|
!firstCandidate?.citationMetadata &&
|
|
@@ -2086,9 +2115,7 @@ async function* generateResponseSequence(stream, apiSettings, inferenceSource) {
|
|
|
2086
2115
|
}
|
|
2087
2116
|
}
|
|
2088
2117
|
/**
|
|
2089
|
-
* Reads a raw stream
|
|
2090
|
-
* chunks, returning a new stream that provides a single complete
|
|
2091
|
-
* GenerateContentResponse in each iteration.
|
|
2118
|
+
* Reads a raw string stream, buffers incomplete chunks, and yields parsed JSON objects.
|
|
2092
2119
|
*/
|
|
2093
2120
|
function getResponseStream(inputStream) {
|
|
2094
2121
|
const reader = inputStream.getReader();
|
|
@@ -2107,6 +2134,8 @@ function getResponseStream(inputStream) {
|
|
|
2107
2134
|
return;
|
|
2108
2135
|
}
|
|
2109
2136
|
currentText += value;
|
|
2137
|
+
// SSE events may span chunk boundaries, so we buffer until we match
|
|
2138
|
+
// the full "data: {json}\n\n" pattern.
|
|
2110
2139
|
let match = currentText.match(responseLineRE);
|
|
2111
2140
|
let parsedResponse;
|
|
2112
2141
|
while (match) {
|
|
@@ -2140,8 +2169,7 @@ function aggregateResponses(responses) {
|
|
|
2140
2169
|
for (const response of responses) {
|
|
2141
2170
|
if (response.candidates) {
|
|
2142
2171
|
for (const candidate of response.candidates) {
|
|
2143
|
-
//
|
|
2144
|
-
// See: https://github.com/firebase/firebase-js-sdk/issues/8566
|
|
2172
|
+
// Use 0 if index is undefined (protobuf default value omission).
|
|
2145
2173
|
const i = candidate.index || 0;
|
|
2146
2174
|
if (!aggregatedResponse.candidates) {
|
|
2147
2175
|
aggregatedResponse.candidates = [];
|
|
@@ -2151,7 +2179,7 @@ function aggregateResponses(responses) {
|
|
|
2151
2179
|
index: candidate.index
|
|
2152
2180
|
};
|
|
2153
2181
|
}
|
|
2154
|
-
//
|
|
2182
|
+
// Overwrite with the latest metadata
|
|
2155
2183
|
aggregatedResponse.candidates[i].citationMetadata =
|
|
2156
2184
|
candidate.citationMetadata;
|
|
2157
2185
|
aggregatedResponse.candidates[i].finishReason = candidate.finishReason;
|
|
@@ -2172,12 +2200,7 @@ function aggregateResponses(responses) {
|
|
|
2172
2200
|
aggregatedResponse.candidates[i].urlContextMetadata =
|
|
2173
2201
|
urlContextMetadata;
|
|
2174
2202
|
}
|
|
2175
|
-
/**
|
|
2176
|
-
* Candidates should always have content and parts, but this handles
|
|
2177
|
-
* possible malformed responses.
|
|
2178
|
-
*/
|
|
2179
2203
|
if (candidate.content) {
|
|
2180
|
-
// Skip a candidate without parts.
|
|
2181
2204
|
if (!candidate.content.parts) {
|
|
2182
2205
|
continue;
|
|
2183
2206
|
}
|
|
@@ -2309,7 +2332,7 @@ async function callCloudOrDevice(request, chromeAdapter, onDeviceCall, inCloudCa
|
|
|
2309
2332
|
* See the License for the specific language governing permissions and
|
|
2310
2333
|
* limitations under the License.
|
|
2311
2334
|
*/
|
|
2312
|
-
async function generateContentStreamOnCloud(apiSettings, model, params,
|
|
2335
|
+
async function generateContentStreamOnCloud(apiSettings, model, params, singleRequestOptions) {
|
|
2313
2336
|
if (apiSettings.backend.backendType === BackendType.GOOGLE_AI) {
|
|
2314
2337
|
params = mapGenerateContentRequest(params);
|
|
2315
2338
|
}
|
|
@@ -2318,14 +2341,14 @@ async function generateContentStreamOnCloud(apiSettings, model, params, requestO
|
|
|
2318
2341
|
model,
|
|
2319
2342
|
apiSettings,
|
|
2320
2343
|
stream: true,
|
|
2321
|
-
|
|
2344
|
+
singleRequestOptions
|
|
2322
2345
|
}, JSON.stringify(params));
|
|
2323
2346
|
}
|
|
2324
|
-
async function generateContentStream(apiSettings, model, params, chromeAdapter,
|
|
2325
|
-
const callResult = await callCloudOrDevice(params, chromeAdapter, () => chromeAdapter.generateContentStream(params), () => generateContentStreamOnCloud(apiSettings, model, params,
|
|
2347
|
+
async function generateContentStream(apiSettings, model, params, chromeAdapter, singleRequestOptions) {
|
|
2348
|
+
const callResult = await callCloudOrDevice(params, chromeAdapter, () => chromeAdapter.generateContentStream(params), () => generateContentStreamOnCloud(apiSettings, model, params, singleRequestOptions));
|
|
2326
2349
|
return processStream(callResult.response, apiSettings, callResult.inferenceSource);
|
|
2327
2350
|
}
|
|
2328
|
-
async function generateContentOnCloud(apiSettings, model, params,
|
|
2351
|
+
async function generateContentOnCloud(apiSettings, model, params, singleRequestOptions) {
|
|
2329
2352
|
if (apiSettings.backend.backendType === BackendType.GOOGLE_AI) {
|
|
2330
2353
|
params = mapGenerateContentRequest(params);
|
|
2331
2354
|
}
|
|
@@ -2334,16 +2357,16 @@ async function generateContentOnCloud(apiSettings, model, params, requestOptions
|
|
|
2334
2357
|
task: "generateContent" /* Task.GENERATE_CONTENT */,
|
|
2335
2358
|
apiSettings,
|
|
2336
2359
|
stream: false,
|
|
2337
|
-
|
|
2360
|
+
singleRequestOptions
|
|
2338
2361
|
}, JSON.stringify(params));
|
|
2339
2362
|
}
|
|
2340
|
-
async function templateGenerateContent(apiSettings, templateId, templateParams,
|
|
2363
|
+
async function templateGenerateContent(apiSettings, templateId, templateParams, singleRequestOptions) {
|
|
2341
2364
|
const response = await makeRequest({
|
|
2342
2365
|
task: "templateGenerateContent" /* ServerPromptTemplateTask.TEMPLATE_GENERATE_CONTENT */,
|
|
2343
2366
|
templateId,
|
|
2344
2367
|
apiSettings,
|
|
2345
2368
|
stream: false,
|
|
2346
|
-
|
|
2369
|
+
singleRequestOptions
|
|
2347
2370
|
}, JSON.stringify(templateParams));
|
|
2348
2371
|
const generateContentResponse = await processGenerateContentResponse(response, apiSettings);
|
|
2349
2372
|
const enhancedResponse = createEnhancedContentResponse(generateContentResponse);
|
|
@@ -2351,18 +2374,18 @@ async function templateGenerateContent(apiSettings, templateId, templateParams,
|
|
|
2351
2374
|
response: enhancedResponse
|
|
2352
2375
|
};
|
|
2353
2376
|
}
|
|
2354
|
-
async function templateGenerateContentStream(apiSettings, templateId, templateParams,
|
|
2377
|
+
async function templateGenerateContentStream(apiSettings, templateId, templateParams, singleRequestOptions) {
|
|
2355
2378
|
const response = await makeRequest({
|
|
2356
2379
|
task: "templateStreamGenerateContent" /* ServerPromptTemplateTask.TEMPLATE_STREAM_GENERATE_CONTENT */,
|
|
2357
2380
|
templateId,
|
|
2358
2381
|
apiSettings,
|
|
2359
2382
|
stream: true,
|
|
2360
|
-
|
|
2383
|
+
singleRequestOptions
|
|
2361
2384
|
}, JSON.stringify(templateParams));
|
|
2362
2385
|
return processStream(response, apiSettings);
|
|
2363
2386
|
}
|
|
2364
|
-
async function generateContent(apiSettings, model, params, chromeAdapter,
|
|
2365
|
-
const callResult = await callCloudOrDevice(params, chromeAdapter, () => chromeAdapter.generateContent(params), () => generateContentOnCloud(apiSettings, model, params,
|
|
2387
|
+
async function generateContent(apiSettings, model, params, chromeAdapter, singleRequestOptions) {
|
|
2388
|
+
const callResult = await callCloudOrDevice(params, chromeAdapter, () => chromeAdapter.generateContent(params), () => generateContentOnCloud(apiSettings, model, params, singleRequestOptions));
|
|
2366
2389
|
const generateContentResponse = await processGenerateContentResponse(callResult.response, apiSettings);
|
|
2367
2390
|
const enhancedResponse = createEnhancedContentResponse(generateContentResponse, callResult.inferenceSource);
|
|
2368
2391
|
return {
|
|
@@ -2616,7 +2639,8 @@ function validateChatHistory(history) {
|
|
|
2616
2639
|
* limitations under the License.
|
|
2617
2640
|
*/
|
|
2618
2641
|
/**
|
|
2619
|
-
*
|
|
2642
|
+
* Used to break the internal promise chain when an error is already handled
|
|
2643
|
+
* by the user, preventing duplicate console logs.
|
|
2620
2644
|
*/
|
|
2621
2645
|
const SILENT_ERROR = 'SILENT_ERROR';
|
|
2622
2646
|
/**
|
|
@@ -2632,6 +2656,10 @@ class ChatSession {
|
|
|
2632
2656
|
this.params = params;
|
|
2633
2657
|
this.requestOptions = requestOptions;
|
|
2634
2658
|
this._history = [];
|
|
2659
|
+
/**
|
|
2660
|
+
* Ensures sequential execution of chat messages to maintain history order.
|
|
2661
|
+
* Each call waits for the previous one to settle before proceeding.
|
|
2662
|
+
*/
|
|
2635
2663
|
this._sendPromise = Promise.resolve();
|
|
2636
2664
|
this._apiSettings = apiSettings;
|
|
2637
2665
|
if (params?.history) {
|
|
@@ -2652,7 +2680,7 @@ class ChatSession {
|
|
|
2652
2680
|
* Sends a chat message and receives a non-streaming
|
|
2653
2681
|
* {@link GenerateContentResult}
|
|
2654
2682
|
*/
|
|
2655
|
-
async sendMessage(request) {
|
|
2683
|
+
async sendMessage(request, singleRequestOptions) {
|
|
2656
2684
|
await this._sendPromise;
|
|
2657
2685
|
const newContent = formatNewContent(request);
|
|
2658
2686
|
const generateContentRequest = {
|
|
@@ -2664,16 +2692,20 @@ class ChatSession {
|
|
|
2664
2692
|
contents: [...this._history, newContent]
|
|
2665
2693
|
};
|
|
2666
2694
|
let finalResult = {};
|
|
2667
|
-
// Add onto the chain.
|
|
2668
2695
|
this._sendPromise = this._sendPromise
|
|
2669
|
-
.then(() => generateContent(this._apiSettings, this.model, generateContentRequest, this.chromeAdapter,
|
|
2696
|
+
.then(() => generateContent(this._apiSettings, this.model, generateContentRequest, this.chromeAdapter, {
|
|
2697
|
+
...this.requestOptions,
|
|
2698
|
+
...singleRequestOptions
|
|
2699
|
+
}))
|
|
2670
2700
|
.then(result => {
|
|
2701
|
+
// TODO: Make this update atomic. If creating `responseContent` throws,
|
|
2702
|
+
// history will contain the user message but not the response, causing
|
|
2703
|
+
// validation errors on the next request.
|
|
2671
2704
|
if (result.response.candidates &&
|
|
2672
2705
|
result.response.candidates.length > 0) {
|
|
2673
2706
|
this._history.push(newContent);
|
|
2674
2707
|
const responseContent = {
|
|
2675
2708
|
parts: result.response.candidates?.[0].content.parts || [],
|
|
2676
|
-
// Response seems to come back without a role set.
|
|
2677
2709
|
role: result.response.candidates?.[0].content.role || 'model'
|
|
2678
2710
|
};
|
|
2679
2711
|
this._history.push(responseContent);
|
|
@@ -2694,7 +2726,7 @@ class ChatSession {
|
|
|
2694
2726
|
* {@link GenerateContentStreamResult} containing an iterable stream
|
|
2695
2727
|
* and a response promise.
|
|
2696
2728
|
*/
|
|
2697
|
-
async sendMessageStream(request) {
|
|
2729
|
+
async sendMessageStream(request, singleRequestOptions) {
|
|
2698
2730
|
await this._sendPromise;
|
|
2699
2731
|
const newContent = formatNewContent(request);
|
|
2700
2732
|
const generateContentRequest = {
|
|
@@ -2705,21 +2737,29 @@ class ChatSession {
|
|
|
2705
2737
|
systemInstruction: this.params?.systemInstruction,
|
|
2706
2738
|
contents: [...this._history, newContent]
|
|
2707
2739
|
};
|
|
2708
|
-
const streamPromise = generateContentStream(this._apiSettings, this.model, generateContentRequest, this.chromeAdapter,
|
|
2709
|
-
|
|
2740
|
+
const streamPromise = generateContentStream(this._apiSettings, this.model, generateContentRequest, this.chromeAdapter, {
|
|
2741
|
+
...this.requestOptions,
|
|
2742
|
+
...singleRequestOptions
|
|
2743
|
+
});
|
|
2744
|
+
// We hook into the chain to update history, but we don't block the
|
|
2745
|
+
// return of `streamPromise` to the user.
|
|
2710
2746
|
this._sendPromise = this._sendPromise
|
|
2711
2747
|
.then(() => streamPromise)
|
|
2712
|
-
// This must be handled to avoid unhandled rejection, but jump
|
|
2713
|
-
// to the final catch block with a label to not log this error.
|
|
2714
2748
|
.catch(_ignored => {
|
|
2749
|
+
// If the initial fetch fails, the user's `streamPromise` rejects.
|
|
2750
|
+
// We swallow the error here to prevent double logging in the final catch.
|
|
2715
2751
|
throw new Error(SILENT_ERROR);
|
|
2716
2752
|
})
|
|
2717
2753
|
.then(streamResult => streamResult.response)
|
|
2718
2754
|
.then(response => {
|
|
2755
|
+
// This runs after the stream completes. Runtime errors here cannot be
|
|
2756
|
+
// caught by the user because their promise has likely already resolved.
|
|
2757
|
+
// TODO: Move response validation logic upstream to `stream-reader` so
|
|
2758
|
+
// errors propagate to the user's `result.response` promise.
|
|
2719
2759
|
if (response.candidates && response.candidates.length > 0) {
|
|
2720
2760
|
this._history.push(newContent);
|
|
2761
|
+
// TODO: Validate that `response.candidates[0].content` is not null.
|
|
2721
2762
|
const responseContent = { ...response.candidates[0].content };
|
|
2722
|
-
// Response seems to come back without a role set.
|
|
2723
2763
|
if (!responseContent.role) {
|
|
2724
2764
|
responseContent.role = 'model';
|
|
2725
2765
|
}
|
|
@@ -2733,12 +2773,8 @@ class ChatSession {
|
|
|
2733
2773
|
}
|
|
2734
2774
|
})
|
|
2735
2775
|
.catch(e => {
|
|
2736
|
-
//
|
|
2737
|
-
|
|
2738
|
-
// Avoid duplicating the error message in logs.
|
|
2739
|
-
if (e.message !== SILENT_ERROR) {
|
|
2740
|
-
// Users do not have access to _sendPromise to catch errors
|
|
2741
|
-
// downstream from streamPromise, so they should not throw.
|
|
2776
|
+
// Filter out errors already handled by the user or initiated by them.
|
|
2777
|
+
if (e.message !== SILENT_ERROR && e.name !== 'AbortError') {
|
|
2742
2778
|
logger.error(e);
|
|
2743
2779
|
}
|
|
2744
2780
|
});
|
|
@@ -2762,7 +2798,7 @@ class ChatSession {
|
|
|
2762
2798
|
* See the License for the specific language governing permissions and
|
|
2763
2799
|
* limitations under the License.
|
|
2764
2800
|
*/
|
|
2765
|
-
async function countTokensOnCloud(apiSettings, model, params,
|
|
2801
|
+
async function countTokensOnCloud(apiSettings, model, params, singleRequestOptions) {
|
|
2766
2802
|
let body = '';
|
|
2767
2803
|
if (apiSettings.backend.backendType === BackendType.GOOGLE_AI) {
|
|
2768
2804
|
const mappedParams = mapCountTokensRequest(params, model);
|
|
@@ -2776,7 +2812,7 @@ async function countTokensOnCloud(apiSettings, model, params, requestOptions) {
|
|
|
2776
2812
|
task: "countTokens" /* Task.COUNT_TOKENS */,
|
|
2777
2813
|
apiSettings,
|
|
2778
2814
|
stream: false,
|
|
2779
|
-
|
|
2815
|
+
singleRequestOptions
|
|
2780
2816
|
}, body);
|
|
2781
2817
|
return response.json();
|
|
2782
2818
|
}
|
|
@@ -2812,6 +2848,7 @@ class GenerativeModel extends AIModel {
|
|
|
2812
2848
|
super(ai, modelParams.model);
|
|
2813
2849
|
this.chromeAdapter = chromeAdapter;
|
|
2814
2850
|
this.generationConfig = modelParams.generationConfig || {};
|
|
2851
|
+
validateGenerationConfig(this.generationConfig);
|
|
2815
2852
|
this.safetySettings = modelParams.safetySettings || [];
|
|
2816
2853
|
this.tools = modelParams.tools;
|
|
2817
2854
|
this.toolConfig = modelParams.toolConfig;
|
|
@@ -2822,7 +2859,7 @@ class GenerativeModel extends AIModel {
|
|
|
2822
2859
|
* Makes a single non-streaming call to the model
|
|
2823
2860
|
* and returns an object containing a single {@link GenerateContentResponse}.
|
|
2824
2861
|
*/
|
|
2825
|
-
async generateContent(request) {
|
|
2862
|
+
async generateContent(request, singleRequestOptions) {
|
|
2826
2863
|
const formattedParams = formatGenerateContentInput(request);
|
|
2827
2864
|
return generateContent(this._apiSettings, this.model, {
|
|
2828
2865
|
generationConfig: this.generationConfig,
|
|
@@ -2831,7 +2868,12 @@ class GenerativeModel extends AIModel {
|
|
|
2831
2868
|
toolConfig: this.toolConfig,
|
|
2832
2869
|
systemInstruction: this.systemInstruction,
|
|
2833
2870
|
...formattedParams
|
|
2834
|
-
}, this.chromeAdapter,
|
|
2871
|
+
}, this.chromeAdapter,
|
|
2872
|
+
// Merge request options
|
|
2873
|
+
{
|
|
2874
|
+
...this.requestOptions,
|
|
2875
|
+
...singleRequestOptions
|
|
2876
|
+
});
|
|
2835
2877
|
}
|
|
2836
2878
|
/**
|
|
2837
2879
|
* Makes a single streaming call to the model
|
|
@@ -2839,7 +2881,7 @@ class GenerativeModel extends AIModel {
|
|
|
2839
2881
|
* over all chunks in the streaming response as well as
|
|
2840
2882
|
* a promise that returns the final aggregated response.
|
|
2841
2883
|
*/
|
|
2842
|
-
async generateContentStream(request) {
|
|
2884
|
+
async generateContentStream(request, singleRequestOptions) {
|
|
2843
2885
|
const formattedParams = formatGenerateContentInput(request);
|
|
2844
2886
|
return generateContentStream(this._apiSettings, this.model, {
|
|
2845
2887
|
generationConfig: this.generationConfig,
|
|
@@ -2848,7 +2890,12 @@ class GenerativeModel extends AIModel {
|
|
|
2848
2890
|
toolConfig: this.toolConfig,
|
|
2849
2891
|
systemInstruction: this.systemInstruction,
|
|
2850
2892
|
...formattedParams
|
|
2851
|
-
}, this.chromeAdapter,
|
|
2893
|
+
}, this.chromeAdapter,
|
|
2894
|
+
// Merge request options
|
|
2895
|
+
{
|
|
2896
|
+
...this.requestOptions,
|
|
2897
|
+
...singleRequestOptions
|
|
2898
|
+
});
|
|
2852
2899
|
}
|
|
2853
2900
|
/**
|
|
2854
2901
|
* Gets a new {@link ChatSession} instance which can be used for
|
|
@@ -2872,9 +2919,26 @@ class GenerativeModel extends AIModel {
|
|
|
2872
2919
|
/**
|
|
2873
2920
|
* Counts the tokens in the provided request.
|
|
2874
2921
|
*/
|
|
2875
|
-
async countTokens(request) {
|
|
2922
|
+
async countTokens(request, singleRequestOptions) {
|
|
2876
2923
|
const formattedParams = formatGenerateContentInput(request);
|
|
2877
|
-
return countTokens(this._apiSettings, this.model, formattedParams, this.chromeAdapter
|
|
2924
|
+
return countTokens(this._apiSettings, this.model, formattedParams, this.chromeAdapter,
|
|
2925
|
+
// Merge request options
|
|
2926
|
+
{
|
|
2927
|
+
...this.requestOptions,
|
|
2928
|
+
...singleRequestOptions
|
|
2929
|
+
});
|
|
2930
|
+
}
|
|
2931
|
+
}
|
|
2932
|
+
/**
|
|
2933
|
+
* Client-side validation of some common `GenerationConfig` pitfalls, in order
|
|
2934
|
+
* to save the developer a wasted request.
|
|
2935
|
+
*/
|
|
2936
|
+
function validateGenerationConfig(generationConfig) {
|
|
2937
|
+
if (
|
|
2938
|
+
// != allows for null and undefined. 0 is considered "set" by the model
|
|
2939
|
+
generationConfig.thinkingConfig?.thinkingBudget != null &&
|
|
2940
|
+
generationConfig.thinkingConfig?.thinkingLevel) {
|
|
2941
|
+
throw new AIError(AIErrorCode.UNSUPPORTED, `Cannot set both thinkingBudget and thinkingLevel in a config.`);
|
|
2878
2942
|
}
|
|
2879
2943
|
}
|
|
2880
2944
|
|
|
@@ -3325,7 +3389,7 @@ class ImagenModel extends AIModel {
|
|
|
3325
3389
|
*
|
|
3326
3390
|
* @public
|
|
3327
3391
|
*/
|
|
3328
|
-
async generateImages(prompt) {
|
|
3392
|
+
async generateImages(prompt, singleRequestOptions) {
|
|
3329
3393
|
const body = createPredictRequestBody(prompt, {
|
|
3330
3394
|
...this.generationConfig,
|
|
3331
3395
|
...this.safetySettings
|
|
@@ -3335,7 +3399,11 @@ class ImagenModel extends AIModel {
|
|
|
3335
3399
|
model: this.model,
|
|
3336
3400
|
apiSettings: this._apiSettings,
|
|
3337
3401
|
stream: false,
|
|
3338
|
-
|
|
3402
|
+
// Merge request options. Single request options overwrite the model's request options.
|
|
3403
|
+
singleRequestOptions: {
|
|
3404
|
+
...this.requestOptions,
|
|
3405
|
+
...singleRequestOptions
|
|
3406
|
+
}
|
|
3339
3407
|
}, JSON.stringify(body));
|
|
3340
3408
|
return handlePredictResponse(response);
|
|
3341
3409
|
}
|
|
@@ -3358,7 +3426,7 @@ class ImagenModel extends AIModel {
|
|
|
3358
3426
|
* returned object will have a `filteredReason` property.
|
|
3359
3427
|
* If all images are filtered, the `images` array will be empty.
|
|
3360
3428
|
*/
|
|
3361
|
-
async generateImagesGCS(prompt, gcsURI) {
|
|
3429
|
+
async generateImagesGCS(prompt, gcsURI, singleRequestOptions) {
|
|
3362
3430
|
const body = createPredictRequestBody(prompt, {
|
|
3363
3431
|
gcsURI,
|
|
3364
3432
|
...this.generationConfig,
|
|
@@ -3369,7 +3437,11 @@ class ImagenModel extends AIModel {
|
|
|
3369
3437
|
model: this.model,
|
|
3370
3438
|
apiSettings: this._apiSettings,
|
|
3371
3439
|
stream: false,
|
|
3372
|
-
|
|
3440
|
+
// Merge request options. Single request options overwrite the model's request options.
|
|
3441
|
+
singleRequestOptions: {
|
|
3442
|
+
...this.requestOptions,
|
|
3443
|
+
...singleRequestOptions
|
|
3444
|
+
}
|
|
3373
3445
|
}, JSON.stringify(body));
|
|
3374
3446
|
return handlePredictResponse(response);
|
|
3375
3447
|
}
|
|
@@ -3563,9 +3635,11 @@ class TemplateGenerativeModel {
|
|
|
3563
3635
|
*
|
|
3564
3636
|
* @beta
|
|
3565
3637
|
*/
|
|
3566
|
-
async generateContent(templateId, templateVariables
|
|
3567
|
-
|
|
3568
|
-
|
|
3638
|
+
async generateContent(templateId, templateVariables, singleRequestOptions) {
|
|
3639
|
+
return templateGenerateContent(this._apiSettings, templateId, { inputs: templateVariables }, {
|
|
3640
|
+
...this.requestOptions,
|
|
3641
|
+
...singleRequestOptions
|
|
3642
|
+
});
|
|
3569
3643
|
}
|
|
3570
3644
|
/**
|
|
3571
3645
|
* Makes a single streaming call to the model and returns an object
|
|
@@ -3579,8 +3653,11 @@ class TemplateGenerativeModel {
|
|
|
3579
3653
|
*
|
|
3580
3654
|
* @beta
|
|
3581
3655
|
*/
|
|
3582
|
-
async generateContentStream(templateId, templateVariables) {
|
|
3583
|
-
return templateGenerateContentStream(this._apiSettings, templateId, { inputs: templateVariables },
|
|
3656
|
+
async generateContentStream(templateId, templateVariables, singleRequestOptions) {
|
|
3657
|
+
return templateGenerateContentStream(this._apiSettings, templateId, { inputs: templateVariables }, {
|
|
3658
|
+
...this.requestOptions,
|
|
3659
|
+
...singleRequestOptions
|
|
3660
|
+
});
|
|
3584
3661
|
}
|
|
3585
3662
|
}
|
|
3586
3663
|
|
|
@@ -3625,13 +3702,16 @@ class TemplateImagenModel {
|
|
|
3625
3702
|
*
|
|
3626
3703
|
* @beta
|
|
3627
3704
|
*/
|
|
3628
|
-
async generateImages(templateId, templateVariables) {
|
|
3705
|
+
async generateImages(templateId, templateVariables, singleRequestOptions) {
|
|
3629
3706
|
const response = await makeRequest({
|
|
3630
3707
|
task: "templatePredict" /* ServerPromptTemplateTask.TEMPLATE_PREDICT */,
|
|
3631
3708
|
templateId,
|
|
3632
3709
|
apiSettings: this._apiSettings,
|
|
3633
3710
|
stream: false,
|
|
3634
|
-
|
|
3711
|
+
singleRequestOptions: {
|
|
3712
|
+
...this.requestOptions,
|
|
3713
|
+
...singleRequestOptions
|
|
3714
|
+
}
|
|
3635
3715
|
}, JSON.stringify({ inputs: templateVariables }));
|
|
3636
3716
|
return handlePredictResponse(response);
|
|
3637
3717
|
}
|
|
@@ -4508,6 +4588,7 @@ exports.SchemaType = SchemaType;
|
|
|
4508
4588
|
exports.StringSchema = StringSchema;
|
|
4509
4589
|
exports.TemplateGenerativeModel = TemplateGenerativeModel;
|
|
4510
4590
|
exports.TemplateImagenModel = TemplateImagenModel;
|
|
4591
|
+
exports.ThinkingLevel = ThinkingLevel;
|
|
4511
4592
|
exports.URLRetrievalStatus = URLRetrievalStatus;
|
|
4512
4593
|
exports.VertexAIBackend = VertexAIBackend;
|
|
4513
4594
|
exports.getAI = getAI;
|