@firebase/ai 2.1.0-canary.5501791d0 → 2.1.0-canary.9b63cd60e

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.
@@ -4,7 +4,7 @@ import { FirebaseError, getModularInstance } from '@firebase/util';
4
4
  import { Logger } from '@firebase/logger';
5
5
 
6
6
  var name = "@firebase/ai";
7
- var version = "2.1.0-canary.5501791d0";
7
+ var version = "2.1.0-canary.9b63cd60e";
8
8
 
9
9
  /**
10
10
  * @license
@@ -653,6 +653,12 @@ class AIService {
653
653
  _delete() {
654
654
  return Promise.resolve();
655
655
  }
656
+ set options(optionsToSet) {
657
+ this._options = optionsToSet;
658
+ }
659
+ get options() {
660
+ return this._options;
661
+ }
656
662
  }
657
663
 
658
664
  /**
@@ -837,7 +843,12 @@ class AIModel {
837
843
  };
838
844
  }
839
845
  else if (ai.appCheck) {
840
- this._apiSettings.getAppCheckToken = () => ai.appCheck.getToken();
846
+ if (ai.options?.useLimitedUseAppCheckTokens) {
847
+ this._apiSettings.getAppCheckToken = () => ai.appCheck.getLimitedUseToken();
848
+ }
849
+ else {
850
+ this._apiSettings.getAppCheckToken = () => ai.appCheck.getToken();
851
+ }
841
852
  }
842
853
  if (ai.auth) {
843
854
  this._apiSettings.getAuthToken = () => ai.auth.getToken();
@@ -1100,6 +1111,28 @@ async function makeRequest(model, task, apiSettings, stream, body, requestOption
1100
1111
  * See the License for the specific language governing permissions and
1101
1112
  * limitations under the License.
1102
1113
  */
1114
+ /**
1115
+ * Check that at least one candidate exists and does not have a bad
1116
+ * finish reason. Warns if multiple candidates exist.
1117
+ */
1118
+ function hasValidCandidates(response) {
1119
+ if (response.candidates && response.candidates.length > 0) {
1120
+ if (response.candidates.length > 1) {
1121
+ logger.warn(`This response had ${response.candidates.length} ` +
1122
+ `candidates. Returning text from the first candidate only. ` +
1123
+ `Access response.candidates directly to use the other candidates.`);
1124
+ }
1125
+ if (hadBadFinishReason(response.candidates[0])) {
1126
+ throw new AIError(AIErrorCode.RESPONSE_ERROR, `Response error: ${formatBlockErrorMessage(response)}. Response body stored in error.response`, {
1127
+ response
1128
+ });
1129
+ }
1130
+ return true;
1131
+ }
1132
+ else {
1133
+ return false;
1134
+ }
1135
+ }
1103
1136
  /**
1104
1137
  * Creates an EnhancedGenerateContentResponse object that has helper functions and
1105
1138
  * other modifications that improve usability.
@@ -1123,18 +1156,8 @@ function createEnhancedContentResponse(response) {
1123
1156
  */
