@firebase/ai 2.4.0 → 2.5.0-canary.0800a8bed
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 +134 -9
- package/dist/ai.d.ts +137 -9
- package/dist/esm/index.esm.js +202 -68
- package/dist/esm/index.esm.js.map +1 -1
- package/dist/esm/src/factory-node.d.ts +19 -0
- package/dist/esm/src/methods/chrome-adapter.d.ts +1 -1
- package/dist/esm/src/methods/live-session.d.ts +64 -9
- package/dist/esm/src/requests/hybrid-helpers.d.ts +7 -2
- package/dist/esm/src/requests/response-helpers.d.ts +2 -2
- package/dist/esm/src/requests/stream-reader.d.ts +2 -1
- 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/enums.d.ts +15 -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 +28 -1
- package/dist/index.cjs.js +202 -67
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.node.cjs.js +306 -166
- package/dist/index.node.cjs.js.map +1 -1
- package/dist/index.node.mjs +306 -167
- package/dist/index.node.mjs.map +1 -1
- package/dist/src/factory-node.d.ts +19 -0
- package/dist/src/methods/chrome-adapter.d.ts +1 -1
- package/dist/src/methods/live-session.d.ts +64 -9
- package/dist/src/requests/hybrid-helpers.d.ts +7 -2
- package/dist/src/requests/response-helpers.d.ts +2 -2
- package/dist/src/requests/stream-reader.d.ts +2 -1
- package/dist/src/service.d.ts +3 -4
- package/dist/src/types/chrome-adapter.d.ts +5 -0
- package/dist/src/types/enums.d.ts +15 -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 +28 -1
- package/package.json +8 -8
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.5.0-canary.0800a8bed";
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* @license
|
|
@@ -34,6 +34,62 @@ const DEFAULT_FETCH_TIMEOUT_MS = 180 * 1000;
|
|
|
34
34
|
*/
|
|
35
35
|
const DEFAULT_HYBRID_IN_CLOUD_MODEL = 'gemini-2.0-flash-lite';
|
|
36
36
|
|
|
37
|
+
/**
|
|
38
|
+
* @license
|
|
39
|
+
* Copyright 2024 Google LLC
|
|
40
|
+
*
|
|
41
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
42
|
+
* you may not use this file except in compliance with the License.
|
|
43
|
+
* You may obtain a copy of the License at
|
|
44
|
+
*
|
|
45
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
46
|
+
*
|
|
47
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
48
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
49
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
50
|
+
* See the License for the specific language governing permissions and
|
|
51
|
+
* limitations under the License.
|
|
52
|
+
*/
|
|
53
|
+
/**
|
|
54
|
+
* Error class for the Firebase AI SDK.
|
|
55
|
+
*
|
|
56
|
+
* @public
|
|
57
|
+
*/
|
|
58
|
+
class AIError extends FirebaseError {
|
|
59
|
+
/**
|
|
60
|
+
* Constructs a new instance of the `AIError` class.
|
|
61
|
+
*
|
|
62
|
+
* @param code - The error code from {@link (AIErrorCode:type)}.
|
|
63
|
+
* @param message - A human-readable message describing the error.
|
|
64
|
+
* @param customErrorData - Optional error data.
|
|
65
|
+
*/
|
|
66
|
+
constructor(code, message, customErrorData) {
|
|
67
|
+
// Match error format used by FirebaseError from ErrorFactory
|
|
68
|
+
const service = AI_TYPE;
|
|
69
|
+
const fullCode = `${service}/${code}`;
|
|
70
|
+
const fullMessage = `${service}: ${message} (${fullCode})`;
|
|
71
|
+
super(code, fullMessage);
|
|
72
|
+
this.code = code;
|
|
73
|
+
this.customErrorData = customErrorData;
|
|
74
|
+
// FirebaseError initializes a stack trace, but it assumes the error is created from the error
|
|
75
|
+
// factory. Since we break this assumption, we set the stack trace to be originating from this
|
|
76
|
+
// constructor.
|
|
77
|
+
// This is only supported in V8.
|
|
78
|
+
if (Error.captureStackTrace) {
|
|
79
|
+
// Allows us to initialize the stack trace without including the constructor itself at the
|
|
80
|
+
// top level of the stack trace.
|
|
81
|
+
Error.captureStackTrace(this, AIError);
|
|
82
|
+
}
|
|
83
|
+
// Allows instanceof AIError in ES5/ES6
|
|
84
|
+
// https://github.com/Microsoft/TypeScript-wiki/blob/master/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work
|
|
85
|
+
// TODO(dlarocque): Replace this with `new.target`: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-2.html#support-for-newtarget
|
|
86
|
+
// which we can now use since we no longer target ES5.
|
|
87
|
+
Object.setPrototypeOf(this, AIError.prototype);
|
|
88
|
+
// Since Error is an interface, we don't inherit toString and so we define it ourselves.
|
|
89
|
+
this.toString = () => fullMessage;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
37
93
|
/**
|
|
38
94
|
* @license
|
|
39
95
|
* Copyright 2024 Google LLC
|
|
@@ -323,6 +379,15 @@ const InferenceMode = {
|
|
|
323
379
|
'ONLY_IN_CLOUD': 'only_in_cloud',
|
|
324
380
|
'PREFER_IN_CLOUD': 'prefer_in_cloud'
|
|
325
381
|
};
|
|
382
|
+
/**
|
|
383
|
+
* Indicates whether inference happened on-device or in-cloud.
|
|
384
|
+
*
|
|
385
|
+
* @beta
|
|
386
|
+
*/
|
|
387
|
+
const InferenceSource = {
|
|
388
|
+
'ON_DEVICE': 'on_device',
|
|
389
|
+
'IN_CLOUD': 'in_cloud'
|
|
390
|
+
};
|
|
326
391
|
/**
|
|
327
392
|
* Represents the result of the code execution.
|
|
328
393
|
*
|
|
@@ -729,6 +794,64 @@ class VertexAIBackend extends Backend {
|
|
|
729
794
|
}
|
|
730
795
|
}
|
|
731
796
|
|
|
797
|
+
/**
|
|
798
|
+
* @license
|
|
799
|
+
* Copyright 2025 Google LLC
|
|
800
|
+
*
|
|
801
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
802
|
+
* you may not use this file except in compliance with the License.
|
|
803
|
+
* You may obtain a copy of the License at
|
|
804
|
+
*
|
|
805
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
806
|
+
*
|
|
807
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
808
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
809
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
810
|
+
* See the License for the specific language governing permissions and
|
|
811
|
+
* limitations under the License.
|
|
812
|
+
*/
|
|
813
|
+
/**
|
|
814
|
+
* Encodes a {@link Backend} into a string that will be used to uniquely identify {@link AI}
|
|
815
|
+
* instances by backend type.
|
|
816
|
+
*
|
|
817
|
+
* @internal
|
|
818
|
+
*/
|
|
819
|
+
function encodeInstanceIdentifier(backend) {
|
|
820
|
+
if (backend instanceof GoogleAIBackend) {
|
|
821
|
+
return `${AI_TYPE}/googleai`;
|
|
822
|
+
}
|
|
823
|
+
else if (backend instanceof VertexAIBackend) {
|
|
824
|
+
return `${AI_TYPE}/vertexai/${backend.location}`;
|
|
825
|
+
}
|
|
826
|
+
else {
|
|
827
|
+
throw new AIError(AIErrorCode.ERROR, `Invalid backend: ${JSON.stringify(backend.backendType)}`);
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
/**
|
|
831
|
+
* Decodes an instance identifier string into a {@link Backend}.
|
|
832
|
+
*
|
|
833
|
+
* @internal
|
|
834
|
+
*/
|
|
835
|
+
function decodeInstanceIdentifier(instanceIdentifier) {
|
|
836
|
+
const identifierParts = instanceIdentifier.split('/');
|
|
837
|
+
if (identifierParts[0] !== AI_TYPE) {
|
|
838
|
+
throw new AIError(AIErrorCode.ERROR, `Invalid instance identifier, unknown prefix '${identifierParts[0]}'`);
|
|
839
|
+
}
|
|
840
|
+
const backendType = identifierParts[1];
|
|
841
|
+
switch (backendType) {
|
|
842
|
+
case 'vertexai':
|
|
843
|
+
const location = identifierParts[2];
|
|
844
|
+
if (!location) {
|
|
845
|
+
throw new AIError(AIErrorCode.ERROR, `Invalid instance identifier, unknown location '${instanceIdentifier}'`);
|
|
846
|
+
}
|
|
847
|
+
return new VertexAIBackend(location);
|
|
848
|
+
case 'googleai':
|
|
849
|
+
return new GoogleAIBackend();
|
|
850
|
+
default:
|
|
851
|
+
throw new AIError(AIErrorCode.ERROR, `Invalid instance identifier string: '${instanceIdentifier}'`);
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
|
|
732
855
|
/**
|
|
733
856
|
* @license
|
|
734
857
|
* Copyright 2024 Google LLC
|
|
@@ -772,62 +895,6 @@ class AIService {
|
|
|
772
895
|
}
|
|
773
896
|
}
|
|
774
897
|
|
|
775
|
-
/**
|
|
776
|
-
* @license
|
|
777
|
-
* Copyright 2024 Google LLC
|
|
778
|
-
*
|
|
779
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
780
|
-
* you may not use this file except in compliance with the License.
|
|
781
|
-
* You may obtain a copy of the License at
|
|
782
|
-
*
|
|
783
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
784
|
-
*
|
|
785
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
786
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
787
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
788
|
-
* See the License for the specific language governing permissions and
|
|
789
|
-
* limitations under the License.
|
|
790
|
-
*/
|
|
791
|
-
/**
|
|
792
|
-
* Error class for the Firebase AI SDK.
|
|
793
|
-
*
|
|
794
|
-
* @public
|
|
795
|
-
*/
|
|
796
|
-
class AIError extends FirebaseError {
|
|
797
|
-
/**
|
|
798
|
-
* Constructs a new instance of the `AIError` class.
|
|
799
|
-
*
|
|
800
|
-
* @param code - The error code from {@link (AIErrorCode:type)}.
|
|
801
|
-
* @param message - A human-readable message describing the error.
|
|
802
|
-
* @param customErrorData - Optional error data.
|
|
803
|
-
*/
|
|
804
|
-
constructor(code, message, customErrorData) {
|
|
805
|
-
// Match error format used by FirebaseError from ErrorFactory
|
|
806
|
-
const service = AI_TYPE;
|
|
807
|
-
const fullCode = `${service}/${code}`;
|
|
808
|
-
const fullMessage = `${service}: ${message} (${fullCode})`;
|
|
809
|
-
super(code, fullMessage);
|
|
810
|
-
this.code = code;
|
|
811
|
-
this.customErrorData = customErrorData;
|
|
812
|
-
// FirebaseError initializes a stack trace, but it assumes the error is created from the error
|
|
813
|
-
// factory. Since we break this assumption, we set the stack trace to be originating from this
|
|
814
|
-
// constructor.
|
|
815
|
-
// This is only supported in V8.
|
|
816
|
-
if (Error.captureStackTrace) {
|
|
817
|
-
// Allows us to initialize the stack trace without including the constructor itself at the
|
|
818
|
-
// top level of the stack trace.
|
|
819
|
-
Error.captureStackTrace(this, AIError);
|
|
820
|
-
}
|
|
821
|
-
// Allows instanceof AIError in ES5/ES6
|
|
822
|
-
// https://github.com/Microsoft/TypeScript-wiki/blob/master/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work
|
|
823
|
-
// TODO(dlarocque): Replace this with `new.target`: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-2.html#support-for-newtarget
|
|
824
|
-
// which we can now use since we no longer target ES5.
|
|
825
|
-
Object.setPrototypeOf(this, AIError.prototype);
|
|
826
|
-
// Since Error is an interface, we don't inherit toString and so we define it ourselves.
|
|
827
|
-
this.toString = () => fullMessage;
|
|
828
|
-
}
|
|
829
|
-
}
|
|
830
|
-
|
|
831
898
|
/**
|
|
832
899
|
* @license
|
|
833
900
|
* Copyright 2025 Google LLC
|
|
@@ -844,46 +911,16 @@ class AIError extends FirebaseError {
|
|
|
844
911
|
* See the License for the specific language governing permissions and
|
|
845
912
|
* limitations under the License.
|
|
846
913
|
*/
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
*
|
|
851
|
-
* @internal
|
|
852
|
-
*/
|
|
853
|
-
function encodeInstanceIdentifier(backend) {
|
|
854
|
-
if (backend instanceof GoogleAIBackend) {
|
|
855
|
-
return `${AI_TYPE}/googleai`;
|
|
856
|
-
}
|
|
857
|
-
else if (backend instanceof VertexAIBackend) {
|
|
858
|
-
return `${AI_TYPE}/vertexai/${backend.location}`;
|
|
859
|
-
}
|
|
860
|
-
else {
|
|
861
|
-
throw new AIError(AIErrorCode.ERROR, `Invalid backend: ${JSON.stringify(backend.backendType)}`);
|
|
862
|
-
}
|
|
863
|
-
}
|
|
864
|
-
/**
|
|
865
|
-
* Decodes an instance identifier string into a {@link Backend}.
|
|
866
|
-
*
|
|
867
|
-
* @internal
|
|
868
|
-
*/
|
|
869
|
-
function decodeInstanceIdentifier(instanceIdentifier) {
|
|
870
|
-
const identifierParts = instanceIdentifier.split('/');
|
|
871
|
-
if (identifierParts[0] !== AI_TYPE) {
|
|
872
|
-
throw new AIError(AIErrorCode.ERROR, `Invalid instance identifier, unknown prefix '${identifierParts[0]}'`);
|
|
873
|
-
}
|
|
874
|
-
const backendType = identifierParts[1];
|
|
875
|
-
switch (backendType) {
|
|
876
|
-
case 'vertexai':
|
|
877
|
-
const location = identifierParts[2];
|
|
878
|
-
if (!location) {
|
|
879
|
-
throw new AIError(AIErrorCode.ERROR, `Invalid instance identifier, unknown location '${instanceIdentifier}'`);
|
|
880
|
-
}
|
|
881
|
-
return new VertexAIBackend(location);
|
|
882
|
-
case 'googleai':
|
|
883
|
-
return new GoogleAIBackend();
|
|
884
|
-
default:
|
|
885
|
-
throw new AIError(AIErrorCode.ERROR, `Invalid instance identifier string: '${instanceIdentifier}'`);
|
|
914
|
+
function factory(container, { instanceIdentifier }) {
|
|
915
|
+
if (!instanceIdentifier) {
|
|
916
|
+
throw new AIError(AIErrorCode.ERROR, 'AIService instance identifier is undefined.');
|
|
886
917
|
}
|
|
918
|
+
const backend = decodeInstanceIdentifier(instanceIdentifier);
|
|
919
|
+
// getImmediate for FirebaseApp will always succeed
|
|
920
|
+
const app = container.getProvider('app').getImmediate();
|
|
921
|
+
const auth = container.getProvider('auth-internal');
|
|
922
|
+
const appCheckProvider = container.getProvider('app-check-internal');
|
|
923
|
+
return new AIService(app, backend, auth, appCheckProvider);
|
|
887
924
|
}
|
|
888
925
|
|
|
889
926
|
/**
|
|
@@ -1269,7 +1306,7 @@ function hasValidCandidates(response) {
|
|
|
1269
1306
|
* Creates an EnhancedGenerateContentResponse object that has helper functions and
|
|
1270
1307
|
* other modifications that improve usability.
|
|
1271
1308
|
*/
|
|
1272
|
-
function createEnhancedContentResponse(response) {
|
|
1309
|
+
function createEnhancedContentResponse(response, inferenceSource = InferenceSource.IN_CLOUD) {
|
|
1273
1310
|
/**
|
|
1274
1311
|
* The Vertex AI backend omits default values.
|
|
1275
1312
|
* This causes the `index` property to be omitted from the first candidate in the
|
|
@@ -1280,6 +1317,7 @@ function createEnhancedContentResponse(response) {
|
|
|
1280
1317
|
response.candidates[0].index = 0;
|
|
1281
1318
|
}
|
|
1282
1319
|
const responseWithHelpers = addHelpers(response);
|
|
1320
|
+
responseWithHelpers.inferenceSource = inferenceSource;
|
|
1283
1321
|
return responseWithHelpers;
|
|
1284
1322
|
}
|
|
1285
1323
|
/**
|
|
@@ -1656,16 +1694,16 @@ const responseLineRE = /^data\: (.*)(?:\n\n|\r\r|\r\n\r\n)/;
|
|
|
1656
1694
|
*
|
|
1657
1695
|
* @param response - Response from a fetch call
|
|
1658
1696
|
*/
|
|
1659
|
-
function processStream(response, apiSettings) {
|
|
1697
|
+
function processStream(response, apiSettings, inferenceSource) {
|
|
1660
1698
|
const inputStream = response.body.pipeThrough(new TextDecoderStream('utf8', { fatal: true }));
|
|
1661
1699
|
const responseStream = getResponseStream(inputStream);
|
|
1662
1700
|
const [stream1, stream2] = responseStream.tee();
|
|
1663
1701
|
return {
|
|
1664
|
-
stream: generateResponseSequence(stream1, apiSettings),
|
|
1665
|
-
response: getResponsePromise(stream2, apiSettings)
|
|
1702
|
+
stream: generateResponseSequence(stream1, apiSettings, inferenceSource),
|
|
1703
|
+
response: getResponsePromise(stream2, apiSettings, inferenceSource)
|
|
1666
1704
|
};
|
|
1667
1705
|
}
|
|
1668
|
-
async function getResponsePromise(stream, apiSettings) {
|
|
1706
|
+
async function getResponsePromise(stream, apiSettings, inferenceSource) {
|
|
1669
1707
|
const allResponses = [];
|
|
1670
1708
|
const reader = stream.getReader();
|
|
1671
1709
|
while (true) {
|
|
@@ -1675,12 +1713,12 @@ async function getResponsePromise(stream, apiSettings) {
|
|
|
1675
1713
|
if (apiSettings.backend.backendType === BackendType.GOOGLE_AI) {
|
|
1676
1714
|
generateContentResponse = mapGenerateContentResponse(generateContentResponse);
|
|
1677
1715
|
}
|
|
1678
|
-
return createEnhancedContentResponse(generateContentResponse);
|
|
1716
|
+
return createEnhancedContentResponse(generateContentResponse, inferenceSource);
|
|
1679
1717
|
}
|
|
1680
1718
|
allResponses.push(value);
|
|
1681
1719
|
}
|
|
1682
1720
|
}
|
|
1683
|
-
async function* generateResponseSequence(stream, apiSettings) {
|
|
1721
|
+
async function* generateResponseSequence(stream, apiSettings, inferenceSource) {
|
|
1684
1722
|
const reader = stream.getReader();
|
|
1685
1723
|
while (true) {
|
|
1686
1724
|
const { value, done } = await reader.read();
|
|
@@ -1689,10 +1727,10 @@ async function* generateResponseSequence(stream, apiSettings) {
|
|
|
1689
1727
|
}
|
|
1690
1728
|
let enhancedResponse;
|
|
1691
1729
|
if (apiSettings.backend.backendType === BackendType.GOOGLE_AI) {
|
|
1692
|
-
enhancedResponse = createEnhancedContentResponse(mapGenerateContentResponse(value));
|
|
1730
|
+
enhancedResponse = createEnhancedContentResponse(mapGenerateContentResponse(value), inferenceSource);
|
|
1693
1731
|
}
|
|
1694
1732
|
else {
|
|
1695
|
-
enhancedResponse = createEnhancedContentResponse(value);
|
|
1733
|
+
enhancedResponse = createEnhancedContentResponse(value, inferenceSource);
|
|
1696
1734
|
}
|
|
1697
1735
|
const firstCandidate = enhancedResponse.candidates?.[0];
|
|
1698
1736
|
// Don't yield a response with no useful data for the developer.
|
|
@@ -1862,31 +1900,52 @@ const errorsCausingFallback = [
|
|
|
1862
1900
|
*/
|
|
1863
1901
|
async function callCloudOrDevice(request, chromeAdapter, onDeviceCall, inCloudCall) {
|
|
1864
1902
|
if (!chromeAdapter) {
|
|
1865
|
-
return
|
|
1903
|
+
return {
|
|
1904
|
+
response: await inCloudCall(),
|
|
1905
|
+
inferenceSource: InferenceSource.IN_CLOUD
|
|
1906
|
+
};
|
|
1866
1907
|
}
|
|
1867
1908
|
switch (chromeAdapter.mode) {
|
|
1868
1909
|
case InferenceMode.ONLY_ON_DEVICE:
|
|
1869
1910
|
if (await chromeAdapter.isAvailable(request)) {
|
|
1870
|
-
return
|
|
1911
|
+
return {
|
|
1912
|
+
response: await onDeviceCall(),
|
|
1913
|
+
inferenceSource: InferenceSource.ON_DEVICE
|
|
1914
|
+
};
|
|
1871
1915
|
}
|
|
1872
1916
|
throw new AIError(AIErrorCode.UNSUPPORTED, 'Inference mode is ONLY_ON_DEVICE, but an on-device model is not available.');
|
|
1873
1917
|
case InferenceMode.ONLY_IN_CLOUD:
|
|
1874
|
-
return
|
|
1918
|
+
return {
|
|
1919
|
+
response: await inCloudCall(),
|
|
1920
|
+
inferenceSource: InferenceSource.IN_CLOUD
|
|
1921
|
+
};
|
|
1875
1922
|
case InferenceMode.PREFER_IN_CLOUD:
|
|
1876
1923
|
try {
|
|
1877
|
-
return
|
|
1924
|
+
return {
|
|
1925
|
+
response: await inCloudCall(),
|
|
1926
|
+
inferenceSource: InferenceSource.IN_CLOUD
|
|
1927
|
+
};
|
|
1878
1928
|
}
|
|
1879
1929
|
catch (e) {
|
|
1880
1930
|
if (e instanceof AIError && errorsCausingFallback.includes(e.code)) {
|
|
1881
|
-
return
|
|
1931
|
+
return {
|
|
1932
|
+
response: await onDeviceCall(),
|
|
1933
|
+
inferenceSource: InferenceSource.ON_DEVICE
|
|
1934
|
+
};
|
|
1882
1935
|
}
|
|
1883
1936
|
throw e;
|
|
1884
1937
|
}
|
|
1885
1938
|
case InferenceMode.PREFER_ON_DEVICE:
|
|
1886
1939
|
if (await chromeAdapter.isAvailable(request)) {
|
|
1887
|
-
return
|
|
1940
|
+
return {
|
|
1941
|
+
response: await onDeviceCall(),
|
|
1942
|
+
inferenceSource: InferenceSource.ON_DEVICE
|
|
1943
|
+
};
|
|
1888
1944
|
}
|
|
1889
|
-
return
|
|
1945
|
+
return {
|
|
1946
|
+
response: await inCloudCall(),
|
|
1947
|
+
inferenceSource: InferenceSource.IN_CLOUD
|
|
1948
|
+
};
|
|
1890
1949
|
default:
|
|
1891
1950
|
throw new AIError(AIErrorCode.ERROR, `Unexpected infererence mode: ${chromeAdapter.mode}`);
|
|
1892
1951
|
}
|
|
@@ -1916,8 +1975,8 @@ async function generateContentStreamOnCloud(apiSettings, model, params, requestO
|
|
|
1916
1975
|
/* stream */ true, JSON.stringify(params), requestOptions);
|
|
1917
1976
|
}
|
|
1918
1977
|
async function generateContentStream(apiSettings, model, params, chromeAdapter, requestOptions) {
|
|
1919
|
-
const
|
|
1920
|
-
return processStream(response, apiSettings); // TODO: Map streaming responses
|
|
1978
|
+
const callResult = await callCloudOrDevice(params, chromeAdapter, () => chromeAdapter.generateContentStream(params), () => generateContentStreamOnCloud(apiSettings, model, params, requestOptions));
|
|
1979
|
+
return processStream(callResult.response, apiSettings); // TODO: Map streaming responses
|
|
1921
1980
|
}
|
|
1922
1981
|
async function generateContentOnCloud(apiSettings, model, params, requestOptions) {
|
|
1923
1982
|
if (apiSettings.backend.backendType === BackendType.GOOGLE_AI) {
|
|
@@ -1927,9 +1986,9 @@ async function generateContentOnCloud(apiSettings, model, params, requestOptions
|
|
|
1927
1986
|
/* stream */ false, JSON.stringify(params), requestOptions);
|
|
1928
1987
|
}
|
|
1929
1988
|
async function generateContent(apiSettings, model, params, chromeAdapter, requestOptions) {
|
|
1930
|
-
const
|
|
1931
|
-
const generateContentResponse = await processGenerateContentResponse(response, apiSettings);
|
|
1932
|
-
const enhancedResponse = createEnhancedContentResponse(generateContentResponse);
|
|
1989
|
+
const callResult = await callCloudOrDevice(params, chromeAdapter, () => chromeAdapter.generateContent(params), () => generateContentOnCloud(apiSettings, model, params, requestOptions));
|
|
1990
|
+
const generateContentResponse = await processGenerateContentResponse(callResult.response, apiSettings);
|
|
1991
|
+
const enhancedResponse = createEnhancedContentResponse(generateContentResponse, callResult.inferenceSource);
|
|
1933
1992
|
return {
|
|
1934
1993
|
response: enhancedResponse
|
|
1935
1994
|
};
|
|
@@ -2503,75 +2562,104 @@ class LiveSession {
|
|
|
2503
2562
|
this.webSocketHandler.send(JSON.stringify(message));
|
|
2504
2563
|
}
|
|
2505
2564
|
/**
|
|
2506
|
-
* Sends
|
|
2565
|
+
* Sends text to the server in realtime.
|
|
2507
2566
|
*
|
|
2508
|
-
* @
|
|
2567
|
+
* @example
|
|
2568
|
+
* ```javascript
|
|
2569
|
+
* liveSession.sendTextRealtime("Hello, how are you?");
|
|
2570
|
+
* ```
|
|
2571
|
+
*
|
|
2572
|
+
* @param text - The text data to send.
|
|
2509
2573
|
* @throws If this session has been closed.
|
|
2510
2574
|
*
|
|
2511
2575
|
* @beta
|
|
2512
2576
|
*/
|
|
2513
|
-
async
|
|
2577
|
+
async sendTextRealtime(text) {
|
|
2514
2578
|
if (this.isClosed) {
|
|
2515
2579
|
throw new AIError(AIErrorCode.REQUEST_ERROR, 'This LiveSession has been closed and cannot be used.');
|
|
2516
2580
|
}
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
this.webSocketHandler.send(JSON.stringify(message));
|
|
2524
|
-
});
|
|
2581
|
+
const message = {
|
|
2582
|
+
realtimeInput: {
|
|
2583
|
+
text
|
|
2584
|
+
}
|
|
2585
|
+
};
|
|
2586
|
+
this.webSocketHandler.send(JSON.stringify(message));
|
|
2525
2587
|
}
|
|
2526
2588
|
/**
|
|
2527
|
-
* Sends
|
|
2589
|
+
* Sends audio data to the server in realtime.
|
|
2528
2590
|
*
|
|
2529
|
-
* @
|
|
2591
|
+
* @remarks The server requires that the audio data is base64-encoded 16-bit PCM at 16kHz
|
|
2592
|
+
* little-endian.
|
|
2593
|
+
*
|
|
2594
|
+
* @example
|
|
2595
|
+
* ```javascript
|
|
2596
|
+
* // const pcmData = ... base64-encoded 16-bit PCM at 16kHz little-endian.
|
|
2597
|
+
* const blob = { mimeType: "audio/pcm", data: pcmData };
|
|
2598
|
+
* liveSession.sendAudioRealtime(blob);
|
|
2599
|
+
* ```
|
|
2600
|
+
*
|
|
2601
|
+
* @param blob - The base64-encoded PCM data to send to the server in realtime.
|
|
2530
2602
|
* @throws If this session has been closed.
|
|
2531
2603
|
*
|
|
2532
2604
|
* @beta
|
|
2533
2605
|
*/
|
|
2534
|
-
async
|
|
2606
|
+
async sendAudioRealtime(blob) {
|
|
2535
2607
|
if (this.isClosed) {
|
|
2536
2608
|
throw new AIError(AIErrorCode.REQUEST_ERROR, 'This LiveSession has been closed and cannot be used.');
|
|
2537
2609
|
}
|
|
2538
2610
|
const message = {
|
|
2539
|
-
|
|
2540
|
-
|
|
2611
|
+
realtimeInput: {
|
|
2612
|
+
audio: blob
|
|
2541
2613
|
}
|
|
2542
2614
|
};
|
|
2543
2615
|
this.webSocketHandler.send(JSON.stringify(message));
|
|
2544
2616
|
}
|
|
2545
2617
|
/**
|
|
2546
|
-
* Sends
|
|
2618
|
+
* Sends video data to the server in realtime.
|
|
2547
2619
|
*
|
|
2548
|
-
* @
|
|
2620
|
+
* @remarks The server requires that the video is sent as individual video frames at 1 FPS. It
|
|
2621
|
+
* is recommended to set `mimeType` to `image/jpeg`.
|
|
2622
|
+
*
|
|
2623
|
+
* @example
|
|
2624
|
+
* ```javascript
|
|
2625
|
+
* // const videoFrame = ... base64-encoded JPEG data
|
|
2626
|
+
* const blob = { mimeType: "image/jpeg", data: videoFrame };
|
|
2627
|
+
* liveSession.sendVideoRealtime(blob);
|
|
2628
|
+
* ```
|
|
2629
|
+
* @param blob - The base64-encoded video data to send to the server in realtime.
|
|
2549
2630
|
* @throws If this session has been closed.
|
|
2550
2631
|
*
|
|
2551
2632
|
* @beta
|
|
2552
2633
|
*/
|
|
2553
|
-
async
|
|
2634
|
+
async sendVideoRealtime(blob) {
|
|
2554
2635
|
if (this.isClosed) {
|
|
2555
2636
|
throw new AIError(AIErrorCode.REQUEST_ERROR, 'This LiveSession has been closed and cannot be used.');
|
|
2556
2637
|
}
|
|
2557
|
-
const
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
const { done, value } = await reader.read();
|
|
2561
|
-
if (done) {
|
|
2562
|
-
break;
|
|
2563
|
-
}
|
|
2564
|
-
else if (!value) {
|
|
2565
|
-
throw new Error('Missing chunk in reader, but reader is not done.');
|
|
2566
|
-
}
|
|
2567
|
-
await this.sendMediaChunks([value]);
|
|
2568
|
-
}
|
|
2569
|
-
catch (e) {
|
|
2570
|
-
// Re-throw any errors that occur during stream consumption or sending.
|
|
2571
|
-
const message = e instanceof Error ? e.message : 'Error processing media stream.';
|
|
2572
|
-
throw new AIError(AIErrorCode.REQUEST_ERROR, message);
|
|
2638
|
+
const message = {
|
|
2639
|
+
realtimeInput: {
|
|
2640
|
+
video: blob
|
|
2573
2641
|
}
|
|
2642
|
+
};
|
|
2643
|
+
this.webSocketHandler.send(JSON.stringify(message));
|
|
2644
|
+
}
|
|
2645
|
+
/**
|
|
2646
|
+
* Sends function responses to the server.
|
|
2647
|
+
*
|
|
2648
|
+
* @param functionResponses - The function responses to send.
|
|
2649
|
+
* @throws If this session has been closed.
|
|
2650
|
+
*
|
|
2651
|
+
* @beta
|
|
2652
|
+
*/
|
|
2653
|
+
async sendFunctionResponses(functionResponses) {
|
|
2654
|
+
if (this.isClosed) {
|
|
2655
|
+
throw new AIError(AIErrorCode.REQUEST_ERROR, 'This LiveSession has been closed and cannot be used.');
|
|
2574
2656
|
}
|
|
2657
|
+
const message = {
|
|
2658
|
+
toolResponse: {
|
|
2659
|
+
functionResponses
|
|
2660
|
+
}
|
|
2661
|
+
};
|
|
2662
|
+
this.webSocketHandler.send(JSON.stringify(message));
|
|
2575
2663
|
}
|
|
2576
2664
|
/**
|
|
2577
2665
|
* Yields messages received from the server.
|
|
@@ -2629,6 +2717,62 @@ class LiveSession {
|
|
|
2629
2717
|
await this.webSocketHandler.close(1000, 'Client closed session.');
|
|
2630
2718
|
}
|
|
2631
2719
|
}
|
|
2720
|
+
/**
|
|
2721
|
+
* Sends realtime input to the server.
|
|
2722
|
+
*
|
|
2723
|
+
* @deprecated Use `sendTextRealtime()`, `sendAudioRealtime()`, and `sendVideoRealtime()` instead.
|
|
2724
|
+
*
|
|
2725
|
+
* @param mediaChunks - The media chunks to send.
|
|
2726
|
+
* @throws If this session has been closed.
|
|
2727
|
+
*
|
|
2728
|
+
* @beta
|
|
2729
|
+
*/
|
|
2730
|
+
async sendMediaChunks(mediaChunks) {
|
|
2731
|
+
if (this.isClosed) {
|
|
2732
|
+
throw new AIError(AIErrorCode.REQUEST_ERROR, 'This LiveSession has been closed and cannot be used.');
|
|
2733
|
+
}
|
|
2734
|
+
// The backend does not support sending more than one mediaChunk in one message.
|
|
2735
|
+
// Work around this limitation by sending mediaChunks in separate messages.
|
|
2736
|
+
mediaChunks.forEach(mediaChunk => {
|
|
2737
|
+
const message = {
|
|
2738
|
+
realtimeInput: { mediaChunks: [mediaChunk] }
|
|
2739
|
+
};
|
|
2740
|
+
this.webSocketHandler.send(JSON.stringify(message));
|
|
2741
|
+
});
|
|
2742
|
+
}
|
|
2743
|
+
/**
|
|
2744
|
+
* @deprecated Use `sendTextRealtime()`, `sendAudioRealtime()`, and `sendVideoRealtime()` instead.
|
|
2745
|
+
*
|
|
2746
|
+
* Sends a stream of {@link GenerativeContentBlob}.
|
|
2747
|
+
*
|
|
2748
|
+
* @param mediaChunkStream - The stream of {@link GenerativeContentBlob} to send.
|
|
2749
|
+
* @throws If this session has been closed.
|
|
2750
|
+
*
|
|
2751
|
+
* @beta
|
|
2752
|
+
*/
|
|
2753
|
+
async sendMediaStream(mediaChunkStream) {
|
|
2754
|
+
if (this.isClosed) {
|
|
2755
|
+
throw new AIError(AIErrorCode.REQUEST_ERROR, 'This LiveSession has been closed and cannot be used.');
|
|
2756
|
+
}
|
|
2757
|
+
const reader = mediaChunkStream.getReader();
|
|
2758
|
+
while (true) {
|
|
2759
|
+
try {
|
|
2760
|
+
const { done, value } = await reader.read();
|
|
2761
|
+
if (done) {
|
|
2762
|
+
break;
|
|
2763
|
+
}
|
|
2764
|
+
else if (!value) {
|
|
2765
|
+
throw new Error('Missing chunk in reader, but reader is not done.');
|
|
2766
|
+
}
|
|
2767
|
+
await this.sendMediaChunks([value]);
|
|
2768
|
+
}
|
|
2769
|
+
catch (e) {
|
|
2770
|
+
// Re-throw any errors that occur during stream consumption or sending.
|
|
2771
|
+
const message = e instanceof Error ? e.message : 'Error processing media stream.';
|
|
2772
|
+
throw new AIError(AIErrorCode.REQUEST_ERROR, message);
|
|
2773
|
+
}
|
|
2774
|
+
}
|
|
2775
|
+
}
|
|
2632
2776
|
}
|
|
2633
2777
|
|
|
2634
2778
|
/**
|
|
@@ -2689,13 +2833,18 @@ class LiveGenerativeModel extends AIModel {
|
|
|
2689
2833
|
else {
|
|
2690
2834
|
fullModelPath = `projects/${this._apiSettings.project}/locations/${this._apiSettings.location}/${this.model}`;
|
|
2691
2835
|
}
|
|
2836
|
+
// inputAudioTranscription and outputAudioTranscription are on the generation config in the public API,
|
|
2837
|
+
// but the backend expects them to be in the `setup` message.
|
|
2838
|
+
const { inputAudioTranscription, outputAudioTranscription, ...generationConfig } = this.generationConfig;
|
|
2692
2839
|
const setupMessage = {
|
|
2693
2840
|
setup: {
|
|
2694
2841
|
model: fullModelPath,
|
|
2695
|
-
generationConfig
|
|
2842
|
+
generationConfig,
|
|
2696
2843
|
tools: this.tools,
|
|
2697
2844
|
toolConfig: this.toolConfig,
|
|
2698
|
-
systemInstruction: this.systemInstruction
|
|
2845
|
+
systemInstruction: this.systemInstruction,
|
|
2846
|
+
inputAudioTranscription,
|
|
2847
|
+
outputAudioTranscription
|
|
2699
2848
|
}
|
|
2700
2849
|
};
|
|
2701
2850
|
try {
|
|
@@ -3401,7 +3550,7 @@ class AudioConversationRunner {
|
|
|
3401
3550
|
mimeType: 'audio/pcm',
|
|
3402
3551
|
data: base64
|
|
3403
3552
|
};
|
|
3404
|
-
void this.liveSession.
|
|
3553
|
+
void this.liveSession.sendAudioRealtime(chunk);
|
|
3405
3554
|
};
|
|
3406
3555
|
}
|
|
3407
3556
|
/**
|
|
@@ -3779,22 +3928,12 @@ function getLiveGenerativeModel(ai, modelParams) {
|
|
|
3779
3928
|
* @packageDocumentation
|
|
3780
3929
|
*/
|
|
3781
3930
|
function registerAI() {
|
|
3782
|
-
_registerComponent(new Component(AI_TYPE,
|
|
3783
|
-
if (!instanceIdentifier) {
|
|
3784
|
-
throw new AIError(AIErrorCode.ERROR, 'AIService instance identifier is undefined.');
|
|
3785
|
-
}
|
|
3786
|
-
const backend = decodeInstanceIdentifier(instanceIdentifier);
|
|
3787
|
-
// getImmediate for FirebaseApp will always succeed
|
|
3788
|
-
const app = container.getProvider('app').getImmediate();
|
|
3789
|
-
const auth = container.getProvider('auth-internal');
|
|
3790
|
-
const appCheckProvider = container.getProvider('app-check-internal');
|
|
3791
|
-
return new AIService(app, backend, auth, appCheckProvider);
|
|
3792
|
-
}, "PUBLIC" /* ComponentType.PUBLIC */).setMultipleInstances(true));
|
|
3931
|
+
_registerComponent(new Component(AI_TYPE, factory, "PUBLIC" /* ComponentType.PUBLIC */).setMultipleInstances(true));
|
|
3793
3932
|
registerVersion(name, version, 'node');
|
|
3794
3933
|
// BUILD_TARGET will be replaced by values like esm, cjs, etc during the compilation
|
|
3795
3934
|
registerVersion(name, version, 'esm2020');
|
|
3796
3935
|
}
|
|
3797
3936
|
registerAI();
|
|
3798
3937
|
|
|
3799
|
-
export { AIError, AIErrorCode, AIModel, AnyOfSchema, ArraySchema, Backend, BackendType, BlockReason, BooleanSchema, ChatSession, FinishReason, FunctionCallingMode, GenerativeModel, GoogleAIBackend, HarmBlockMethod, HarmBlockThreshold, HarmCategory, HarmProbability, HarmSeverity, ImagenAspectRatio, ImagenImageFormat, ImagenModel, ImagenPersonFilterLevel, ImagenSafetyFilterLevel, InferenceMode, IntegerSchema, Language, LiveGenerativeModel, LiveResponseType, LiveSession, Modality, NumberSchema, ObjectSchema, Outcome, POSSIBLE_ROLES, ResponseModality, Schema, SchemaType, StringSchema, URLRetrievalStatus, VertexAIBackend, getAI, getGenerativeModel, getImagenModel, getLiveGenerativeModel, startAudioConversation };
|
|
3938
|
+
export { AIError, AIErrorCode, AIModel, AnyOfSchema, ArraySchema, Backend, BackendType, BlockReason, BooleanSchema, ChatSession, 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, URLRetrievalStatus, VertexAIBackend, getAI, getGenerativeModel, getImagenModel, getLiveGenerativeModel, startAudioConversation };
|
|
3800
3939
|
//# sourceMappingURL=index.node.mjs.map
|