@firebase/ai 2.1.0-canary.44d8d742f → 2.1.0-canary.84b8bed35

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/index.cjs.js CHANGED
@@ -8,7 +8,7 @@ var util = require('@firebase/util');
8
8
  var logger$1 = require('@firebase/logger');
9
9
 
10
10
  var name = "@firebase/ai";
11
- var version = "2.1.0-canary.44d8d742f";
11
+ var version = "2.1.0-canary.84b8bed35";
12
12
 
13
13
  /**
14
14
  * @license
@@ -657,6 +657,12 @@ class AIService {
657
657
  _delete() {
658
658
  return Promise.resolve();
659
659
  }
660
+ set options(optionsToSet) {
661
+ this._options = optionsToSet;
662
+ }
663
+ get options() {
664
+ return this._options;
665
+ }
660
666
  }
661
667
 
662
668
  /**
@@ -841,7 +847,12 @@ class AIModel {
841
847
  };
842
848
  }
843
849
  else if (ai.appCheck) {
844
- this._apiSettings.getAppCheckToken = () => ai.appCheck.getToken();
850
+ if (ai.options?.useLimitedUseAppCheckTokens) {
851
+ this._apiSettings.getAppCheckToken = () => ai.appCheck.getLimitedUseToken();
852
+ }
853
+ else {
854
+ this._apiSettings.getAppCheckToken = () => ai.appCheck.getToken();
855
+ }
845
856
  }
846
857
  if (ai.auth) {
847
858
  this._apiSettings.getAuthToken = () => ai.auth.getToken();
@@ -1104,6 +1115,28 @@ async function makeRequest(model, task, apiSettings, stream, body, requestOption
1104
1115
  * See the License for the specific language governing permissions and
1105
1116
  * limitations under the License.
1106
1117
  */
1118
+ /**
1119
+ * Check that at least one candidate exists and does not have a bad
1120
+ * finish reason. Warns if multiple candidates exist.
1121
+ */
1122
+ function hasValidCandidates(response) {
1123
+ if (response.candidates && response.candidates.length > 0) {
1124
+ if (response.candidates.length > 1) {
1125
+ logger.warn(`This response had ${response.candidates.length} ` +
1126
+ `candidates. Returning text from the first candidate only. ` +
1127
+ `Access response.candidates directly to use the other candidates.`);
1128
+ }
1129
+ if (hadBadFinishReason(response.candidates[0])) {
1130
+ throw new AIError(AIErrorCode.RESPONSE_ERROR, `Response error: ${formatBlockErrorMessage(response)}. Response body stored in error.response`, {
1131
+ response
1132
+ });
1133
+ }
1134
+ return true;
1135
+ }
1136
+ else {
1137
+ return false;
1138
+ }
1139
+ }
1107
1140
  /**
1108
1141
  * Creates an EnhancedGenerateContentResponse object that has helper functions and
1109
1142
  * other modifications that improve usability.
@@ -1127,18 +1160,8 @@ function createEnhancedContentResponse(response) {
1127
1160
  */