1124
1157
  function addHelpers(response) {
1125
1158
  response.text = () => {
1126
- if (response.candidates && response.candidates.length > 0) {
1127
- if (response.candidates.length > 1) {
1128
- logger.warn(`This response had ${response.candidates.length} ` +
1129
- `candidates. Returning text from the first candidate only. ` +
1130
- `Access response.candidates directly to use the other candidates.`);
1131
- }
1132
- if (hadBadFinishReason(response.candidates[0])) {
1133
- throw new AIError(AIErrorCode.RESPONSE_ERROR, `Response error: ${formatBlockErrorMessage(response)}. Response body stored in error.response`, {
1134
- response
1135
- });
1136
- }
1137
- return getText(response);
1159
+ if (hasValidCandidates(response)) {
1160
+ return getText(response, part => !part.thought);
1138
1161
  }
1139
1162
  else if (response.promptFeedback) {
1140
1163
  throw new AIError(AIErrorCode.RESPONSE_ERROR, `Text not available. ${formatBlockErrorMessage(response)}`, {
@@ -1143,18 +1166,20 @@ function addHelpers(response) {
1143
1166
  }
1144
1167
  return '';
1145
1168
  };
1169
+ response.thoughtSummary = () => {
1170
+ if (hasValidCandidates(response)) {
1171
+ const result = getText(response, part => !!part.thought);
1172
+ return result === '' ? undefined : result;
1173
+ }
1174
+ else if (response.promptFeedback) {
1175
+ throw new AIError(AIErrorCode.RESPONSE_ERROR, `Thought summary not available. ${formatBlockErrorMessage(response)}`, {
1176
+ response
1177
+ });
1178
+ }
1179
+ return undefined;
1180
+ };
1146
1181
  response.inlineDataParts = () => {
1147
- if (response.candidates && response.candidates.length > 0) {
1148
- if (response.candidates.length > 1) {
1149
- logger.warn(`This response had ${response.candidates.length} ` +
1150
- `candidates. Returning data from the first candidate only. ` +
1151
- `Access response.candidates directly to use the other candidates.`);
1152
- }
1153
- if (hadBadFinishReason(response.candidates[0])) {
1154
- throw new AIError(AIErrorCode.RESPONSE_ERROR, `Response error: ${formatBlockErrorMessage(response)}. Response body stored in error.response`, {
1155
- response
1156
- });
1157
- }
1182
+ if (hasValidCandidates(response)) {
1158
1183
  return getInlineDataParts(response);
1159
1184
  }
1160
1185
  else if (response.promptFeedback) {
@@ -1165,17 +1190,7 @@ function addHelpers(response) {
1165
1190
  return undefined;
1166
1191
  };
1167
1192
  response.functionCalls = () => {
1168
- if (response.candidates && response.candidates.length > 0) {
1169
- if (response.candidates.length > 1) {
1170
- logger.warn(`This response had ${response.candidates.length} ` +
1171
- `candidates. Returning function calls from the first candidate only. ` +
1172
- `Access response.candidates directly to use the other candidates.`);
1173
- }
1174
- if (hadBadFinishReason(response.candidates[0])) {
1175
- throw new AIError(AIErrorCode.RESPONSE_ERROR, `Response error: ${formatBlockErrorMessage(response)}. Response body stored in error.response`, {
1176
- response
1177
- });
1178
- }
1193
+ if (hasValidCandidates(response)) {
1179
1194
  return getFunctionCalls(response);
1180
1195
  }
1181
1196
  else if (response.promptFeedback) {
@@ -1188,13 +1203,17 @@ function addHelpers(response) {
1188
1203
  return response;
1189
1204
  }
1190
1205
  /**
1191
- * Returns all text found in all parts of first candidate.
1206
+ * Returns all text from the first candidate's parts, filtering by whether
1207
+ * `partFilter()` returns true.
1208
+ *
1209
+ * @param response - The `GenerateContentResponse` from which to extract text.
1210
+ * @param partFilter - Only return `Part`s for which this returns true
1192
1211
  */
1193
- function getText(response) {
1212
+ function getText(response, partFilter) {
1194
1213
  const textStrings = [];
1195
1214
  if (response.candidates?.[0].content?.parts) {
1196
1215
  for (const part of response.candidates?.[0].content?.parts) {
1197
- if (part.text) {
1216
+ if (part.text && partFilter(part)) {
1198
1217
  textStrings.push(part.text);
1199
1218
  }
1200
1219
  }
@@ -1207,7 +1226,7 @@ function getText(response) {
1207
1226
  }
1208
1227
  }
1209
1228
  /**
1210
- * Returns {@link FunctionCall}s associated with first candidate.
1229
+ * Returns every {@link FunctionCall} associated with first candidate.
1211
1230
  */
1212
1231
  function getFunctionCalls(response) {
1213
1232
  const functionCalls = [];
@@ -1226,7 +1245,7 @@ function getFunctionCalls(response) {
1226
1245
  }
1227
1246
  }
1228
1247
  /**
1229
- * Returns {@link InlineDataPart}s in the first candidate if present.
1248
+ * Returns every {@link InlineDataPart} in the first candidate if present.
1230
1249
  *
1231
1250
  * @internal
1232
1251
  */
@@ -1874,12 +1893,14 @@ const VALID_PART_FIELDS = [
1874
1893
  'text',
1875
1894
  'inlineData',
1876
1895
  'functionCall',
1877
- 'functionResponse'
1896
+ 'functionResponse',
1897
+ 'thought',
1898
+ 'thoughtSignature'
1878
1899
  ];
1879
1900
  const VALID_PARTS_PER_ROLE = {
1880
1901
  user: ['text', 'inlineData'],
1881
1902
  function: ['functionResponse'],
1882
- model: ['text', 'functionCall'],
1903
+ model: ['text', 'functionCall', 'thought', 'thoughtSignature'],
1883
1904
  // System instructions shouldn't be in history anyway.
1884
1905
  system: ['text']
1885
1906
  };
@@ -1901,7 +1922,7 @@ function validateChatHistory(history) {
1901
1922
  throw new AIError(AIErrorCode.INVALID_CONTENT, `Each item should include role field. Got ${role} but valid roles are: ${JSON.stringify(POSSIBLE_ROLES)}`);
1902
1923
  }
1903
1924
  if (!Array.isArray(parts)) {
1904
- throw new AIError(AIErrorCode.INVALID_CONTENT, `Content should have 'parts' but property with an array of Parts`);
1925
+ throw new AIError(AIErrorCode.INVALID_CONTENT, `Content should have 'parts' property with an array of Parts`);
1905
1926
  }
1906
1927
  if (parts.length === 0) {
1907
1928
  throw new AIError(AIErrorCode.INVALID_CONTENT, `Each Content should have at least one part`);
@@ -1910,7 +1931,9 @@ function validateChatHistory(history) {
1910
1931
  text: 0,
1911
1932
  inlineData: 0,
1912
1933
  functionCall: 0,
1913
- functionResponse: 0
1934
+ functionResponse: 0,
1935
+ thought: 0,
1936
+ thoughtSignature: 0
1914
1937
  };
1915
1938
  for (const part of parts) {
1916
1939
  for (const key of VALID_PART_FIELDS) {
@@ -2959,14 +2982,20 @@ class ImagenImageFormat {
2959
2982
  *
2960
2983
  * @public
2961
2984
  */
2962
- function getAI(app = getApp(), options = { backend: new GoogleAIBackend() }) {
2985
+ function getAI(app = getApp(), options) {
2963
2986
  app = getModularInstance(app);
2964
2987
  // Dependencies
2965
2988
  const AIProvider = _getProvider(app, AI_TYPE);
2966
- const identifier = encodeInstanceIdentifier(options.backend);
2967
- return AIProvider.getImmediate({
2989
+ const backend = options?.backend ?? new GoogleAIBackend();
2990
+ const finalOptions = {
2991
+ useLimitedUseAppCheckTokens: options?.useLimitedUseAppCheckTokens ?? false
2992
+ };
2993
+ const identifier = encodeInstanceIdentifier(backend);
2994
+ const aiInstance = AIProvider.getImmediate({
2968
2995
  identifier
2969
2996
  });
2997
+ aiInstance.options = finalOptions;
2998
+ return aiInstance;
2970
2999
  }
2971
3000
  /**
2972
3001
  * Returns a {@link GenerativeModel} class with methods for inference