@@ -86,7 +86,7 @@ internal GenerativeModel(FirebaseApp firebaseApp,
86
86
/// <param name="content">The input given to the model as a prompt.</param>
87
87
/// <param name="cancellationToken">An optional token to cancel the operation.</param>
88
88
/// <returns>The generated content response from the model.</returns>
89
- /// <exception cref="FirebaseAIException ">Thrown when an error occurs during content generation.</exception>
89
+ /// <exception cref="HttpRequestException ">Thrown when an error occurs during content generation.</exception>
90
90
public Task < GenerateContentResponse > GenerateContentAsync (
91
91
ModelContent content , CancellationToken cancellationToken = default ) {
92
92
return GenerateContentAsync ( new [ ] { content } , cancellationToken ) ;
@@ -97,7 +97,7 @@ public Task<GenerateContentResponse> GenerateContentAsync(
97
97
/// <param name="text">The text given to the model as a prompt.</param>
98
98
/// <param name="cancellationToken">An optional token to cancel the operation.</param>
99
99
/// <returns>The generated content response from the model.</returns>
100
- /// <exception cref="FirebaseAIException ">Thrown when an error occurs during content generation.</exception>
100
+ /// <exception cref="HttpRequestException ">Thrown when an error occurs during content generation.</exception>
101
101
public Task < GenerateContentResponse > GenerateContentAsync (
102
102
string text , CancellationToken cancellationToken = default ) {
103
103
return GenerateContentAsync ( new [ ] { ModelContent . Text ( text ) } , cancellationToken ) ;
@@ -108,7 +108,7 @@ public Task<GenerateContentResponse> GenerateContentAsync(
108
108
/// <param name="content">The input given to the model as a prompt.</param>
109
109
/// <param name="cancellationToken">An optional token to cancel the operation.</param>
110
110
/// <returns>The generated content response from the model.</returns>
111
- /// <exception cref="FirebaseAIException ">Thrown when an error occurs during content generation.</exception>
111
+ /// <exception cref="HttpRequestException ">Thrown when an error occurs during content generation.</exception>
112
112
public Task < GenerateContentResponse > GenerateContentAsync (
113
113
IEnumerable < ModelContent > content , CancellationToken cancellationToken = default ) {
114
114
return GenerateContentAsyncInternal ( content , cancellationToken ) ;
@@ -120,7 +120,7 @@ public Task<GenerateContentResponse> GenerateContentAsync(
120
120
/// <param name="content">The input given to the model as a prompt.</param>
121
121
/// <param name="cancellationToken">An optional token to cancel the operation.</param>
122
122
/// <returns>A stream of generated content responses from the model.</returns>
123
- /// <exception cref="FirebaseAIException ">Thrown when an error occurs during content generation.</exception>
123
+ /// <exception cref="HttpRequestException ">Thrown when an error occurs during content generation.</exception>
124
124
public IAsyncEnumerable < GenerateContentResponse > GenerateContentStreamAsync (
125
125
ModelContent content , CancellationToken cancellationToken = default ) {
126
126
return GenerateContentStreamAsync ( new [ ] { content } , cancellationToken ) ;
@@ -131,7 +131,7 @@ public IAsyncEnumerable<GenerateContentResponse> GenerateContentStreamAsync(
131
131
/// <param name="text">The text given to the model as a prompt.</param>
132
132
/// <param name="cancellationToken">An optional token to cancel the operation.</param>
133
133
/// <returns>A stream of generated content responses from the model.</returns>
134
- /// <exception cref="FirebaseAIException ">Thrown when an error occurs during content generation.</exception>
134
+ /// <exception cref="HttpRequestException ">Thrown when an error occurs during content generation.</exception>
135
135
public IAsyncEnumerable < GenerateContentResponse > GenerateContentStreamAsync (
136
136
string text , CancellationToken cancellationToken = default ) {
137
137
return GenerateContentStreamAsync ( new [ ] { ModelContent . Text ( text ) } , cancellationToken ) ;
@@ -142,7 +142,7 @@ public IAsyncEnumerable<GenerateContentResponse> GenerateContentStreamAsync(
142
142
/// <param name="content">The input given to the model as a prompt.</param>
143
143
/// <param name="cancellationToken">An optional token to cancel the operation.</param>
144
144
/// <returns>A stream of generated content responses from the model.</returns>
145
- /// <exception cref="FirebaseAIException ">Thrown when an error occurs during content generation.</exception>
145
+ /// <exception cref="HttpRequestException ">Thrown when an error occurs during content generation.</exception>
146
146
public IAsyncEnumerable < GenerateContentResponse > GenerateContentStreamAsync (
147
147
IEnumerable < ModelContent > content , CancellationToken cancellationToken = default ) {
148
148
return GenerateContentStreamAsyncInternal ( content , cancellationToken ) ;
@@ -153,7 +153,7 @@ public IAsyncEnumerable<GenerateContentResponse> GenerateContentStreamAsync(
153
153
/// </summary>
154
154
/// <param name="content">The input given to the model as a prompt.</param>
155
155
/// <returns>The `CountTokensResponse` of running the model's tokenizer on the input.</returns>
156
- /// <exception cref="FirebaseAIException ">Thrown when an error occurs during the request.</exception>
156
+ /// <exception cref="HttpRequestException ">Thrown when an error occurs during the request.</exception>
157
157
public Task < CountTokensResponse > CountTokensAsync (
158
158
ModelContent content , CancellationToken cancellationToken = default ) {
159
159
return CountTokensAsync ( new [ ] { content } , cancellationToken ) ;
@@ -164,7 +164,7 @@ public Task<CountTokensResponse> CountTokensAsync(
164
164
/// <param name="text">The text input given to the model as a prompt.</param>
165
165
/// <param name="cancellationToken">An optional token to cancel the operation.</param>
166
166
/// <returns>The `CountTokensResponse` of running the model's tokenizer on the input.</returns>
167
- /// <exception cref="FirebaseAIException ">Thrown when an error occurs during the request.</exception>
167
+ /// <exception cref="HttpRequestException ">Thrown when an error occurs during the request.</exception>
168
168
public Task < CountTokensResponse > CountTokensAsync (
169
169
string text , CancellationToken cancellationToken = default ) {
170
170
return CountTokensAsync ( new [ ] { ModelContent . Text ( text ) } , cancellationToken ) ;
@@ -175,7 +175,7 @@ public Task<CountTokensResponse> CountTokensAsync(
175
175
/// <param name="content">The input given to the model as a prompt.</param>
176
176
/// <param name="cancellationToken">An optional token to cancel the operation.</param>
177
177
/// <returns>The `CountTokensResponse` of running the model's tokenizer on the input.</returns>
178
- /// <exception cref="FirebaseAIException ">Thrown when an error occurs during the request.</exception>
178
+ /// <exception cref="HttpRequestException ">Thrown when an error occurs during the request.</exception>
179
179
public Task < CountTokensResponse > CountTokensAsync (
180
180
IEnumerable < ModelContent > content , CancellationToken cancellationToken = default ) {
181
181
return CountTokensAsyncInternal ( content , cancellationToken ) ;
@@ -213,16 +213,8 @@ private async Task<GenerateContentResponse> GenerateContentAsyncInternal(
213
213
UnityEngine . Debug . Log ( "Request:\n " + bodyJson ) ;
214
214
#endif
215
215
216
- HttpResponseMessage response ;
217
- try {
218
- response = await _httpClient . SendAsync ( request , cancellationToken ) ;
219
- response . EnsureSuccessStatusCode ( ) ;
220
- } catch ( TaskCanceledException e ) when ( e . InnerException is TimeoutException ) {
221
- throw new FirebaseAIRequestTimeoutException ( "Request timed out." , e ) ;
222
- } catch ( HttpRequestException e ) {
223
- // TODO: Convert to a more precise exception when possible.
224
- throw new FirebaseAIException ( "HTTP request failed." , e ) ;
225
- }
216
+ var response = await _httpClient . SendAsync ( request , cancellationToken ) ;
217
+ await ValidateHttpResponse ( response ) ;
226
218
227
219
string result = await response . Content . ReadAsStringAsync ( ) ;
228
220
@@ -233,6 +225,33 @@ private async Task<GenerateContentResponse> GenerateContentAsyncInternal(
233
225
return GenerateContentResponse . FromJson ( result , _backend . Provider ) ;
234
226
}
235
227
228
+ // Helper function to throw an exception if the Http Response indicates failure.
229
+ // Useful as EnsureSuccessStatusCode can leave out relevant information.
230
+ private async Task ValidateHttpResponse ( HttpResponseMessage response ) {
231
+ if ( response . IsSuccessStatusCode ) {
232
+ return ;
233
+ }
234
+
235
+ // Status code indicates failure, try to read the content for more details
236
+ string errorContent = "No error content available." ;
237
+ if ( response . Content != null ) {
238
+ try {
239
+ errorContent = await response . Content . ReadAsStringAsync ( ) ;
240
+ } catch ( Exception readEx ) {
241
+ // Handle being unable to read the content
242
+ errorContent = $ "Failed to read error content: { readEx . Message } ";
243
+ }
244
+ }
245
+
246
+ // Construct the exception with as much information as possible.
247
+ var ex = new HttpRequestException (
248
+ $ "HTTP request failed with status code: { ( int ) response . StatusCode } ({ response . ReasonPhrase } ).\n " +
249
+ $ "Error Content: { errorContent } "
250
+ ) ;
251
+
252
+ throw ex ;
253
+ }
254
+
236
255
private async IAsyncEnumerable < GenerateContentResponse > GenerateContentStreamAsyncInternal (
237
256
IEnumerable < ModelContent > content ,
238
257
[ EnumeratorCancellation ] CancellationToken cancellationToken ) {
@@ -249,16 +268,8 @@ private async IAsyncEnumerable<GenerateContentResponse> GenerateContentStreamAsy
249
268
UnityEngine . Debug . Log ( "Request:\n " + bodyJson ) ;
250
269
#endif
251
270
252
- HttpResponseMessage response ;
253
- try {
254
- response = await _httpClient . SendAsync ( request , HttpCompletionOption . ResponseHeadersRead , cancellationToken ) ;
255
- response . EnsureSuccessStatusCode ( ) ;
256
- } catch ( TaskCanceledException e ) when ( e . InnerException is TimeoutException ) {
257
- throw new FirebaseAIRequestTimeoutException ( "Request timed out." , e ) ;
258
- } catch ( HttpRequestException e ) {
259
- // TODO: Convert to a more precise exception when possible.
260
- throw new FirebaseAIException ( "HTTP request failed." , e ) ;
261
- }
271
+ var response = await _httpClient . SendAsync ( request , HttpCompletionOption . ResponseHeadersRead , cancellationToken ) ;
272
+ await ValidateHttpResponse ( response ) ;
262
273
263
274
// We are expecting a Stream as the response, so handle that.
264
275
using var stream = await response . Content . ReadAsStreamAsync ( ) ;
@@ -293,16 +304,8 @@ private async Task<CountTokensResponse> CountTokensAsyncInternal(
293
304
UnityEngine . Debug . Log ( "CountTokensRequest:\n " + bodyJson ) ;
294
305
#endif
295
306
296
- HttpResponseMessage response ;
297
- try {
298
- response = await _httpClient . SendAsync ( request , cancellationToken ) ;
299
- response . EnsureSuccessStatusCode ( ) ;
300
- } catch ( TaskCanceledException e ) when ( e . InnerException is TimeoutException ) {
301
- throw new FirebaseAIRequestTimeoutException ( "Request timed out." , e ) ;
302
- } catch ( HttpRequestException e ) {
303
- // TODO: Convert to a more precise exception when possible.
304
- throw new FirebaseAIException ( "HTTP request failed." , e ) ;
305
- }
307
+ var response = await _httpClient . SendAsync ( request , cancellationToken ) ;
308
+ await ValidateHttpResponse ( response ) ;
306
309
307
310
string result = await response . Content . ReadAsStringAsync ( ) ;
308
311
0 commit comments