@firebase/ai 2.4.0-canary.44d9891f9 → 2.4.0-canary.6e0e30317
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 +111 -9
- package/dist/ai.d.ts +114 -9
- package/dist/esm/index.esm.js +129 -39
- package/dist/esm/index.esm.js.map +1 -1
- package/dist/esm/src/factory-node.d.ts +19 -0
- package/dist/esm/src/methods/live-session.d.ts +64 -9
- package/dist/esm/src/service.d.ts +3 -4
- package/dist/esm/src/types/chrome-adapter.d.ts +5 -0
- package/dist/esm/src/types/live-responses.d.ts +21 -3
- package/dist/esm/src/types/requests.d.ts +23 -0
- package/dist/esm/src/types/responses.d.ts +21 -0
- package/dist/index.cjs.js +129 -39
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.node.cjs.js +253 -145
- package/dist/index.node.cjs.js.map +1 -1
- package/dist/index.node.mjs +253 -145
- package/dist/index.node.mjs.map +1 -1
- package/dist/src/factory-node.d.ts +19 -0
- package/dist/src/methods/live-session.d.ts +64 -9
- package/dist/src/service.d.ts +3 -4
- package/dist/src/types/chrome-adapter.d.ts +5 -0
- package/dist/src/types/live-responses.d.ts +21 -3
- package/dist/src/types/requests.d.ts +23 -0
- package/dist/src/types/responses.d.ts +21 -0
- package/package.json +8 -8
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.4.0-canary.
|
|
11
|
+
var version = "2.4.0-canary.6e0e30317";
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* @license
|
|
@@ -38,6 +38,62 @@ const DEFAULT_FETCH_TIMEOUT_MS = 180 * 1000;
|
|
|
38
38
|
*/
|
|
39
39
|
const DEFAULT_HYBRID_IN_CLOUD_MODEL = 'gemini-2.0-flash-lite';
|
|
40
40
|
|
|
41
|
+
/**
|
|
42
|
+
* @license
|
|
43
|
+
* Copyright 2024 Google LLC
|
|
44
|
+
*
|
|
45
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
46
|
+
* you may not use this file except in compliance with the License.
|
|
47
|
+
* You may obtain a copy of the License at
|
|
48
|
+
*
|
|
49
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
50
|
+
*
|
|
51
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
52
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
53
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
54
|
+
* See the License for the specific language governing permissions and
|
|
55
|
+
* limitations under the License.
|
|
56
|
+
*/
|
|
57
|
+
/**
|
|
58
|
+
* Error class for the Firebase AI SDK.
|
|
59
|
+
*
|
|
60
|
+
* @public
|
|
61
|
+
*/
|
|
62
|
+
class AIError extends util.FirebaseError {
|
|
63
|
+
/**
|
|
64
|
+
* Constructs a new instance of the `AIError` class.
|
|
65
|
+
*
|
|
66
|
+
* @param code - The error code from {@link (AIErrorCode:type)}.
|
|
67
|
+
* @param message - A human-readable message describing the error.
|
|
68
|
+
* @param customErrorData - Optional error data.
|
|
69
|
+
*/
|
|
70
|
+
constructor(code, message, customErrorData) {
|
|
71
|
+
// Match error format used by FirebaseError from ErrorFactory
|
|
72
|
+
const service = AI_TYPE;
|
|
73
|
+
const fullCode = `${service}/${code}`;
|
|
74
|
+
const fullMessage = `${service}: ${message} (${fullCode})`;
|
|
75
|
+
super(code, fullMessage);
|
|
76
|
+
this.code = code;
|
|
77
|
+
this.customErrorData = customErrorData;
|
|
78
|
+
// FirebaseError initializes a stack trace, but it assumes the error is created from the error
|
|
79
|
+
// factory. Since we break this assumption, we set the stack trace to be originating from this
|
|
80
|
+
// constructor.
|
|
81
|
+
// This is only supported in V8.
|
|
82
|
+
if (Error.captureStackTrace) {
|
|
83
|
+
// Allows us to initialize the stack trace without including the constructor itself at the
|
|
84
|
+
// top level of the stack trace.
|
|
85
|
+
Error.captureStackTrace(this, AIError);
|
|
86
|
+
}
|
|
87
|
+
// Allows instanceof AIError in ES5/ES6
|
|
88
|
+
// https://github.com/Microsoft/TypeScript-wiki/blob/master/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work
|
|
89
|
+
// TODO(dlarocque): Replace this with `new.target`: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-2.html#support-for-newtarget
|
|
90
|
+
// which we can now use since we no longer target ES5.
|
|
91
|
+
Object.setPrototypeOf(this, AIError.prototype);
|
|
92
|
+
// Since Error is an interface, we don't inherit toString and so we define it ourselves.
|
|
93
|
+
this.toString = () => fullMessage;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
41
97
|
/**
|
|
42
98
|
* @license
|
|
43
99
|
* Copyright 2024 Google LLC
|
|
@@ -742,6 +798,64 @@ class VertexAIBackend extends Backend {
|
|
|
742
798
|
}
|
|
743
799
|
}
|
|
744
800
|
|
|
801
|
+
/**
|
|
802
|
+
* @license
|
|
803
|
+
* Copyright 2025 Google LLC
|
|
804
|
+
*
|
|
805
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
806
|
+
* you may not use this file except in compliance with the License.
|
|
807
|
+
* You may obtain a copy of the License at
|
|
808
|
+
*
|
|
809
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
810
|
+
*
|
|
811
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
812
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
813
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
814
|
+
* See the License for the specific language governing permissions and
|
|
815
|
+
* limitations under the License.
|
|
816
|
+
*/
|
|
817
|
+
/**
|
|
818
|
+
* Encodes a {@link Backend} into a string that will be used to uniquely identify {@link AI}
|
|
819
|
+
* instances by backend type.
|
|
820
|
+
*
|
|
821
|
+
* @internal
|
|
822
|
+
*/
|
|
823
|
+
function encodeInstanceIdentifier(backend) {
|
|
824
|
+
if (backend instanceof GoogleAIBackend) {
|
|
825
|
+
return `${AI_TYPE}/googleai`;
|
|
826
|
+
}
|
|
827
|
+
else if (backend instanceof VertexAIBackend) {
|
|
828
|
+
return `${AI_TYPE}/vertexai/${backend.location}`;
|
|
829
|
+
}
|
|
830
|
+
else {
|
|
831
|
+
throw new AIError(AIErrorCode.ERROR, `Invalid backend: ${JSON.stringify(backend.backendType)}`);
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
/**
|
|
835
|
+
* Decodes an instance identifier string into a {@link Backend}.
|
|
836
|
+
*
|
|
837
|
+
* @internal
|
|
838
|
+
*/
|
|
839
|
+
function decodeInstanceIdentifier(instanceIdentifier) {
|
|
840
|
+
const identifierParts = instanceIdentifier.split('/');
|
|
841
|
+
if (identifierParts[0] !== AI_TYPE) {
|
|
842
|
+
throw new AIError(AIErrorCode.ERROR, `Invalid instance identifier, unknown prefix '${identifierParts[0]}'`);
|
|
843
|
+
}
|
|
844
|
+
const backendType = identifierParts[1];
|
|
845
|
+
switch (backendType) {
|
|
846
|
+
case 'vertexai':
|
|
847
|
+
const location = identifierParts[2];
|
|
848
|
+
if (!location) {
|
|
849
|
+
throw new AIError(AIErrorCode.ERROR, `Invalid instance identifier, unknown location '${instanceIdentifier}'`);
|
|
850
|
+
}
|
|
851
|
+
return new VertexAIBackend(location);
|
|
852
|
+
case 'googleai':
|
|
853
|
+
return new GoogleAIBackend();
|
|
854
|
+
default:
|
|
855
|
+
throw new AIError(AIErrorCode.ERROR, `Invalid instance identifier string: '${instanceIdentifier}'`);
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
|
|
745
859
|
/**
|
|
746
860
|
* @license
|
|
747
861
|
* Copyright 2024 Google LLC
|
|
@@ -785,62 +899,6 @@ class AIService {
|
|
|
785
899
|
}
|
|
786
900
|
}
|
|
787
901
|
|
|
788
|
-
/**
|
|
789
|
-
* @license
|
|
790
|
-
* Copyright 2024 Google LLC
|
|
791
|
-
*
|
|
792
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
793
|
-
* you may not use this file except in compliance with the License.
|
|
794
|
-
* You may obtain a copy of the License at
|
|
795
|
-
*
|
|
796
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
797
|
-
*
|
|
798
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
799
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
800
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
801
|
-
* See the License for the specific language governing permissions and
|
|
802
|
-
* limitations under the License.
|
|
803
|
-
*/
|
|
804
|
-
/**
|
|
805
|
-
* Error class for the Firebase AI SDK.
|
|
806
|
-
*
|
|
807
|
-
* @public
|
|
808
|
-
*/
|
|
809
|
-
class AIError extends util.FirebaseError {
|
|
810
|
-
/**
|
|
811
|
-
* Constructs a new instance of the `AIError` class.
|
|
812
|
-
*
|
|
813
|
-
* @param code - The error code from {@link (AIErrorCode:type)}.
|
|
814
|
-
* @param message - A human-readable message describing the error.
|
|
815
|
-
* @param customErrorData - Optional error data.
|
|
816
|
-
*/
|
|
817
|
-
constructor(code, message, customErrorData) {
|
|
818
|
-
// Match error format used by FirebaseError from ErrorFactory
|
|
819
|
-
const service = AI_TYPE;
|
|
820
|
-
const fullCode = `${service}/${code}`;
|
|
821
|
-
const fullMessage = `${service}: ${message} (${fullCode})`;
|
|
822
|
-
super(code, fullMessage);
|
|
823
|
-
this.code = code;
|
|
824
|
-
this.customErrorData = customErrorData;
|
|
825
|
-
// FirebaseError initializes a stack trace, but it assumes the error is created from the error
|
|
826
|
-
// factory. Since we break this assumption, we set the stack trace to be originating from this
|
|
827
|
-
// constructor.
|
|
828
|
-
// This is only supported in V8.
|
|
829
|
-
if (Error.captureStackTrace) {
|
|
830
|
-
// Allows us to initialize the stack trace without including the constructor itself at the
|
|
831
|
-
// top level of the stack trace.
|
|
832
|
-
Error.captureStackTrace(this, AIError);
|
|
833
|
-
}
|
|
834
|
-
// Allows instanceof AIError in ES5/ES6
|
|
835
|
-
// https://github.com/Microsoft/TypeScript-wiki/blob/master/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work
|
|
836
|
-
// TODO(dlarocque): Replace this with `new.target`: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-2.html#support-for-newtarget
|
|
837
|
-
// which we can now use since we no longer target ES5.
|
|
838
|
-
Object.setPrototypeOf(this, AIError.prototype);
|
|
839
|
-
// Since Error is an interface, we don't inherit toString and so we define it ourselves.
|
|
840
|
-
this.toString = () => fullMessage;
|
|
841
|
-
}
|
|
842
|
-
}
|
|
843
|
-
|
|
844
902
|
/**
|
|
845
903
|
* @license
|
|
846
904
|
* Copyright 2025 Google LLC
|
|
@@ -857,46 +915,16 @@ class AIError extends util.FirebaseError {
|
|
|
857
915
|
* See the License for the specific language governing permissions and
|
|
858
916
|
* limitations under the License.
|
|
859
917
|
*/
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
*
|
|
864
|
-
* @internal
|
|
865
|
-
*/
|
|
866
|
-
function encodeInstanceIdentifier(backend) {
|
|
867
|
-
if (backend instanceof GoogleAIBackend) {
|
|
868
|
-
return `${AI_TYPE}/googleai`;
|
|
869
|
-
}
|
|
870
|
-
else if (backend instanceof VertexAIBackend) {
|
|
871
|
-
return `${AI_TYPE}/vertexai/${backend.location}`;
|
|
872
|
-
}
|
|
873
|
-
else {
|
|
874
|
-
throw new AIError(AIErrorCode.ERROR, `Invalid backend: ${JSON.stringify(backend.backendType)}`);
|
|
875
|
-
}
|
|
876
|
-
}
|
|
877
|
-
/**
|
|
878
|
-
* Decodes an instance identifier string into a {@link Backend}.
|
|
879
|
-
*
|
|
880
|
-
* @internal
|
|
881
|
-
*/
|
|
882
|
-
function decodeInstanceIdentifier(instanceIdentifier) {
|
|
883
|
-
const identifierParts = instanceIdentifier.split('/');
|
|
884
|
-
if (identifierParts[0] !== AI_TYPE) {
|
|
885
|
-
throw new AIError(AIErrorCode.ERROR, `Invalid instance identifier, unknown prefix '${identifierParts[0]}'`);
|
|
886
|
-
}
|
|
887
|
-
const backendType = identifierParts[1];
|
|
888
|
-
switch (backendType) {
|
|
889
|
-
case 'vertexai':
|
|
890
|
-
const location = identifierParts[2];
|
|
891
|
-
if (!location) {
|
|
892
|
-
throw new AIError(AIErrorCode.ERROR, `Invalid instance identifier, unknown location '${instanceIdentifier}'`);
|
|
893
|
-
}
|
|
894
|
-
return new VertexAIBackend(location);
|
|
895
|
-
case 'googleai':
|
|
896
|
-
return new GoogleAIBackend();
|
|
897
|
-
default:
|
|
898
|
-
throw new AIError(AIErrorCode.ERROR, `Invalid instance identifier string: '${instanceIdentifier}'`);
|
|
918
|
+
function factory(container, { instanceIdentifier }) {
|
|
919
|
+
if (!instanceIdentifier) {
|
|
920
|
+
throw new AIError(AIErrorCode.ERROR, 'AIService instance identifier is undefined.');
|
|
899
921
|
}
|
|
922
|
+
const backend = decodeInstanceIdentifier(instanceIdentifier);
|
|
923
|
+
// getImmediate for FirebaseApp will always succeed
|
|
924
|
+
const app = container.getProvider('app').getImmediate();
|
|
925
|
+
const auth = container.getProvider('auth-internal');
|
|
926
|
+
const appCheckProvider = container.getProvider('app-check-internal');
|
|
927
|
+
return new AIService(app, backend, auth, appCheckProvider);
|
|
900
928
|
}
|
|
901
929
|
|
|
902
930
|
/**
|
|
@@ -2538,75 +2566,104 @@ class LiveSession {
|
|
|
2538
2566
|
this.webSocketHandler.send(JSON.stringify(message));
|
|
2539
2567
|
}
|
|
2540
2568
|
/**
|
|
2541
|
-
* Sends
|
|
2569
|
+
* Sends text to the server in realtime.
|
|
2542
2570
|
*
|
|
2543
|
-
* @
|
|
2571
|
+
* @example
|
|
2572
|
+
* ```javascript
|
|
2573
|
+
* liveSession.sendTextRealtime("Hello, how are you?");
|
|
2574
|
+
* ```
|
|
2575
|
+
*
|
|
2576
|
+
* @param text - The text data to send.
|
|
2544
2577
|
* @throws If this session has been closed.
|
|
2545
2578
|
*
|
|
2546
2579
|
* @beta
|
|
2547
2580
|
*/
|
|
2548
|
-
async
|
|
2581
|
+
async sendTextRealtime(text) {
|
|
2549
2582
|
if (this.isClosed) {
|
|
2550
2583
|
throw new AIError(AIErrorCode.REQUEST_ERROR, 'This LiveSession has been closed and cannot be used.');
|
|
2551
2584
|
}
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
this.webSocketHandler.send(JSON.stringify(message));
|
|
2559
|
-
});
|
|
2585
|
+
const message = {
|
|
2586
|
+
realtimeInput: {
|
|
2587
|
+
text
|
|
2588
|
+
}
|
|
2589
|
+
};
|
|
2590
|
+
this.webSocketHandler.send(JSON.stringify(message));
|
|
2560
2591
|
}
|
|
2561
2592
|
/**
|
|
2562
|
-
* Sends
|
|
2593
|
+
* Sends audio data to the server in realtime.
|
|
2563
2594
|
*
|
|
2564
|
-
* @
|
|
2595
|
+
* @remarks The server requires that the audio data is base64-encoded 16-bit PCM at 16kHz
|
|
2596
|
+
* little-endian.
|
|
2597
|
+
*
|
|
2598
|
+
* @example
|
|
2599
|
+
* ```javascript
|
|
2600
|
+
* // const pcmData = ... base64-encoded 16-bit PCM at 16kHz little-endian.
|
|
2601
|
+
* const blob = { mimeType: "audio/pcm", data: pcmData };
|
|
2602
|
+
* liveSession.sendAudioRealtime(blob);
|
|
2603
|
+
* ```
|
|
2604
|
+
*
|
|
2605
|
+
* @param blob - The base64-encoded PCM data to send to the server in realtime.
|
|
2565
2606
|
* @throws If this session has been closed.
|
|
2566
2607
|
*
|
|
2567
2608
|
* @beta
|
|
2568
2609
|
*/
|
|
2569
|
-
async
|
|
2610
|
+
async sendAudioRealtime(blob) {
|
|
2570
2611
|
if (this.isClosed) {
|
|
2571
2612
|
throw new AIError(AIErrorCode.REQUEST_ERROR, 'This LiveSession has been closed and cannot be used.');
|
|
2572
2613
|
}
|
|
2573
2614
|
const message = {
|
|
2574
|
-
|
|
2575
|
-
|
|
2615
|
+
realtimeInput: {
|
|
2616
|
+
audio: blob
|
|
2576
2617
|
}
|
|
2577
2618
|
};
|
|
2578
2619
|
this.webSocketHandler.send(JSON.stringify(message));
|
|
2579
2620
|
}
|
|
2580
2621
|
/**
|
|
2581
|
-
* Sends
|
|
2622
|
+
* Sends video data to the server in realtime.
|
|
2582
2623
|
*
|
|
2583
|
-
* @
|
|
2624
|
+
* @remarks The server requires that the video is sent as individual video frames at 1 FPS. It
|
|
2625
|
+
* is recommended to set `mimeType` to `image/jpeg`.
|
|
2626
|
+
*
|
|
2627
|
+
* @example
|
|
2628
|
+
* ```javascript
|
|
2629
|
+
* // const videoFrame = ... base64-encoded JPEG data
|
|
2630
|
+
* const blob = { mimeType: "image/jpeg", data: videoFrame };
|
|
2631
|
+
* liveSession.sendVideoRealtime(blob);
|
|
2632
|
+
* ```
|
|
2633
|
+
* @param blob - The base64-encoded video data to send to the server in realtime.
|
|
2584
2634
|
* @throws If this session has been closed.
|
|
2585
2635
|
*
|
|
2586
2636
|
* @beta
|
|
2587
2637
|
*/
|
|
2588
|
-
async
|
|
2638
|
+
async sendVideoRealtime(blob) {
|
|
2589
2639
|
if (this.isClosed) {
|
|
2590
2640
|
throw new AIError(AIErrorCode.REQUEST_ERROR, 'This LiveSession has been closed and cannot be used.');
|
|
2591
2641
|
}
|
|
2592
|
-
const
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
const { done, value } = await reader.read();
|
|
2596
|
-
if (done) {
|
|
2597
|
-
break;
|
|
2598
|
-
}
|
|
2599
|
-
else if (!value) {
|
|
2600
|
-
throw new Error('Missing chunk in reader, but reader is not done.');
|
|
2601
|
-
}
|
|
2602
|
-
await this.sendMediaChunks([value]);
|
|
2603
|
-
}
|
|
2604
|
-
catch (e) {
|
|
2605
|
-
// Re-throw any errors that occur during stream consumption or sending.
|
|
2606
|
-
const message = e instanceof Error ? e.message : 'Error processing media stream.';
|
|
2607
|
-
throw new AIError(AIErrorCode.REQUEST_ERROR, message);
|
|
2642
|
+
const message = {
|
|
2643
|
+
realtimeInput: {
|
|
2644
|
+
video: blob
|
|
2608
2645
|
}
|
|
2646
|
+
};
|
|
2647
|
+
this.webSocketHandler.send(JSON.stringify(message));
|
|
2648
|
+
}
|
|
2649
|
+
/**
|
|
2650
|
+
* Sends function responses to the server.
|
|
2651
|
+
*
|
|
2652
|
+
* @param functionResponses - The function responses to send.
|
|
2653
|
+
* @throws If this session has been closed.
|
|
2654
|
+
*
|
|
2655
|
+
* @beta
|
|
2656
|
+
*/
|
|
2657
|
+
async sendFunctionResponses(functionResponses) {
|
|
2658
|
+
if (this.isClosed) {
|
|
2659
|
+
throw new AIError(AIErrorCode.REQUEST_ERROR, 'This LiveSession has been closed and cannot be used.');
|
|
2609
2660
|
}
|
|
2661
|
+
const message = {
|
|
2662
|
+
toolResponse: {
|
|
2663
|
+
functionResponses
|
|
2664
|
+
}
|
|
2665
|
+
};
|
|
2666
|
+
this.webSocketHandler.send(JSON.stringify(message));
|
|
2610
2667
|
}
|
|
2611
2668
|
/**
|
|
2612
2669
|
* Yields messages received from the server.
|
|
@@ -2664,6 +2721,62 @@ class LiveSession {
|
|
|
2664
2721
|
await this.webSocketHandler.close(1000, 'Client closed session.');
|
|
2665
2722
|
}
|
|
2666
2723
|
}
|
|
2724
|
+
/**
|
|
2725
|
+
* Sends realtime input to the server.
|
|
2726
|
+
*
|
|
2727
|
+
* @deprecated Use `sendTextRealtime()`, `sendAudioRealtime()`, and `sendVideoRealtime()` instead.
|
|
2728
|
+
*
|
|
2729
|
+
* @param mediaChunks - The media chunks to send.
|
|
2730
|
+
* @throws If this session has been closed.
|
|
2731
|
+
*
|
|
2732
|
+
* @beta
|
|
2733
|
+
*/
|
|
2734
|
+
async sendMediaChunks(mediaChunks) {
|
|
2735
|
+
if (this.isClosed) {
|
|
2736
|
+
throw new AIError(AIErrorCode.REQUEST_ERROR, 'This LiveSession has been closed and cannot be used.');
|
|
2737
|
+
}
|
|
2738
|
+
// The backend does not support sending more than one mediaChunk in one message.
|
|
2739
|
+
// Work around this limitation by sending mediaChunks in separate messages.
|
|
2740
|
+
mediaChunks.forEach(mediaChunk => {
|
|
2741
|
+
const message = {
|
|
2742
|
+
realtimeInput: { mediaChunks: [mediaChunk] }
|
|
2743
|
+
};
|
|
2744
|
+
this.webSocketHandler.send(JSON.stringify(message));
|
|
2745
|
+
});
|
|
2746
|
+
}
|
|
2747
|
+
/**
|
|
2748
|
+
* @deprecated Use `sendTextRealtime()`, `sendAudioRealtime()`, and `sendVideoRealtime()` instead.
|
|
2749
|
+
*
|
|
2750
|
+
* Sends a stream of {@link GenerativeContentBlob}.
|
|
2751
|
+
*
|
|
2752
|
+
* @param mediaChunkStream - The stream of {@link GenerativeContentBlob} to send.
|
|
2753
|
+
* @throws If this session has been closed.
|
|
2754
|
+
*
|
|
2755
|
+
* @beta
|
|
2756
|
+
*/
|
|
2757
|
+
async sendMediaStream(mediaChunkStream) {
|
|
2758
|
+
if (this.isClosed) {
|
|
2759
|
+
throw new AIError(AIErrorCode.REQUEST_ERROR, 'This LiveSession has been closed and cannot be used.');
|
|
2760
|
+
}
|
|
2761
|
+
const reader = mediaChunkStream.getReader();
|
|
2762
|
+
while (true) {
|
|
2763
|
+
try {
|
|
2764
|
+
const { done, value } = await reader.read();
|
|
2765
|
+
if (done) {
|
|
2766
|
+
break;
|
|
2767
|
+
}
|
|
2768
|
+
else if (!value) {
|
|
2769
|
+
throw new Error('Missing chunk in reader, but reader is not done.');
|
|
2770
|
+
}
|
|
2771
|
+
await this.sendMediaChunks([value]);
|
|
2772
|
+
}
|
|
2773
|
+
catch (e) {
|
|
2774
|
+
// Re-throw any errors that occur during stream consumption or sending.
|
|
2775
|
+
const message = e instanceof Error ? e.message : 'Error processing media stream.';
|
|
2776
|
+
throw new AIError(AIErrorCode.REQUEST_ERROR, message);
|
|
2777
|
+
}
|
|
2778
|
+
}
|
|
2779
|
+
}
|
|
2667
2780
|
}
|
|
2668
2781
|
|
|
2669
2782
|
/**
|
|
@@ -2724,13 +2837,18 @@ class LiveGenerativeModel extends AIModel {
|
|
|
2724
2837
|
else {
|
|
2725
2838
|
fullModelPath = `projects/${this._apiSettings.project}/locations/${this._apiSettings.location}/${this.model}`;
|
|
2726
2839
|
}
|
|
2840
|
+
// inputAudioTranscription and outputAudioTranscription are on the generation config in the public API,
|
|
2841
|
+
// but the backend expects them to be in the `setup` message.
|
|
2842
|
+
const { inputAudioTranscription, outputAudioTranscription, ...generationConfig } = this.generationConfig;
|
|
2727
2843
|
const setupMessage = {
|
|
2728
2844
|
setup: {
|
|
2729
2845
|
model: fullModelPath,
|
|
2730
|
-
generationConfig
|
|
2846
|
+
generationConfig,
|
|
2731
2847
|
tools: this.tools,
|
|
2732
2848
|
toolConfig: this.toolConfig,
|
|
2733
|
-
systemInstruction: this.systemInstruction
|
|
2849
|
+
systemInstruction: this.systemInstruction,
|
|
2850
|
+
inputAudioTranscription,
|
|
2851
|
+
outputAudioTranscription
|
|
2734
2852
|
}
|
|
2735
2853
|
};
|
|
2736
2854
|
try {
|
|
@@ -3436,7 +3554,7 @@ class AudioConversationRunner {
|
|
|
3436
3554
|
mimeType: 'audio/pcm',
|
|
3437
3555
|
data: base64
|
|
3438
3556
|
};
|
|
3439
|
-
void this.liveSession.
|
|
3557
|
+
void this.liveSession.sendAudioRealtime(chunk);
|
|
3440
3558
|
};
|
|
3441
3559
|
}
|
|
3442
3560
|
/**
|
|
@@ -3814,17 +3932,7 @@ function getLiveGenerativeModel(ai, modelParams) {
|
|
|
3814
3932
|
* @packageDocumentation
|
|
3815
3933
|
*/
|
|
3816
3934
|
function registerAI() {
|
|
3817
|
-
app._registerComponent(new component.Component(AI_TYPE,
|
|
3818
|
-
if (!instanceIdentifier) {
|
|
3819
|
-
throw new AIError(AIErrorCode.ERROR, 'AIService instance identifier is undefined.');
|
|
3820
|
-
}
|
|
3821
|
-
const backend = decodeInstanceIdentifier(instanceIdentifier);
|
|
3822
|
-
// getImmediate for FirebaseApp will always succeed
|
|
3823
|
-
const app = container.getProvider('app').getImmediate();
|
|
3824
|
-
const auth = container.getProvider('auth-internal');
|
|
3825
|
-
const appCheckProvider = container.getProvider('app-check-internal');
|
|
3826
|
-
return new AIService(app, backend, auth, appCheckProvider);
|
|
3827
|
-
}, "PUBLIC" /* ComponentType.PUBLIC */).setMultipleInstances(true));
|
|
3935
|
+
app._registerComponent(new component.Component(AI_TYPE, factory, "PUBLIC" /* ComponentType.PUBLIC */).setMultipleInstances(true));
|
|
3828
3936
|
app.registerVersion(name, version, 'node');
|
|
3829
3937
|
// BUILD_TARGET will be replaced by values like esm, cjs, etc during the compilation
|
|
3830
3938
|
app.registerVersion(name, version, 'cjs2020');
|