@firebase/ai 2.3.0-canary.cb3bdd812 → 2.3.0-canary.ea8512812

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, 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.cb3bdd812";
7
+ var version = "2.3.0-canary.ea8512812";
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',
@@ -360,6 +360,45 @@ const Language = {
360
360
  * See the License for the specific language governing permissions and
361
361
  * limitations under the License.
362
362
  */
363
+ /**
364
+ * The status of a URL retrieval.
365
+ *
366
+ * @remarks
367
+ * <b>URL_RETRIEVAL_STATUS_UNSPECIFIED:</b> Unspecified retrieval status.
368
+ * <br/>
369
+ * <b>URL_RETRIEVAL_STATUS_SUCCESS:</b> The URL retrieval was successful.
370
+ * <br/>
371
+ * <b>URL_RETRIEVAL_STATUS_ERROR:</b> The URL retrieval failed.
372
+ * <br/>
373
+ * <b>URL_RETRIEVAL_STATUS_PAYWALL:</b> The URL retrieval failed because the content is behind a paywall.
374
+ * <br/>
375
+ * <b>URL_RETRIEVAL_STATUS_UNSAFE:</b> The URL retrieval failed because the content is unsafe.
376
+ * <br/>
377
+ *
378
+ * @beta
379
+ */
380
+ const URLRetrievalStatus = {
381
+ /**
382
+ * Unspecified retrieval status.
383
+ */
384
+ URL_RETRIEVAL_STATUS_UNSPECIFIED: 'URL_RETRIEVAL_STATUS_UNSPECIFIED',
385
+ /**
386
+ * The URL retrieval was successful.
387
+ */
388
+ URL_RETRIEVAL_STATUS_SUCCESS: 'URL_RETRIEVAL_STATUS_SUCCESS',
389
+ /**
390
+ * The URL retrieval failed.
391
+ */
392
+ URL_RETRIEVAL_STATUS_ERROR: 'URL_RETRIEVAL_STATUS_ERROR',
393
+ /**
394
+ * The URL retrieval failed because the content is behind a paywall.
395
+ */
396
+ URL_RETRIEVAL_STATUS_PAYWALL: 'URL_RETRIEVAL_STATUS_PAYWALL',
397
+ /**
398
+ * The URL retrieval failed because the content is unsafe.
399
+ */
400
+ URL_RETRIEVAL_STATUS_UNSAFE: 'URL_RETRIEVAL_STATUS_UNSAFE'
401
+ };
363
402
  /**
364
403
  * The types of responses that can be returned by {@link LiveSession.receive}.
365
404
  *
@@ -1553,7 +1592,7 @@ function mapGenerateContentCandidates(candidates) {
1553
1592
  // videoMetadata is not supported.
1554
1593
  // Throw early since developers may send a long video as input and only expect to pay
1555
1594
  // for inference on a small portion of the video.
1556
- if (candidate.content?.parts.some(part => part?.videoMetadata)) {
1595
+ if (candidate.content?.parts?.some(part => part?.videoMetadata)) {
1557
1596
  throw new AIError(AIErrorCode.UNSUPPORTED, 'Part.videoMetadata is not supported in the Gemini Developer API. Please remove this property.');
1558
1597
  }
1559
1598
  const mappedCandidate = {
@@ -1563,7 +1602,8 @@ function mapGenerateContentCandidates(candidates) {
1563
1602
  finishMessage: candidate.finishMessage,
1564
1603
  safetyRatings: mappedSafetyRatings,
1565
1604
  citationMetadata,
1566
- groundingMetadata: candidate.groundingMetadata
1605
+ groundingMetadata: candidate.groundingMetadata,
1606
+ urlContextMetadata: candidate.urlContextMetadata
1567
1607
  };
1568
1608
  mappedCandidates.push(mappedCandidate);
1569
1609
  });
@@ -1654,6 +1694,14 @@ async function* generateResponseSequence(stream, apiSettings) {
1654
1694
  else {
1655
1695
  enhancedResponse = createEnhancedContentResponse(value);
1656
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
+ }
1657
1705
  yield enhancedResponse;
1658
1706
  }
1659
1707
  }
@@ -1733,36 +1781,43 @@ function aggregateResponses(responses) {
1733
1781
  candidate.safetyRatings;
1734
1782
  aggregatedResponse.candidates[i].groundingMetadata =
1735
1783
  candidate.groundingMetadata;
1784
+ // The urlContextMetadata object is defined in the first chunk of the response stream.
1785
+ // In all subsequent chunks, the urlContextMetadata object will be undefined. We need to
1786
+ // make sure that we don't overwrite the first value urlContextMetadata object with undefined.
1787
+ // FIXME: What happens if we receive a second, valid urlContextMetadata object?
1788
+ const urlContextMetadata = candidate.urlContextMetadata;
1789
+ if (typeof urlContextMetadata === 'object' &&
1790
+ urlContextMetadata !== null &&
1791
+ Object.keys(urlContextMetadata).length > 0) {
1792
+ aggregatedResponse.candidates[i].urlContextMetadata =
1793
+ urlContextMetadata;
1794
+ }
1736
1795
  /**
1737
1796
  * Candidates should always have content and parts, but this handles
1738
1797
  * possible malformed responses.
1739
1798
  */
1740
- if (candidate.content && candidate.content.parts) {
1799
+ if (candidate.content) {
1800
+ // Skip a candidate without parts.
1801
+ if (!candidate.content.parts) {
1802
+ continue;
1803
+ }
1741
1804
  if (!aggregatedResponse.candidates[i].content) {
1742
1805
  aggregatedResponse.candidates[i].content = {
1743
1806
  role: candidate.content.role || 'user',
1744
1807
  parts: []
1745
1808
  };
1746
1809
  }
1747
- const newPart = {};
1748
1810
  for (const part of candidate.content.parts) {
1749
- if (part.text !== undefined) {
1750
- // The backend can send empty text parts. If these are sent back
1751
- // (e.g. in chat history), the backend will respond with an error.
1752
- // To prevent this, ignore empty text parts.
1753
- if (part.text === '') {
1754
- continue;
1755
- }
1756
- newPart.text = part.text;
1757
- }
1758
- if (part.functionCall) {
1759
- newPart.functionCall = part.functionCall;
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;
1760
1817
  }
1761
- if (Object.keys(newPart).length === 0) {
1762
- throw new AIError(AIErrorCode.INVALID_CONTENT, 'Part should have at least one property, but there are none. This is likely caused ' +
1763
- 'by a malformed response from the backend.');
1818
+ if (Object.keys(newPart).length > 0) {
1819
+ aggregatedResponse.candidates[i].content.parts.push(newPart);
1764
1820
  }
1765
- aggregatedResponse.candidates[i].content.parts.push(newPart);
1766
1821
  }
1767
1822
  }
1768
1823
  }
@@ -3722,5 +3777,5 @@ function registerAI() {
3722
3777
  }
3723
3778
  registerAI();
3724
3779
 
3725
- 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, VertexAIBackend, getAI, getGenerativeModel, getImagenModel, getLiveGenerativeModel, startAudioConversation };
3780
+ 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 };
3726
3781
  //# sourceMappingURL=index.node.mjs.map