@firebase/ai 2.3.0-canary.7a7634f79 → 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.
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.3.0-canary.7a7634f79";
11
+ var version = "2.3.0-canary.ea8512812";
12
12
 
13
13
  /**
14
14
  * @license
@@ -1919,7 +1919,7 @@ function mapGenerateContentCandidates(candidates) {
1919
1919
  // videoMetadata is not supported.
1920
1920
  // Throw early since developers may send a long video as input and only expect to pay
1921
1921
  // for inference on a small portion of the video.
1922
- if (candidate.content?.parts.some(part => part?.videoMetadata)) {
1922
+ if (candidate.content?.parts?.some(part => part?.videoMetadata)) {
1923
1923
  throw new AIError(AIErrorCode.UNSUPPORTED, 'Part.videoMetadata is not supported in the Gemini Developer API. Please remove this property.');
1924
1924
  }
1925
1925
  const mappedCandidate = {
@@ -2021,6 +2021,14 @@ async function* generateResponseSequence(stream, apiSettings) {
2021
2021
  else {
2022
2022
  enhancedResponse = createEnhancedContentResponse(value);
2023
2023
  }
2024
+ const firstCandidate = enhancedResponse.candidates?.[0];
2025
+ // Don't yield a response with no useful data for the developer.
2026
+ if (!firstCandidate?.content?.parts &&
2027
+ !firstCandidate?.finishReason &&
2028
+ !firstCandidate?.citationMetadata &&
2029
+ !firstCandidate?.urlContextMetadata) {
2030
+ continue;
2031
+ }
2024
2032
  yield enhancedResponse;
2025
2033
  }
2026
2034
  }
@@ -2115,32 +2123,28 @@ function aggregateResponses(responses) {
2115
2123
  * Candidates should always have content and parts, but this handles
2116
2124
  * possible malformed responses.
2117
2125
  */
2118
- if (candidate.content && candidate.content.parts) {
2126
+ if (candidate.content) {
2127
+ // Skip a candidate without parts.
2128
+ if (!candidate.content.parts) {
2129
+ continue;
2130
+ }
2119
2131
  if (!aggregatedResponse.candidates[i].content) {
2120
2132
  aggregatedResponse.candidates[i].content = {
2121
2133
  role: candidate.content.role || 'user',
2122
2134
  parts: []
2123
2135
  };
2124
2136
  }
2125
- const newPart = {};
2126
2137
  for (const part of candidate.content.parts) {
2127
- if (part.text !== undefined) {
2128
- // The backend can send empty text parts. If these are sent back
2129
- // (e.g. in chat history), the backend will respond with an error.
2130
- // To prevent this, ignore empty text parts.
2131
- if (part.text === '') {
2132
- continue;
2133
- }
2134
- newPart.text = part.text;
2135
- }
2136
- if (part.functionCall) {
2137
- newPart.functionCall = part.functionCall;
2138
+ const newPart = { ...part };
2139
+ // The backend can send empty text parts. If these are sent back
2140
+ // (e.g. in chat history), the backend will respond with an error.
2141
+ // To prevent this, ignore empty text parts.
2142
+ if (part.text === '') {
2143
+ continue;
2138
2144
  }
2139
- if (Object.keys(newPart).length === 0) {
2140
- throw new AIError(AIErrorCode.INVALID_CONTENT, 'Part should have at least one property, but there are none. This is likely caused ' +
2141
- 'by a malformed response from the backend.');
2145
+ if (Object.keys(newPart).length > 0) {
2146
+ aggregatedResponse.candidates[i].content.parts.push(newPart);
2142
2147
  }
2143
- aggregatedResponse.candidates[i].content.parts.push(newPart);
2144
2148
  }
2145
2149
  }
2146
2150
  }