@firebase/ai 2.3.0-canary.0ffcb26af → 2.3.0-canary.2596dd1b5

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.
Files changed (33) hide show
  1. package/dist/ai-public.d.ts +37 -28
  2. package/dist/ai.d.ts +37 -28
  3. package/dist/esm/index.esm.js +55 -32
  4. package/dist/esm/index.esm.js.map +1 -1
  5. package/dist/esm/src/api.d.ts +1 -1
  6. package/dist/esm/src/methods/live-session-helpers.d.ts +2 -2
  7. package/dist/esm/src/methods/live-session.d.ts +10 -1
  8. package/dist/esm/src/models/imagen-model.d.ts +2 -2
  9. package/dist/esm/src/requests/imagen-image-format.d.ts +3 -3
  10. package/dist/esm/src/types/content.d.ts +4 -4
  11. package/dist/esm/src/types/enums.d.ts +4 -4
  12. package/dist/esm/src/types/imagen/requests.d.ts +9 -9
  13. package/dist/esm/src/types/imagen/responses.d.ts +3 -3
  14. package/dist/esm/src/types/live-responses.d.ts +9 -1
  15. package/dist/esm/src/types/requests.d.ts +1 -1
  16. package/dist/index.cjs.js +55 -32
  17. package/dist/index.cjs.js.map +1 -1
  18. package/dist/index.node.cjs.js +55 -32
  19. package/dist/index.node.cjs.js.map +1 -1
  20. package/dist/index.node.mjs +55 -32
  21. package/dist/index.node.mjs.map +1 -1
  22. package/dist/src/api.d.ts +1 -1
  23. package/dist/src/methods/live-session-helpers.d.ts +2 -2
  24. package/dist/src/methods/live-session.d.ts +10 -1
  25. package/dist/src/models/imagen-model.d.ts +2 -2
  26. package/dist/src/requests/imagen-image-format.d.ts +3 -3
  27. package/dist/src/types/content.d.ts +4 -4
  28. package/dist/src/types/enums.d.ts +4 -4
  29. package/dist/src/types/imagen/requests.d.ts +9 -9
  30. package/dist/src/types/imagen/responses.d.ts +3 -3
  31. package/dist/src/types/live-responses.d.ts +9 -1
  32. package/dist/src/types/requests.d.ts +1 -1
  33. package/package.json +8 -8
@@ -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.3.0-canary.0ffcb26af";
7
+ var version = "2.3.0-canary.2596dd1b5";
8
8
 
9
9
  /**
10
10
  * @license
@@ -326,7 +326,7 @@ const InferenceMode = {
326
326
  /**
327
327
  * Represents the result of the code execution.
328
328
  *
329
- * @public
329
+ * @beta
330
330
  */