1128
1161
  function addHelpers(response) {
1129
1162
  response.text = () => {
1130
- if (response.candidates && response.candidates.length > 0) {
1131
- if (response.candidates.length > 1) {
1132
- logger.warn(`This response had ${response.candidates.length} ` +
1133
- `candidates. Returning text from the first candidate only. ` +
1134
- `Access response.candidates directly to use the other candidates.`);
1135
- }
1136
- if (hadBadFinishReason(response.candidates[0])) {
1137
- throw new AIError(AIErrorCode.RESPONSE_ERROR, `Response error: ${formatBlockErrorMessage(response)}. Response body stored in error.response`, {
1138
- response
1139
- });
1140
- }
1141
- return getText(response);
1163
+ if (hasValidCandidates(response)) {
1164
+ return getText(response, part => !part.thought);
1142
1165
  }
1143
1166
  else if (response.promptFeedback) {
1144
1167
  throw new AIError(AIErrorCode.RESPONSE_ERROR, `Text not available. ${formatBlockErrorMessage(response)}`, {
@@ -1147,18 +1170,20 @@ function addHelpers(response) {
1147
1170
  }
1148
1171
  return '';
1149
1172
  };
1173
+ response.thoughtSummary = () => {
1174
+ if (hasValidCandidates(response)) {
1175
+ const result = getText(response, part => !!part.thought);
1176
+ return result === '' ? undefined : result;
1177
+ }
1178
+ else if (response.promptFeedback) {
1179
+ throw new AIError(AIErrorCode.RESPONSE_ERROR, `Thought summary not available. ${formatBlockErrorMessage(response)}`, {
1180
+ response
1181
+ });
1182
+ }
1183
+ return undefined;
1184
+ };
1150
1185
  response.inlineDataParts = () => {
1151
- if (response.candidates && response.candidates.length > 0) {
1152
- if (response.candidates.length > 1) {
1153
- logger.warn(`This response had ${response.candidates.length} ` +
1154
- `candidates. Returning data from the first candidate only. ` +
1155
- `Access response.candidates directly to use the other candidates.`);
1156
- }
1157
- if (hadBadFinishReason(response.candidates[0])) {
1158
- throw new AIError(AIErrorCode.RESPONSE_ERROR, `Response error: ${formatBlockErrorMessage(response)}. Response body stored in error.response`, {
1159
- response
1160
- });
1161
- }
1186
+ if (hasValidCandidates(response)) {
1162
1187
  return getInlineDataParts(response);
1163
1188
  }
1164
1189
  else if (response.promptFeedback) {
@@ -1169,17 +1194,7 @@ function addHelpers(response) {
1169
1194
  return undefined;
1170
1195
  };
1171
1196
  response.functionCalls = () => {
1172
- if (response.candidates && response.candidates.length > 0) {
1173
- if (response.candidates.length > 1) {
1174
- logger.warn(`This response had ${response.candidates.length} ` +
1175
- `candidates. Returning function calls from the first candidate only. ` +
1176
- `Access response.candidates directly to use the other candidates.`);
1177
- }
1178
- if (hadBadFinishReason(response.candidates[0])) {
1179
- throw new AIError(AIErrorCode.RESPONSE_ERROR, `Response error: ${formatBlockErrorMessage(response)}. Response body stored in error.response`, {
1180
- response
1181
- });
1182
- }
1197
+ if (hasValidCandidates(response)) {
1183
1198
  return getFunctionCalls(response);
1184
1199
  }
1185
1200
  else if (response.promptFeedback) {
@@ -1192,13 +1207,17 @@ function addHelpers(response) {
1192
1207
  return response;
1193
1208
  }
1194
1209
  /**
1195
- * Returns all text found in all parts of first candidate.
1210
+ * Returns all text from the first candidate's parts, filtering by whether
1211
+ * `partFilter()` returns true.
1212
+ *
1213
+ * @param response - The `GenerateContentResponse` from which to extract text.
1214
+ * @param partFilter - Only return `Part`s for which this returns true
1196
1215
  */
1197
- function getText(response) {
1216
+ function getText(response, partFilter) {
1198
1217
  const textStrings = [];
1199
1218
  if (response.candidates?.[0].content?.parts) {
1200
1219
  for (const part of response.candidates?.[0].content?.parts) {
1201
- if (part.text) {
1220
+ if (part.text && partFilter(part)) {
1202
1221
  textStrings.push(part.text);
1203
1222
  }
1204
1223
  }
@@ -1211,7 +1230,7 @@ function getText(response) {
1211
1230
  }
1212
1231
  }
1213
1232
  /**
1214
- * Returns {@link FunctionCall}s associated with first candidate.
1233
+ * Returns every {@link FunctionCall} associated with first candidate.
1215
1234
  */
1216
1235
  function getFunctionCalls(response) {
1217
1236
  const functionCalls = [];
@@ -1230,7 +1249,7 @@ function getFunctionCalls(response) {
1230
1249
  }
1231
1250
  }
1232
1251
  /**
1233
- * Returns {@link InlineDataPart}s in the first candidate if present.
1252
+ * Returns every {@link InlineDataPart} in the first candidate if present.
1234
1253
  *
1235
1254
  * @internal
1236
1255
  */
@@ -1310,7 +1329,7 @@ async function handlePredictResponse(response) {
1310
1329
  });
1311
1330
  }
1312
1331
  else {
1313
- throw new AIError(AIErrorCode.RESPONSE_ERROR, `Predictions array in response has missing properties. Response: ${JSON.stringify(responseJson)}`);
1332
+ throw new AIError(AIErrorCode.RESPONSE_ERROR, `Unexpected element in 'predictions' array in response: '${JSON.stringify(prediction)}'`);
1314
1333
  }
1315
1334
  }
1316
1335
  return { images, filteredReason };
@@ -1878,12 +1897,14 @@ const VALID_PART_FIELDS = [
1878
1897
  'text',
1879
1898
  'inlineData',
1880
1899
  'functionCall',
1881
- 'functionResponse'
1900
+ 'functionResponse',
1901
+ 'thought',
1902
+ 'thoughtSignature'
1882
1903
  ];
1883
1904
  const VALID_PARTS_PER_ROLE = {
1884
1905
  user: ['text', 'inlineData'],
1885
1906
  function: ['functionResponse'],
1886
- model: ['text', 'functionCall'],
1907
+ model: ['text', 'functionCall', 'thought', 'thoughtSignature'],
1887
1908
  // System instructions shouldn't be in history anyway.
1888
1909
  system: ['text']
1889
1910
  };
@@ -1905,7 +1926,7 @@ function validateChatHistory(history) {
1905
1926
  throw new AIError(AIErrorCode.INVALID_CONTENT, `Each item should include role field. Got ${role} but valid roles are: ${JSON.stringify(POSSIBLE_ROLES)}`);
1906
1927
  }
1907
1928
  if (!Array.isArray(parts)) {
1908
- throw new AIError(AIErrorCode.INVALID_CONTENT, `Content should have 'parts' but property with an array of Parts`);
1929
+ throw new AIError(AIErrorCode.INVALID_CONTENT, `Content should have 'parts' property with an array of Parts`);
1909
1930
  }
1910
1931
  if (parts.length === 0) {
1911
1932
  throw new AIError(AIErrorCode.INVALID_CONTENT, `Each Content should have at least one part`);
@@ -1914,7 +1935,9 @@ function validateChatHistory(history) {
1914
1935
  text: 0,
1915
1936
  inlineData: 0,
1916
1937
  functionCall: 0,
1917
- functionResponse: 0
1938
+ functionResponse: 0,
1939
+ thought: 0,
1940
+ thoughtSignature: 0
1918
1941
  };
1919
1942
  for (const part of parts) {
1920
1943
  for (const key of VALID_PART_FIELDS) {
@@ -2373,17 +2396,17 @@ class ChromeAdapterImpl {
2373
2396
  /**
2374
2397
  * Checks if a given request can be made on-device.
2375
2398
  *
2376
- * <ol>Encapsulates a few concerns:
2377
- * <li>the mode</li>
2378
- * <li>API existence</li>
2379
- * <li>prompt formatting</li>
2380
- * <li>model availability, including triggering download if necessary</li>
2381
- * </ol>
2399
+ * Encapsulates a few concerns:
2400
+ * the mode
2401
+ * API existence
2402
+ * prompt formatting
2403
+ * model availability, including triggering download if necessary
2382
2404
  *
2383
- * <p>Pros: callers needn't be concerned with details of on-device availability.</p>
2384
- * <p>Cons: this method spans a few concerns and splits request validation from usage.
2405
+ *
2406
+ * Pros: callers needn't be concerned with details of on-device availability.</p>
2407
+ * Cons: this method spans a few concerns and splits request validation from usage.
2385
2408
  * If instance variables weren't already part of the API, we could consider a better
2386
- * separation of concerns.</p>
2409
+ * separation of concerns.
2387
2410
  */
2388
2411
  async isAvailable(request) {
2389
2412
  if (!this.mode) {
@@ -2424,8 +2447,9 @@ class ChromeAdapterImpl {
2424
2447
  /**
2425
2448
  * Generates content on device.
2426
2449
  *
2427
- * <p>This is comparable to {@link GenerativeModel.generateContent} for generating content in
2428
- * Cloud.</p>
2450
+ * @remarks
2451
+ * This is comparable to {@link GenerativeModel.generateContent} for generating content in
2452
+ * Cloud.
2429
2453
  * @param request - a standard Firebase AI {@link GenerateContentRequest}
2430
2454
  * @returns {@link Response}, so we can reuse common response formatting.
2431
2455
  */
@@ -2438,8 +2462,9 @@ class ChromeAdapterImpl {
2438
2462
  /**
2439
2463
  * Generates content stream on device.
2440
2464
  *
2441
- * <p>This is comparable to {@link GenerativeModel.generateContentStream} for generating content in
2442
- * Cloud.</p>
2465
+ * @remarks
2466
+ * This is comparable to {@link GenerativeModel.generateContentStream} for generating content in
2467
+ * Cloud.
2443
2468
  * @param request - a standard Firebase AI {@link GenerateContentRequest}
2444
2469
  * @returns {@link Response}, so we can reuse common response formatting.
2445
2470
  */
@@ -2490,11 +2515,11 @@ class ChromeAdapterImpl {
2490
2515
  /**
2491
2516
  * Triggers out-of-band download of an on-device model.
2492
2517
  *
2493
- * <p>Chrome only downloads models as needed. Chrome knows a model is needed when code calls
2494
- * LanguageModel.create.</p>
2518
+ * Chrome only downloads models as needed. Chrome knows a model is needed when code calls
2519
+ * LanguageModel.create.
2495
2520
  *
2496
- * <p>Since Chrome manages the download, the SDK can only avoid redundant download requests by
2497
- * tracking if a download has previously been requested.</p>
2521
+ * Since Chrome manages the download, the SDK can only avoid redundant download requests by
2522
+ * tracking if a download has previously been requested.
2498
2523
  */
2499
2524
  download() {
2500
2525
  if (this.isDownloading) {
@@ -2548,12 +2573,12 @@ class ChromeAdapterImpl {
2548
2573
  /**
2549
2574
  * Abstracts Chrome session creation.
2550
2575
  *
2551
- * <p>Chrome uses a multi-turn session for all inference. Firebase AI uses single-turn for all
2576
+ * Chrome uses a multi-turn session for all inference. Firebase AI uses single-turn for all
2552
2577
  * inference. To map the Firebase AI API to Chrome's API, the SDK creates a new session for all
2553
- * inference.</p>
2578
+ * inference.
2554
2579
  *
2555
- * <p>Chrome will remove a model from memory if it's no longer in use, so this method ensures a
2556
- * new session is created before an old session is destroyed.</p>
2580
+ * Chrome will remove a model from memory if it's no longer in use, so this method ensures a
2581
+ * new session is created before an old session is destroyed.
2557
2582
  */
2558
2583
  async createSession() {
2559
2584
  if (!this.languageModelProvider) {
@@ -2961,14 +2986,20 @@ class ImagenImageFormat {
2961
2986
  *
2962
2987
  * @public
2963
2988
  */
2964
- function getAI(app$1 = app.getApp(), options = { backend: new GoogleAIBackend() }) {
2989
+ function getAI(app$1 = app.getApp(), options) {
2965
2990
  app$1 = util.getModularInstance(app$1);
2966
2991
  // Dependencies
2967
2992
  const AIProvider = app._getProvider(app$1, AI_TYPE);
2968
- const identifier = encodeInstanceIdentifier(options.backend);
2969
- return AIProvider.getImmediate({
2993
+ const backend = options?.backend ?? new GoogleAIBackend();
2994
+ const finalOptions = {
2995
+ useLimitedUseAppCheckTokens: options?.useLimitedUseAppCheckTokens ?? false
2996
+ };
2997
+ const identifier = encodeInstanceIdentifier(backend);
2998
+ const aiInstance = AIProvider.getImmediate({
2970
2999
  identifier
2971
3000
  });
3001
+ aiInstance.options = finalOptions;
3002
+ return aiInstance;
2972
3003
  }
2973
3004
  /**
2974
3005
  * Returns a {@link GenerativeModel} class with methods for inference
@@ -3024,18 +3055,19 @@ function getImagenModel(ai, modelParams, requestOptions) {
3024
3055
  *
3025
3056
  * @packageDocumentation
3026
3057
  */
3058
+ function factory(container, { instanceIdentifier }) {
3059
+ if (!instanceIdentifier) {
3060
+ throw new AIError(AIErrorCode.ERROR, 'AIService instance identifier is undefined.');
3061
+ }
3062
+ const backend = decodeInstanceIdentifier(instanceIdentifier);
3063
+ // getImmediate for FirebaseApp will always succeed
3064
+ const app = container.getProvider('app').getImmediate();
3065
+ const auth = container.getProvider('auth-internal');
3066
+ const appCheckProvider = container.getProvider('app-check-internal');
3067
+ return new AIService(app, backend, auth, appCheckProvider);
3068
+ }
3027
3069
  function registerAI() {
3028
- app._registerComponent(new component.Component(AI_TYPE, (container, { instanceIdentifier }) => {
3029
- if (!instanceIdentifier) {
3030
- throw new AIError(AIErrorCode.ERROR, 'AIService instance identifier is undefined.');
3031
- }
3032
- const backend = decodeInstanceIdentifier(instanceIdentifier);
3033
- // getImmediate for FirebaseApp will always succeed
3034
- const app = container.getProvider('app').getImmediate();
3035
- const auth = container.getProvider('auth-internal');
3036
- const appCheckProvider = container.getProvider('app-check-internal');
3037
- return new AIService(app, backend, auth, appCheckProvider);
3038
- }, "PUBLIC" /* ComponentType.PUBLIC */).setMultipleInstances(true));
3070
+ app._registerComponent(new component.Component(AI_TYPE, factory, "PUBLIC" /* ComponentType.PUBLIC */).setMultipleInstances(true));
3039
3071
  app.registerVersion(name, version);
3040
3072
  // BUILD_TARGET will be replaced by values like esm, cjs, etc during the compilation
3041
3073
  app.registerVersion(name, version, 'cjs2020');
@@ -3077,6 +3109,7 @@ exports.Schema = Schema;
3077
3109
  exports.SchemaType = SchemaType;
3078
3110
  exports.StringSchema = StringSchema;
3079
3111
  exports.VertexAIBackend = VertexAIBackend;
3112
+ exports.factory = factory;
3080
3113
  exports.getAI = getAI;
3081
3114
  exports.getGenerativeModel = getGenerativeModel;
3082
3115
  exports.getImagenModel = getImagenModel;