@firebase/ai 2.11.1 → 2.12.0-20260505131936
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 +358 -9
- package/dist/ai.d.ts +386 -12
- package/dist/esm/index.esm.js +375 -223
- package/dist/esm/index.esm.js.map +1 -1
- package/dist/esm/src/methods/live-session.d.ts +40 -5
- package/dist/esm/src/models/live-generative-model.d.ts +6 -4
- package/dist/esm/src/models/template-generative-model.d.ts +7 -3
- package/dist/esm/src/types/enums.d.ts +80 -0
- package/dist/esm/src/types/live-responses.d.ts +3 -1
- package/dist/esm/src/types/requests.d.ts +148 -2
- package/dist/esm/src/types/responses.d.ts +69 -2
- package/dist/index.cjs.js +376 -222
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.node.cjs.js +376 -222
- package/dist/index.node.cjs.js.map +1 -1
- package/dist/index.node.mjs +375 -223
- package/dist/index.node.mjs.map +1 -1
- package/dist/src/methods/live-session.d.ts +40 -5
- package/dist/src/models/live-generative-model.d.ts +6 -4
- package/dist/src/models/template-generative-model.d.ts +7 -3
- package/dist/src/types/enums.d.ts +80 -0
- package/dist/src/types/live-responses.d.ts +3 -1
- package/dist/src/types/requests.d.ts +148 -2
- package/dist/src/types/responses.d.ts +69 -2
- package/package.json +9 -9
package/dist/index.node.mjs
CHANGED
|
@@ -4,7 +4,7 @@ import { FirebaseError, Deferred, getModularInstance } from '@firebase/util';
|
|
|
4
4
|
import { Logger } from '@firebase/logger';
|
|
5
5
|
|
|
6
6
|
var name = "@firebase/ai";
|
|
7
|
-
var version = "2.
|
|
7
|
+
var version = "2.12.0-20260505131936";
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* @license
|
|
@@ -277,7 +277,78 @@ const FinishReason = {
|
|
|
277
277
|
/**
|
|
278
278
|
* The function call generated by the model was invalid.
|
|
279
279
|
*/
|
|
280
|
-
MALFORMED_FUNCTION_CALL: 'MALFORMED_FUNCTION_CALL'
|
|
280
|
+
MALFORMED_FUNCTION_CALL: 'MALFORMED_FUNCTION_CALL',
|
|
281
|
+
/**
|
|
282
|
+
* Token generation stopped because generated images contain safety violations.
|
|
283
|
+
*/
|
|
284
|
+
IMAGE_SAFETY: 'IMAGE_SAFETY',
|
|
285
|
+
/**
|
|
286
|
+
* Image generation stopped because generated images have other prohibited content.
|
|
287
|
+
*/
|
|
288
|
+
IMAGE_PROHIBITED_CONTENT: 'IMAGE_PROHIBITED_CONTENT',
|
|
289
|
+
/**
|
|
290
|
+
* Image generation stopped because of other miscellaneous issue.
|
|
291
|
+
*/
|
|
292
|
+
IMAGE_OTHER: 'IMAGE_OTHER',
|
|
293
|
+
/**
|
|
294
|
+
* The model was expected to generate an image, but none was generated.
|
|
295
|
+
*/
|
|
296
|
+
NO_IMAGE: 'NO_IMAGE',
|
|
297
|
+
/**
|
|
298
|
+
* Image generation stopped due to recitation.
|
|
299
|
+
*/
|
|
300
|
+
IMAGE_RECITATION: 'IMAGE_RECITATION',
|
|
301
|
+
/**
|
|
302
|
+
* The response candidate content was flagged for using an unsupported language.
|
|
303
|
+
*/
|
|
304
|
+
LANGUAGE: 'LANGUAGE',
|
|
305
|
+
/**
|
|
306
|
+
* Model generated a tool call but no tools were enabled in the request.
|
|
307
|
+
*/
|
|
308
|
+
UNEXPECTED_TOOL_CALL: 'UNEXPECTED_TOOL_CALL',
|
|
309
|
+
/**
|
|
310
|
+
* Model called too many tools consecutively, thus the system exited execution.
|
|
311
|
+
*/
|
|
312
|
+
TOO_MANY_TOOL_CALLS: 'TOO_MANY_TOOL_CALLS',
|
|
313
|
+
/**
|
|
314
|
+
* Request has at least one thought signature missing.
|
|
315
|
+
*/
|
|
316
|
+
MISSING_THOUGHT_SIGNATURE: 'MISSING_THOUGHT_SIGNATURE',
|
|
317
|
+
/**
|
|
318
|
+
* Finished due to malformed response.
|
|
319
|
+
*/
|
|
320
|
+
MALFORMED_RESPONSE: 'MALFORMED_RESPONSE'
|
|
321
|
+
};
|
|
322
|
+
/**
|
|
323
|
+
* Aspect ratios for generated images.
|
|
324
|
+
* @public
|
|
325
|
+
*/
|
|
326
|
+
/* eslint-disable camelcase */
|
|
327
|
+
const ImageConfigAspectRatio = {
|
|
328
|
+
SQUARE_1x1: '1:1',
|
|
329
|
+
PORTRAIT_9x16: '9:16',
|
|
330
|
+
LANDSCAPE_16x9: '16:9',
|
|
331
|
+
PORTRAIT_3x4: '3:4',
|
|
332
|
+
LANDSCAPE_4x3: '4:3',
|
|
333
|
+
PORTRAIT_2x3: '2:3',
|
|
334
|
+
LANDSCAPE_3x2: '3:2',
|
|
335
|
+
PORTRAIT_4x5: '4:5',
|
|
336
|
+
LANDSCAPE_5x4: '5:4',
|
|
337
|
+
PORTRAIT_1x4: '1:4',
|
|
338
|
+
LANDSCAPE_4x1: '4:1',
|
|
339
|
+
PORTRAIT_1x8: '1:8',
|
|
340
|
+
LANDSCAPE_8x1: '8:1',
|
|
341
|
+
ULTRAWIDE_21x9: '21:9'
|
|
342
|
+
};
|
|
343
|
+
/**
|
|
344
|
+
* Sizes for generated images. For example, '1K' is 1024px, '2K' is 2048px, and '4K' is 4096px.
|
|
345
|
+
* @public
|
|
346
|
+
*/
|
|
347
|
+
const ImageConfigImageSize = {
|
|
348
|
+
SIZE_512: '512',
|
|
349
|
+
SIZE_1K: '1K',
|
|
350
|
+
SIZE_2K: '2K',
|
|
351
|
+
SIZE_4K: '4K'
|
|
281
352
|
};
|
|
282
353
|
/**
|
|
283
354
|
* @public
|
|
@@ -487,7 +558,8 @@ const LiveResponseType = {
|
|
|
487
558
|
SERVER_CONTENT: 'serverContent',
|
|
488
559
|
TOOL_CALL: 'toolCall',
|
|
489
560
|
TOOL_CALL_CANCELLATION: 'toolCallCancellation',
|
|
490
|
-
GOING_AWAY_NOTICE: 'goingAwayNotice'
|
|
561
|
+
GOING_AWAY_NOTICE: 'goingAwayNotice',
|
|
562
|
+
SESSION_RESUMPTION_UPDATE: 'sessionResumptionUpdate'
|
|
491
563
|
};
|
|
492
564
|
|
|
493
565
|
/**
|
|
@@ -1522,7 +1594,24 @@ function getInlineDataParts(response) {
|
|
|
1522
1594
|
return undefined;
|
|
1523
1595
|
}
|
|
1524
1596
|
}
|
|
1525
|
-
const badFinishReasons = [
|
|
1597
|
+
const badFinishReasons = [
|
|
1598
|
+
FinishReason.RECITATION,
|
|
1599
|
+
FinishReason.SAFETY,
|
|
1600
|
+
FinishReason.BLOCKLIST,
|
|
1601
|
+
FinishReason.PROHIBITED_CONTENT,
|
|
1602
|
+
FinishReason.SPII,
|
|
1603
|
+
FinishReason.MALFORMED_FUNCTION_CALL,
|
|
1604
|
+
FinishReason.IMAGE_SAFETY,
|
|
1605
|
+
FinishReason.IMAGE_PROHIBITED_CONTENT,
|
|
1606
|
+
FinishReason.IMAGE_OTHER,
|
|
1607
|
+
FinishReason.NO_IMAGE,
|
|
1608
|
+
FinishReason.IMAGE_RECITATION,
|
|
1609
|
+
FinishReason.LANGUAGE,
|
|
1610
|
+
FinishReason.UNEXPECTED_TOOL_CALL,
|
|
1611
|
+
FinishReason.TOO_MANY_TOOL_CALLS,
|
|
1612
|
+
FinishReason.MISSING_THOUGHT_SIGNATURE,
|
|
1613
|
+
FinishReason.MALFORMED_RESPONSE
|
|
1614
|
+
];
|
|
1526
1615
|
function hadBadFinishReason(candidate) {
|
|
1527
1616
|
return (!!candidate.finishReason &&
|
|
1528
1617
|
badFinishReasons.some(reason => reason === candidate.finishReason));
|
|
@@ -2924,6 +3013,153 @@ function validateGenerationConfig(generationConfig) {
|
|
|
2924
3013
|
}
|
|
2925
3014
|
}
|
|
2926
3015
|
|
|
3016
|
+
/**
|
|
3017
|
+
* @license
|
|
3018
|
+
* Copyright 2025 Google LLC
|
|
3019
|
+
*
|
|
3020
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
3021
|
+
* you may not use this file except in compliance with the License.
|
|
3022
|
+
* You may obtain a copy of the License at
|
|
3023
|
+
*
|
|
3024
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
3025
|
+
*
|
|
3026
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
3027
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
3028
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
3029
|
+
* See the License for the specific language governing permissions and
|
|
3030
|
+
* limitations under the License.
|
|
3031
|
+
*/
|
|
3032
|
+
/**
|
|
3033
|
+
* A wrapper for the native `WebSocket` available in both Browsers and Node >= 22.
|
|
3034
|
+
*
|
|
3035
|
+
* @internal
|
|
3036
|
+
*/
|
|
3037
|
+
class WebSocketHandlerImpl {
|
|
3038
|
+
constructor() {
|
|
3039
|
+
if (typeof WebSocket === 'undefined') {
|
|
3040
|
+
throw new AIError(AIErrorCode.UNSUPPORTED, 'The WebSocket API is not available in this environment. ' +
|
|
3041
|
+
'The "Live" feature is not supported here. It is supported in ' +
|
|
3042
|
+
'modern browser windows, Web Workers with WebSocket support, and Node >= 22.');
|
|
3043
|
+
}
|
|
3044
|
+
}
|
|
3045
|
+
connect(url) {
|
|
3046
|
+
return new Promise((resolve, reject) => {
|
|
3047
|
+
this.ws = new WebSocket(url);
|
|
3048
|
+
this.ws.binaryType = 'blob'; // Only important to set in Node
|
|
3049
|
+
this.ws.addEventListener('open', () => resolve(), { once: true });
|
|
3050
|
+
this.ws.addEventListener('error', () => reject(new AIError(AIErrorCode.FETCH_ERROR, `Error event raised on WebSocket`)), { once: true });
|
|
3051
|
+
this.ws.addEventListener('close', (closeEvent) => {
|
|
3052
|
+
if (closeEvent.reason) {
|
|
3053
|
+
logger.warn(`WebSocket connection closed by server. Reason: '${closeEvent.reason}'`);
|
|
3054
|
+
}
|
|
3055
|
+
});
|
|
3056
|
+
});
|
|
3057
|
+
}
|
|
3058
|
+
send(data) {
|
|
3059
|
+
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
|
|
3060
|
+
throw new AIError(AIErrorCode.REQUEST_ERROR, 'WebSocket is not open.');
|
|
3061
|
+
}
|
|
3062
|
+
this.ws.send(data);
|
|
3063
|
+
}
|
|
3064
|
+
async *listen() {
|
|
3065
|
+
if (!this.ws) {
|
|
3066
|
+
throw new AIError(AIErrorCode.REQUEST_ERROR, 'WebSocket is not connected.');
|
|
3067
|
+
}
|
|
3068
|
+
const messageQueue = [];
|
|
3069
|
+
const errorQueue = [];
|
|
3070
|
+
let resolvePromise = null;
|
|
3071
|
+
let isClosed = false;
|
|
3072
|
+
const messageListener = async (event) => {
|
|
3073
|
+
let data;
|
|
3074
|
+
if (event.data instanceof Blob) {
|
|
3075
|
+
data = await event.data.text();
|
|
3076
|
+
}
|
|
3077
|
+
else if (typeof event.data === 'string') {
|
|
3078
|
+
data = event.data;
|
|
3079
|
+
}
|
|
3080
|
+
else {
|
|
3081
|
+
errorQueue.push(new AIError(AIErrorCode.PARSE_FAILED, `Failed to parse WebSocket response. Expected data to be a Blob or string, but was ${typeof event.data}.`));
|
|
3082
|
+
if (resolvePromise) {
|
|
3083
|
+
resolvePromise();
|
|
3084
|
+
resolvePromise = null;
|
|
3085
|
+
}
|
|
3086
|
+
return;
|
|
3087
|
+
}
|
|
3088
|
+
try {
|
|
3089
|
+
const obj = JSON.parse(data);
|
|
3090
|
+
messageQueue.push(obj);
|
|
3091
|
+
}
|
|
3092
|
+
catch (e) {
|
|
3093
|
+
const err = e;
|
|
3094
|
+
errorQueue.push(new AIError(AIErrorCode.PARSE_FAILED, `Error parsing WebSocket message to JSON: ${err.message}`));
|
|
3095
|
+
}
|
|
3096
|
+
if (resolvePromise) {
|
|
3097
|
+
resolvePromise();
|
|
3098
|
+
resolvePromise = null;
|
|
3099
|
+
}
|
|
3100
|
+
};
|
|
3101
|
+
const errorListener = () => {
|
|
3102
|
+
errorQueue.push(new AIError(AIErrorCode.FETCH_ERROR, 'WebSocket connection error.'));
|
|
3103
|
+
if (resolvePromise) {
|
|
3104
|
+
resolvePromise();
|
|
3105
|
+
resolvePromise = null;
|
|
3106
|
+
}
|
|
3107
|
+
};
|
|
3108
|
+
const closeListener = (event) => {
|
|
3109
|
+
if (event.reason) {
|
|
3110
|
+
logger.warn(`WebSocket connection closed by the server with reason: ${event.reason}`);
|
|
3111
|
+
}
|
|
3112
|
+
isClosed = true;
|
|
3113
|
+
if (resolvePromise) {
|
|
3114
|
+
resolvePromise();
|
|
3115
|
+
resolvePromise = null;
|
|
3116
|
+
}
|
|
3117
|
+
// Clean up listeners to prevent memory leaks
|
|
3118
|
+
this.ws?.removeEventListener('message', messageListener);
|
|
3119
|
+
this.ws?.removeEventListener('close', closeListener);
|
|
3120
|
+
this.ws?.removeEventListener('error', errorListener);
|
|
3121
|
+
};
|
|
3122
|
+
this.ws.addEventListener('message', messageListener);
|
|
3123
|
+
this.ws.addEventListener('close', closeListener);
|
|
3124
|
+
this.ws.addEventListener('error', errorListener);
|
|
3125
|
+
while (!isClosed) {
|
|
3126
|
+
if (errorQueue.length > 0) {
|
|
3127
|
+
const error = errorQueue.shift();
|
|
3128
|
+
throw error;
|
|
3129
|
+
}
|
|
3130
|
+
if (messageQueue.length > 0) {
|
|
3131
|
+
yield messageQueue.shift();
|
|
3132
|
+
}
|
|
3133
|
+
else {
|
|
3134
|
+
await new Promise(resolve => {
|
|
3135
|
+
resolvePromise = resolve;
|
|
3136
|
+
});
|
|
3137
|
+
}
|
|
3138
|
+
}
|
|
3139
|
+
// If the loop terminated because isClosed is true, check for any final errors
|
|
3140
|
+
if (errorQueue.length > 0) {
|
|
3141
|
+
const error = errorQueue.shift();
|
|
3142
|
+
throw error;
|
|
3143
|
+
}
|
|
3144
|
+
}
|
|
3145
|
+
close(code, reason) {
|
|
3146
|
+
return new Promise(resolve => {
|
|
3147
|
+
if (!this.ws) {
|
|
3148
|
+
return resolve();
|
|
3149
|
+
}
|
|
3150
|
+
this.ws.addEventListener('close', () => resolve(), { once: true });
|
|
3151
|
+
// Calling 'close' during these states results in an error.
|
|
3152
|
+
if (this.ws.readyState === WebSocket.CLOSED ||
|
|
3153
|
+
this.ws.readyState === WebSocket.CONNECTING) {
|
|
3154
|
+
return resolve();
|
|
3155
|
+
}
|
|
3156
|
+
if (this.ws.readyState !== WebSocket.CLOSING) {
|
|
3157
|
+
this.ws.close(code, reason);
|
|
3158
|
+
}
|
|
3159
|
+
});
|
|
3160
|
+
}
|
|
3161
|
+
}
|
|
3162
|
+
|
|
2927
3163
|
/**
|
|
2928
3164
|
* @license
|
|
2929
3165
|
* Copyright 2025 Google LLC
|
|
@@ -2951,9 +3187,10 @@ class LiveSession {
|
|
|
2951
3187
|
/**
|
|
2952
3188
|
* @internal
|
|
2953
3189
|
*/
|
|
2954
|
-
constructor(
|
|
2955
|
-
this.
|
|
2956
|
-
this.
|
|
3190
|
+
constructor(_setupMessage, _apiSettings, _sessionResumption, webSocketHandler) {
|
|
3191
|
+
this._setupMessage = _setupMessage;
|
|
3192
|
+
this._apiSettings = _apiSettings;
|
|
3193
|
+
this._sessionResumption = _sessionResumption;
|
|
2957
3194
|
/**
|
|
2958
3195
|
* Indicates whether this Live session is closed.
|
|
2959
3196
|
*
|
|
@@ -2966,6 +3203,64 @@ class LiveSession {
|
|
|
2966
3203
|
* @beta
|
|
2967
3204
|
*/
|
|
2968
3205
|
this.inConversation = false;
|
|
3206
|
+
/**
|
|
3207
|
+
* Generator yielding WebSocket messages from the server.
|
|
3208
|
+
*/
|
|
3209
|
+
this._serverMessages = null;
|
|
3210
|
+
this._webSocketHandler = webSocketHandler || new WebSocketHandlerImpl();
|
|
3211
|
+
this.connectionPromise = this._connectSession(this._sessionResumption);
|
|
3212
|
+
}
|
|
3213
|
+
/**
|
|
3214
|
+
* Initializes connection to the WebSocket. Should be called immediately
|
|
3215
|
+
* after instantiation.
|
|
3216
|
+
*
|
|
3217
|
+
* @internal
|
|
3218
|
+
*/
|
|
3219
|
+
async _connectSession(sessionResumption) {
|
|
3220
|
+
const url = new WebSocketUrl(this._apiSettings);
|
|
3221
|
+
await this._webSocketHandler.connect(url.toString());
|
|
3222
|
+
try {
|
|
3223
|
+
// Begin listening for server messages, and begin the handshake by sending the 'setupMessage'
|
|
3224
|
+
this._serverMessages = this._webSocketHandler.listen();
|
|
3225
|
+
const setupMessage = { ...this._setupMessage };
|
|
3226
|
+
if (sessionResumption) {
|
|
3227
|
+
setupMessage.setup.sessionResumption = sessionResumption;
|
|
3228
|
+
}
|
|
3229
|
+
this._webSocketHandler.send(JSON.stringify(setupMessage));
|
|
3230
|
+
// Verify we received the handshake response 'setupComplete'
|
|
3231
|
+
const firstMessage = (await this._serverMessages.next()).value;
|
|
3232
|
+
if (!firstMessage ||
|
|
3233
|
+
!(typeof firstMessage === 'object') ||
|
|
3234
|
+
!('setupComplete' in firstMessage)) {
|
|
3235
|
+
await this._webSocketHandler.close(1011, 'Handshake failure');
|
|
3236
|
+
throw new AIError(AIErrorCode.RESPONSE_ERROR, 'Server connection handshake failed. The server did not respond with a setupComplete message.');
|
|
3237
|
+
}
|
|
3238
|
+
this.isClosed = false;
|
|
3239
|
+
}
|
|
3240
|
+
catch (e) {
|
|
3241
|
+
// Ensure connection is closed on any setup error
|
|
3242
|
+
await this._webSocketHandler.close();
|
|
3243
|
+
throw e;
|
|
3244
|
+
}
|
|
3245
|
+
}
|
|
3246
|
+
/**
|
|
3247
|
+
* Resumes an existing live session with the server.
|
|
3248
|
+
*
|
|
3249
|
+
* This closes the current WebSocket connection and establishes a new one using
|
|
3250
|
+
* the same configuration (URI, headers, model, system instruction, tools, etc.)
|
|
3251
|
+
* as the original session.
|
|
3252
|
+
*
|
|
3253
|
+
* @param sessionResumption - The configuration for session resumption, such as the handle to the previous session state to restore.
|
|
3254
|
+
* @throws If the session resumption configuration is unsupported.
|
|
3255
|
+
*
|
|
3256
|
+
* @beta
|
|
3257
|
+
*/
|
|
3258
|
+
async resumeSession(sessionResumption) {
|
|
3259
|
+
if (!this._sessionResumption) {
|
|
3260
|
+
throw new AIError(AIErrorCode.UNSUPPORTED, 'Cannot resume session: no sessionResumption config provided');
|
|
3261
|
+
}
|
|
3262
|
+
await this.close();
|
|
3263
|
+
await this._connectSession(sessionResumption);
|
|
2969
3264
|
}
|
|
2970
3265
|
/**
|
|
2971
3266
|
* Sends content to the server.
|
|
@@ -2987,7 +3282,7 @@ class LiveSession {
|
|
|
2987
3282
|
turnComplete
|
|
2988
3283
|
}
|
|
2989
3284
|
};
|
|
2990
|
-
this.
|
|
3285
|
+
this._webSocketHandler.send(JSON.stringify(message));
|
|
2991
3286
|
}
|
|
2992
3287
|
/**
|
|
2993
3288
|
* Sends text to the server in realtime.
|
|
@@ -3011,7 +3306,7 @@ class LiveSession {
|
|
|
3011
3306
|
text
|
|
3012
3307
|
}
|
|
3013
3308
|
};
|
|
3014
|
-
this.
|
|
3309
|
+
this._webSocketHandler.send(JSON.stringify(message));
|
|
3015
3310
|
}
|
|
3016
3311
|
/**
|
|
3017
3312
|
* Sends audio data to the server in realtime.
|
|
@@ -3040,7 +3335,7 @@ class LiveSession {
|
|
|
3040
3335
|
audio: blob
|
|
3041
3336
|
}
|
|
3042
3337
|
};
|
|
3043
|
-
this.
|
|
3338
|
+
this._webSocketHandler.send(JSON.stringify(message));
|
|
3044
3339
|
}
|
|
3045
3340
|
/**
|
|
3046
3341
|
* Sends video data to the server in realtime.
|
|
@@ -3068,7 +3363,7 @@ class LiveSession {
|
|
|
3068
3363
|
video: blob
|
|
3069
3364
|
}
|
|
3070
3365
|
};
|
|
3071
|
-
this.
|
|
3366
|
+
this._webSocketHandler.send(JSON.stringify(message));
|
|
3072
3367
|
}
|
|
3073
3368
|
/**
|
|
3074
3369
|
* Sends function responses to the server.
|
|
@@ -3087,7 +3382,7 @@ class LiveSession {
|
|
|
3087
3382
|
functionResponses
|
|
3088
3383
|
}
|
|
3089
3384
|
};
|
|
3090
|
-
this.
|
|
3385
|
+
this._webSocketHandler.send(JSON.stringify(message));
|
|
3091
3386
|
}
|
|
3092
3387
|
/**
|
|
3093
3388
|
* Yields messages received from the server.
|
|
@@ -3102,42 +3397,50 @@ class LiveSession {
|
|
|
3102
3397
|
if (this.isClosed) {
|
|
3103
3398
|
throw new AIError(AIErrorCode.SESSION_CLOSED, 'Cannot read from a Live session that is closed. Try starting a new Live session.');
|
|
3104
3399
|
}
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
if (
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3400
|
+
if (this._serverMessages) {
|
|
3401
|
+
for await (const message of this._serverMessages) {
|
|
3402
|
+
if (message && typeof message === 'object') {
|
|
3403
|
+
if (LiveResponseType.SERVER_CONTENT in message) {
|
|
3404
|
+
yield {
|
|
3405
|
+
type: 'serverContent',
|
|
3406
|
+
...message
|
|
3407
|
+
.serverContent
|
|
3408
|
+
};
|
|
3409
|
+
}
|
|
3410
|
+
else if (LiveResponseType.TOOL_CALL in message) {
|
|
3411
|
+
yield {
|
|
3412
|
+
type: 'toolCall',
|
|
3413
|
+
...message
|
|
3414
|
+
.toolCall
|
|
3415
|
+
};
|
|
3416
|
+
}
|
|
3417
|
+
else if (LiveResponseType.TOOL_CALL_CANCELLATION in message) {
|
|
3418
|
+
yield {
|
|
3419
|
+
type: 'toolCallCancellation',
|
|
3420
|
+
...message.toolCallCancellation
|
|
3421
|
+
};
|
|
3422
|
+
}
|
|
3423
|
+
else if ('goAway' in message) {
|
|
3424
|
+
const notice = message.goAway;
|
|
3425
|
+
yield {
|
|
3426
|
+
type: LiveResponseType.GOING_AWAY_NOTICE,
|
|
3427
|
+
timeLeft: parseDuration(notice.timeLeft)
|
|
3428
|
+
};
|
|
3429
|
+
}
|
|
3430
|
+
else if (LiveResponseType.SESSION_RESUMPTION_UPDATE in message) {
|
|
3431
|
+
yield {
|
|
3432
|
+
type: LiveResponseType.SESSION_RESUMPTION_UPDATE,
|
|
3433
|
+
...message.sessionResumptionUpdate
|
|
3434
|
+
};
|
|
3435
|
+
}
|
|
3436
|
+
else {
|
|
3437
|
+
logger.warn(`Received an unknown message type from the server: ${JSON.stringify(message)}`);
|
|
3438
|
+
}
|
|
3133
3439
|
}
|
|
3134
3440
|
else {
|
|
3135
|
-
logger.warn(`Received an
|
|
3441
|
+
logger.warn(`Received an invalid message from the server: ${JSON.stringify(message)}`);
|
|
3136
3442
|
}
|
|
3137
3443
|
}
|
|
3138
|
-
else {
|
|
3139
|
-
logger.warn(`Received an invalid message from the server: ${JSON.stringify(message)}`);
|
|
3140
|
-
}
|
|
3141
3444
|
}
|
|
3142
3445
|
}
|
|
3143
3446
|
/**
|
|
@@ -3149,7 +3452,7 @@ class LiveSession {
|
|
|
3149
3452
|
async close() {
|
|
3150
3453
|
if (!this.isClosed) {
|
|
3151
3454
|
this.isClosed = true;
|
|
3152
|
-
await this.
|
|
3455
|
+
await this._webSocketHandler.close(1000, 'Client closed session.');
|
|
3153
3456
|
}
|
|
3154
3457
|
}
|
|
3155
3458
|
/**
|
|
@@ -3172,7 +3475,7 @@ class LiveSession {
|
|
|
3172
3475
|
const message = {
|
|
3173
3476
|
realtimeInput: { mediaChunks: [mediaChunk] }
|
|
3174
3477
|
};
|
|
3175
|
-
this.
|
|
3478
|
+
this._webSocketHandler.send(JSON.stringify(message));
|
|
3176
3479
|
});
|
|
3177
3480
|
}
|
|
3178
3481
|
/**
|
|
@@ -3252,6 +3555,7 @@ class LiveGenerativeModel extends AIModel {
|
|
|
3252
3555
|
*/
|
|
3253
3556
|
constructor(ai, modelParams,
|
|
3254
3557
|
/**
|
|
3558
|
+
* For testing injection
|
|
3255
3559
|
* @internal
|
|
3256
3560
|
*/
|
|
3257
3561
|
_webSocketHandler) {
|
|
@@ -3270,9 +3574,7 @@ class LiveGenerativeModel extends AIModel {
|
|
|
3270
3574
|
*
|
|
3271
3575
|
* @beta
|
|
3272
3576
|
*/
|
|
3273
|
-
async connect() {
|
|
3274
|
-
const url = new WebSocketUrl(this._apiSettings);
|
|
3275
|
-
await this._webSocketHandler.connect(url.toString());
|
|
3577
|
+
async connect(sessionResumption) {
|
|
3276
3578
|
let fullModelPath;
|
|
3277
3579
|
if (this._apiSettings.backend.backendType === BackendType.GOOGLE_AI) {
|
|
3278
3580
|
fullModelPath = `projects/${this._apiSettings.project}/${this.model}`;
|
|
@@ -3283,36 +3585,24 @@ class LiveGenerativeModel extends AIModel {
|
|
|
3283
3585
|
// inputAudioTranscription and outputAudioTranscription are on the generation config in the public API,
|
|
3284
3586
|
// but the backend expects them to be in the `setup` message.
|
|
3285
3587
|
const { inputAudioTranscription, outputAudioTranscription, ...generationConfig } = this.generationConfig;
|
|
3588
|
+
const contextWindowCompression = generationConfig.contextWindowCompression;
|
|
3589
|
+
delete generationConfig.contextWindowCompression;
|
|
3286
3590
|
const setupMessage = {
|
|
3287
3591
|
setup: {
|
|
3288
3592
|
model: fullModelPath,
|
|
3289
3593
|
generationConfig,
|
|
3594
|
+
contextWindowCompression,
|
|
3290
3595
|
tools: this.tools,
|
|
3291
3596
|
toolConfig: this.toolConfig,
|
|
3292
3597
|
systemInstruction: this.systemInstruction,
|
|
3293
3598
|
inputAudioTranscription,
|
|
3294
|
-
outputAudioTranscription
|
|
3599
|
+
outputAudioTranscription,
|
|
3600
|
+
sessionResumption
|
|
3295
3601
|
}
|
|
3296
3602
|
};
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
this._webSocketHandler.send(JSON.stringify(setupMessage));
|
|
3301
|
-
// Verify we received the handshake response 'setupComplete'
|
|
3302
|
-
const firstMessage = (await serverMessages.next()).value;
|
|
3303
|
-
if (!firstMessage ||
|
|
3304
|
-
!(typeof firstMessage === 'object') ||
|
|
3305
|
-
!('setupComplete' in firstMessage)) {
|
|
3306
|
-
await this._webSocketHandler.close(1011, 'Handshake failure');
|
|
3307
|
-
throw new AIError(AIErrorCode.RESPONSE_ERROR, 'Server connection handshake failed. The server did not respond with a setupComplete message.');
|
|
3308
|
-
}
|
|
3309
|
-
return new LiveSession(this._webSocketHandler, serverMessages);
|
|
3310
|
-
}
|
|
3311
|
-
catch (e) {
|
|
3312
|
-
// Ensure connection is closed on any setup error
|
|
3313
|
-
await this._webSocketHandler.close();
|
|
3314
|
-
throw e;
|
|
3315
|
-
}
|
|
3603
|
+
const session = new LiveSession(setupMessage, this._apiSettings, sessionResumption, this._webSocketHandler);
|
|
3604
|
+
await session.connectionPromise;
|
|
3605
|
+
return session;
|
|
3316
3606
|
}
|
|
3317
3607
|
}
|
|
3318
3608
|
|
|
@@ -3453,153 +3743,6 @@ class ImagenModel extends AIModel {
|
|
|
3453
3743
|
}
|
|
3454
3744
|
}
|
|
3455
3745
|
|
|
3456
|
-
/**
|
|
3457
|
-
* @license
|
|
3458
|
-
* Copyright 2025 Google LLC
|
|
3459
|
-
*
|
|
3460
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
3461
|
-
* you may not use this file except in compliance with the License.
|
|
3462
|
-
* You may obtain a copy of the License at
|
|
3463
|
-
*
|
|
3464
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
3465
|
-
*
|
|
3466
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
3467
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
3468
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
3469
|
-
* See the License for the specific language governing permissions and
|
|
3470
|
-
* limitations under the License.
|
|
3471
|
-
*/
|
|
3472
|
-
/**
|
|
3473
|
-
* A wrapper for the native `WebSocket` available in both Browsers and Node >= 22.
|
|
3474
|
-
*
|
|
3475
|
-
* @internal
|
|
3476
|
-
*/
|
|
3477
|
-
class WebSocketHandlerImpl {
|
|
3478
|
-
constructor() {
|
|
3479
|
-
if (typeof WebSocket === 'undefined') {
|
|
3480
|
-
throw new AIError(AIErrorCode.UNSUPPORTED, 'The WebSocket API is not available in this environment. ' +
|
|
3481
|
-
'The "Live" feature is not supported here. It is supported in ' +
|
|
3482
|
-
'modern browser windows, Web Workers with WebSocket support, and Node >= 22.');
|
|
3483
|
-
}
|
|
3484
|
-
}
|
|
3485
|
-
connect(url) {
|
|
3486
|
-
return new Promise((resolve, reject) => {
|
|
3487
|
-
this.ws = new WebSocket(url);
|
|
3488
|
-
this.ws.binaryType = 'blob'; // Only important to set in Node
|
|
3489
|
-
this.ws.addEventListener('open', () => resolve(), { once: true });
|
|
3490
|
-
this.ws.addEventListener('error', () => reject(new AIError(AIErrorCode.FETCH_ERROR, `Error event raised on WebSocket`)), { once: true });
|
|
3491
|
-
this.ws.addEventListener('close', (closeEvent) => {
|
|
3492
|
-
if (closeEvent.reason) {
|
|
3493
|
-
logger.warn(`WebSocket connection closed by server. Reason: '${closeEvent.reason}'`);
|
|
3494
|
-
}
|
|
3495
|
-
});
|
|
3496
|
-
});
|
|
3497
|
-
}
|
|
3498
|
-
send(data) {
|
|
3499
|
-
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
|
|
3500
|
-
throw new AIError(AIErrorCode.REQUEST_ERROR, 'WebSocket is not open.');
|
|
3501
|
-
}
|
|
3502
|
-
this.ws.send(data);
|
|
3503
|
-
}
|
|
3504
|
-
async *listen() {
|
|
3505
|
-
if (!this.ws) {
|
|
3506
|
-
throw new AIError(AIErrorCode.REQUEST_ERROR, 'WebSocket is not connected.');
|
|
3507
|
-
}
|
|
3508
|
-
const messageQueue = [];
|
|
3509
|
-
const errorQueue = [];
|
|
3510
|
-
let resolvePromise = null;
|
|
3511
|
-
let isClosed = false;
|
|
3512
|
-
const messageListener = async (event) => {
|
|
3513
|
-
let data;
|
|
3514
|
-
if (event.data instanceof Blob) {
|
|
3515
|
-
data = await event.data.text();
|
|
3516
|
-
}
|
|
3517
|
-
else if (typeof event.data === 'string') {
|
|
3518
|
-
data = event.data;
|
|
3519
|
-
}
|
|
3520
|
-
else {
|
|
3521
|
-
errorQueue.push(new AIError(AIErrorCode.PARSE_FAILED, `Failed to parse WebSocket response. Expected data to be a Blob or string, but was ${typeof event.data}.`));
|
|
3522
|
-
if (resolvePromise) {
|
|
3523
|
-
resolvePromise();
|
|
3524
|
-
resolvePromise = null;
|
|
3525
|
-
}
|
|
3526
|
-
return;
|
|
3527
|
-
}
|
|
3528
|
-
try {
|
|
3529
|
-
const obj = JSON.parse(data);
|
|
3530
|
-
messageQueue.push(obj);
|
|
3531
|
-
}
|
|
3532
|
-
catch (e) {
|
|
3533
|
-
const err = e;
|
|
3534
|
-
errorQueue.push(new AIError(AIErrorCode.PARSE_FAILED, `Error parsing WebSocket message to JSON: ${err.message}`));
|
|
3535
|
-
}
|
|
3536
|
-
if (resolvePromise) {
|
|
3537
|
-
resolvePromise();
|
|
3538
|
-
resolvePromise = null;
|
|
3539
|
-
}
|
|
3540
|
-
};
|
|
3541
|
-
const errorListener = () => {
|
|
3542
|
-
errorQueue.push(new AIError(AIErrorCode.FETCH_ERROR, 'WebSocket connection error.'));
|
|
3543
|
-
if (resolvePromise) {
|
|
3544
|
-
resolvePromise();
|
|
3545
|
-
resolvePromise = null;
|
|
3546
|
-
}
|
|
3547
|
-
};
|
|
3548
|
-
const closeListener = (event) => {
|
|
3549
|
-
if (event.reason) {
|
|
3550
|
-
logger.warn(`WebSocket connection closed by the server with reason: ${event.reason}`);
|
|
3551
|
-
}
|
|
3552
|
-
isClosed = true;
|
|
3553
|
-
if (resolvePromise) {
|
|
3554
|
-
resolvePromise();
|
|
3555
|
-
resolvePromise = null;
|
|
3556
|
-
}
|
|
3557
|
-
// Clean up listeners to prevent memory leaks
|
|
3558
|
-
this.ws?.removeEventListener('message', messageListener);
|
|
3559
|
-
this.ws?.removeEventListener('close', closeListener);
|
|
3560
|
-
this.ws?.removeEventListener('error', errorListener);
|
|
3561
|
-
};
|
|
3562
|
-
this.ws.addEventListener('message', messageListener);
|
|
3563
|
-
this.ws.addEventListener('close', closeListener);
|
|
3564
|
-
this.ws.addEventListener('error', errorListener);
|
|
3565
|
-
while (!isClosed) {
|
|
3566
|
-
if (errorQueue.length > 0) {
|
|
3567
|
-
const error = errorQueue.shift();
|
|
3568
|
-
throw error;
|
|
3569
|
-
}
|
|
3570
|
-
if (messageQueue.length > 0) {
|
|
3571
|
-
yield messageQueue.shift();
|
|
3572
|
-
}
|
|
3573
|
-
else {
|
|
3574
|
-
await new Promise(resolve => {
|
|
3575
|
-
resolvePromise = resolve;
|
|
3576
|
-
});
|
|
3577
|
-
}
|
|
3578
|
-
}
|
|
3579
|
-
// If the loop terminated because isClosed is true, check for any final errors
|
|
3580
|
-
if (errorQueue.length > 0) {
|
|
3581
|
-
const error = errorQueue.shift();
|
|
3582
|
-
throw error;
|
|
3583
|
-
}
|
|
3584
|
-
}
|
|
3585
|
-
close(code, reason) {
|
|
3586
|
-
return new Promise(resolve => {
|
|
3587
|
-
if (!this.ws) {
|
|
3588
|
-
return resolve();
|
|
3589
|
-
}
|
|
3590
|
-
this.ws.addEventListener('close', () => resolve(), { once: true });
|
|
3591
|
-
// Calling 'close' during these states results in an error.
|
|
3592
|
-
if (this.ws.readyState === WebSocket.CLOSED ||
|
|
3593
|
-
this.ws.readyState === WebSocket.CONNECTING) {
|
|
3594
|
-
return resolve();
|
|
3595
|
-
}
|
|
3596
|
-
if (this.ws.readyState !== WebSocket.CLOSING) {
|
|
3597
|
-
this.ws.close(code, reason);
|
|
3598
|
-
}
|
|
3599
|
-
});
|
|
3600
|
-
}
|
|
3601
|
-
}
|
|
3602
|
-
|
|
3603
3746
|
/**
|
|
3604
3747
|
* @license
|
|
3605
3748
|
* Copyright 2026 Google LLC
|
|
@@ -3744,11 +3887,16 @@ class TemplateGenerativeModel {
|
|
|
3744
3887
|
* @param templateId - The ID of the server-side template to execute.
|
|
3745
3888
|
* @param templateVariables - A key-value map of variables to populate the
|
|
3746
3889
|
* template with.
|
|
3890
|
+
* @param singleRequestOptions - Optional. Options to use for this request.
|
|
3891
|
+
* @param templateToolConfig - Optional. Configuration for tools to use with this request.
|
|
3747
3892
|
*
|
|
3748
3893
|
* @beta
|
|
3749
3894
|
*/
|
|
3750
|
-
async generateContent(templateId, templateVariables, singleRequestOptions) {
|
|
3751
|
-
return templateGenerateContent(this._apiSettings, templateId, {
|
|
3895
|
+
async generateContent(templateId, templateVariables, singleRequestOptions, templateToolConfig) {
|
|
3896
|
+
return templateGenerateContent(this._apiSettings, templateId, {
|
|
3897
|
+
inputs: templateVariables,
|
|
3898
|
+
...(templateToolConfig && { toolConfig: templateToolConfig })
|
|
3899
|
+
}, {
|
|
3752
3900
|
...this.requestOptions,
|
|
3753
3901
|
...singleRequestOptions
|
|
3754
3902
|
});
|
|
@@ -3762,11 +3910,16 @@ class TemplateGenerativeModel {
|
|
|
3762
3910
|
* @param templateId - The ID of the server-side template to execute.
|
|
3763
3911
|
* @param templateVariables - A key-value map of variables to populate the
|
|
3764
3912
|
* template with.
|
|
3913
|
+
* @param singleRequestOptions - Optional.Options to use for this request.
|
|
3914
|
+
* @param templateToolConfig - Optional. Configuration for tools to use with this request.
|
|
3765
3915
|
*
|
|
3766
3916
|
* @beta
|
|
3767
3917
|
*/
|
|
3768
|
-
async generateContentStream(templateId, templateVariables, singleRequestOptions) {
|
|
3769
|
-
return templateGenerateContentStream(this._apiSettings, templateId, {
|
|
3918
|
+
async generateContentStream(templateId, templateVariables, singleRequestOptions, templateToolConfig) {
|
|
3919
|
+
return templateGenerateContentStream(this._apiSettings, templateId, {
|
|
3920
|
+
inputs: templateVariables,
|
|
3921
|
+
...(templateToolConfig && { toolConfig: templateToolConfig })
|
|
3922
|
+
}, {
|
|
3770
3923
|
...this.requestOptions,
|
|
3771
3924
|
...singleRequestOptions
|
|
3772
3925
|
});
|
|
@@ -4659,8 +4812,7 @@ function getLiveGenerativeModel(ai, modelParams) {
|
|
|
4659
4812
|
if (!modelParams.model) {
|
|
4660
4813
|
throw new AIError(AIErrorCode.NO_MODEL, `Must provide a model name for getLiveGenerativeModel. Example: getLiveGenerativeModel(ai, { model: 'my-model-name' })`);
|
|
4661
4814
|
}
|
|
4662
|
-
|
|
4663
|
-
return new LiveGenerativeModel(ai, modelParams, webSocketHandler);
|
|
4815
|
+
return new LiveGenerativeModel(ai, modelParams);
|
|
4664
4816
|
}
|
|
4665
4817
|
/**
|
|
4666
4818
|
* Returns a {@link TemplateGenerativeModel} class for executing server-side
|
|
@@ -4703,5 +4855,5 @@ function registerAI() {
|
|
|
4703
4855
|
}
|
|
4704
4856
|
registerAI();
|
|
4705
4857
|
|
|
4706
|
-
export { AIError, AIErrorCode, AIModel, AnyOfSchema, ArraySchema, Backend, BackendType, BlockReason, BooleanSchema, ChatSession, ChatSessionBase, FinishReason, FunctionCallingMode, GenerativeModel, GoogleAIBackend, HarmBlockMethod, HarmBlockThreshold, HarmCategory, HarmProbability, HarmSeverity, ImagenAspectRatio, ImagenImageFormat, ImagenModel, ImagenPersonFilterLevel, ImagenSafetyFilterLevel, InferenceMode, InferenceSource, IntegerSchema, Language, LiveGenerativeModel, LiveResponseType, LiveSession, Modality, NumberSchema, ObjectSchema, Outcome, POSSIBLE_ROLES, ResponseModality, Schema, SchemaType, StringSchema, TemplateGenerativeModel, TemplateImagenModel, ThinkingLevel, URLRetrievalStatus, VertexAIBackend, getAI, getGenerativeModel, getImagenModel, getLiveGenerativeModel, getTemplateGenerativeModel, getTemplateImagenModel, startAudioConversation };
|
|
4858
|
+
export { AIError, AIErrorCode, AIModel, AnyOfSchema, ArraySchema, Backend, BackendType, BlockReason, BooleanSchema, ChatSession, ChatSessionBase, FinishReason, FunctionCallingMode, GenerativeModel, GoogleAIBackend, HarmBlockMethod, HarmBlockThreshold, HarmCategory, HarmProbability, HarmSeverity, ImageConfigAspectRatio, ImageConfigImageSize, ImagenAspectRatio, ImagenImageFormat, ImagenModel, ImagenPersonFilterLevel, ImagenSafetyFilterLevel, InferenceMode, InferenceSource, IntegerSchema, Language, LiveGenerativeModel, LiveResponseType, LiveSession, Modality, NumberSchema, ObjectSchema, Outcome, POSSIBLE_ROLES, ResponseModality, Schema, SchemaType, StringSchema, TemplateGenerativeModel, TemplateImagenModel, ThinkingLevel, URLRetrievalStatus, VertexAIBackend, getAI, getGenerativeModel, getImagenModel, getLiveGenerativeModel, getTemplateGenerativeModel, getTemplateImagenModel, startAudioConversation };
|
|
4707
4859
|
//# sourceMappingURL=index.node.mjs.map
|