331
331
  const Outcome = {
332
332
  UNSPECIFIED: 'OUTCOME_UNSPECIFIED',
@@ -337,7 +337,7 @@ const Outcome = {
337
337
  /**
338
338
  * The programming language of the code.
339
339
  *
340
- * @public
340
+ * @beta
341
341
  */
342
342
  const Language = {
343
343
  UNSPECIFIED: 'LANGUAGE_UNSPECIFIED',
@@ -525,7 +525,7 @@ const SchemaType = {
525
525
  * and the {@link https://cloud.google.com/vertex-ai/generative-ai/docs/image/responsible-ai-imagen#safety-filters | Responsible AI and usage guidelines}
526
526
  * for more details.
527
527
  *
528
- * @beta
528
+ * @public
529
529
  */
530
530
  const ImagenSafetyFilterLevel = {
531
531
  /**
@@ -554,7 +554,7 @@ const ImagenSafetyFilterLevel = {
554
554
  * See the <a href="http://firebase.google.com/docs/vertex-ai/generate-images">personGeneration</a>
555
555
  * documentation for more details.
556
556
  *
557
- * @beta
557
+ * @public
558
558
  */
559
559
  const ImagenPersonFilterLevel = {
560
560
  /**
@@ -587,7 +587,7 @@ const ImagenPersonFilterLevel = {
587
587
  * See the {@link http://firebase.google.com/docs/vertex-ai/generate-images | documentation }
588
588
  * for more details and examples of the supported aspect ratios.
589
589
  *
590
- * @beta
590
+ * @public
591
591
  */
592
592
  const ImagenAspectRatio = {
593
593
  /**
@@ -1592,7 +1592,7 @@ function mapGenerateContentCandidates(candidates) {
1592
1592
  // videoMetadata is not supported.
1593
1593
  // Throw early since developers may send a long video as input and only expect to pay
1594
1594
  // for inference on a small portion of the video.
1595
- if (candidate.content?.parts.some(part => part?.videoMetadata)) {
1595
+ if (candidate.content?.parts?.some(part => part?.videoMetadata)) {
1596
1596
  throw new AIError(AIErrorCode.UNSUPPORTED, 'Part.videoMetadata is not supported in the Gemini Developer API. Please remove this property.');
1597
1597
  }
1598
1598
  const mappedCandidate = {
@@ -1694,6 +1694,14 @@ async function* generateResponseSequence(stream, apiSettings) {
1694
1694
  else {
1695
1695
  enhancedResponse = createEnhancedContentResponse(value);
1696
1696
  }
1697
+ const firstCandidate = enhancedResponse.candidates?.[0];
1698
+ // Don't yield a response with no useful data for the developer.
1699
+ if (!firstCandidate?.content?.parts &&
1700
+ !firstCandidate?.finishReason &&
1701
+ !firstCandidate?.citationMetadata &&
1702
+ !firstCandidate?.urlContextMetadata) {
1703
+ continue;
1704
+ }
1697
1705
  yield enhancedResponse;
1698
1706
  }
1699
1707
  }
@@ -1788,32 +1796,28 @@ function aggregateResponses(responses) {
1788
1796
  * Candidates should always have content and parts, but this handles
1789
1797
  * possible malformed responses.
1790
1798
  */
1791
- if (candidate.content && candidate.content.parts) {
1799
+ if (candidate.content) {
1800
+ // Skip a candidate without parts.
1801
+ if (!candidate.content.parts) {
1802
+ continue;
1803
+ }
1792
1804
  if (!aggregatedResponse.candidates[i].content) {
1793
1805
  aggregatedResponse.candidates[i].content = {
1794
1806
  role: candidate.content.role || 'user',
1795
1807
  parts: []
1796
1808
  };
1797
1809
  }
1798
- const newPart = {};
1799
1810
  for (const part of candidate.content.parts) {
1800
- if (part.text !== undefined) {
1801
- // The backend can send empty text parts. If these are sent back
1802
- // (e.g. in chat history), the backend will respond with an error.
1803
- // To prevent this, ignore empty text parts.
1804
- if (part.text === '') {
1805
- continue;
1806
- }
1807
- newPart.text = part.text;
1811
+ const newPart = { ...part };
1812
+ // The backend can send empty text parts. If these are sent back
1813
+ // (e.g. in chat history), the backend will respond with an error.
1814
+ // To prevent this, ignore empty text parts.
1815
+ if (part.text === '') {
1816
+ continue;
1808
1817
  }
1809
- if (part.functionCall) {
1810
- newPart.functionCall = part.functionCall;
1818
+ if (Object.keys(newPart).length > 0) {
1819
+ aggregatedResponse.candidates[i].content.parts.push(newPart);
1811
1820
  }
1812
- if (Object.keys(newPart).length === 0) {
1813
- throw new AIError(AIErrorCode.INVALID_CONTENT, 'Part should have at least one property, but there are none. This is likely caused ' +
1814
- 'by a malformed response from the backend.');
1815
- }
1816
- aggregatedResponse.candidates[i].content.parts.push(newPart);
1817
1821
  }
1818
1822
  }
1819
1823
  }
@@ -2519,6 +2523,25 @@ class LiveSession {
2519
2523
  this.webSocketHandler.send(JSON.stringify(message));
2520
2524
  });
2521
2525
  }
2526
+ /**
2527
+ * Sends function responses to the server.
2528
+ *
2529
+ * @param functionResponses - The function responses to send.
2530
+ * @throws If this session has been closed.
2531
+ *
2532
+ * @beta
2533
+ */
2534
+ async sendFunctionResponses(functionResponses) {
2535
+ if (this.isClosed) {
2536
+ throw new AIError(AIErrorCode.REQUEST_ERROR, 'This LiveSession has been closed and cannot be used.');
2537
+ }
2538
+ const message = {
2539
+ toolResponse: {
2540
+ functionResponses
2541
+ }
2542
+ };
2543
+ this.webSocketHandler.send(JSON.stringify(message));
2544
+ }
2522
2545
  /**
2523
2546
  * Sends a stream of {@link GenerativeContentBlob}.
2524
2547
  *
@@ -2733,7 +2756,7 @@ class LiveGenerativeModel extends AIModel {
2733
2756
  * }
2734
2757
  * ```
2735
2758
  *
2736
- * @beta
2759
+ * @public
2737
2760
  */
2738
2761
  class ImagenModel extends AIModel {
2739
2762
  /**
@@ -2769,7 +2792,7 @@ class ImagenModel extends AIModel {
2769
2792
  * returned object will have a `filteredReason` property.
2770
2793
  * If all images are filtered, the `images` array will be empty.
2771
2794
  *
2772
- * @beta
2795
+ * @public
2773
2796
  */
2774
2797
  async generateImages(prompt) {
2775
2798
  const body = createPredictRequestBody(prompt, {
@@ -3232,7 +3255,7 @@ class AnyOfSchema extends Schema {
3232
3255
  * }
3233
3256
  * ```
3234
3257
  *
3235
- * @beta
3258
+ * @public
3236
3259
  */
3237
3260
  class ImagenImageFormat {
3238
3261
  constructor() {
@@ -3244,7 +3267,7 @@ class ImagenImageFormat {
3244
3267
  * @param compressionQuality - The level of compression (a number between 0 and 100).
3245
3268
  * @returns An {@link ImagenImageFormat} object for a JPEG image.
3246
3269
  *
3247
- * @beta
3270
+ * @public
3248
3271
  */
3249
3272
  static jpeg(compressionQuality) {
3250
3273
  if (compressionQuality &&
@@ -3258,7 +3281,7 @@ class ImagenImageFormat {
3258
3281
  *
3259
3282
  * @returns An {@link ImagenImageFormat} object for a PNG image.
3260
3283
  *
3261
- * @beta
3284
+ * @public
3262
3285
  */
3263
3286
  static png() {
3264
3287
  return { mimeType: 'image/png' };
@@ -3500,9 +3523,9 @@ class AudioConversationRunner {
3500
3523
  }
3501
3524
  else {
3502
3525
  try {
3503
- const resultPart = await this.options.functionCallingHandler(message.functionCalls);
3526
+ const functionResponse = await this.options.functionCallingHandler(message.functionCalls);
3504
3527
  if (!this.isStopped) {
3505
- void this.liveSession.send([resultPart]);
3528
+ void this.liveSession.sendFunctionResponses([functionResponse]);
3506
3529
  }
3507
3530
  }
3508
3531
  catch (e) {
@@ -3722,7 +3745,7 @@ function getGenerativeModel(ai, modelParams, requestOptions) {
3722
3745
  * @throws If the `apiKey` or `projectId` fields are missing in your
3723
3746
  * Firebase config.
3724
3747
  *
3725
- * @beta
3748
+ * @public
3726
3749
  */
3727
3750
  function getImagenModel(ai, modelParams, requestOptions) {
3728
3751
  if (!modelParams.model) {