@@ -86,7 +86,7 @@ internal GenerativeModel(FirebaseApp firebaseApp,
8686 /// <param name="content">The input given to the model as a prompt.</param>
8787 /// <param name="cancellationToken">An optional token to cancel the operation.</param>
8888 /// <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>
9090 public Task < GenerateContentResponse > GenerateContentAsync (
9191 ModelContent content , CancellationToken cancellationToken = default ) {
9292 return GenerateContentAsync ( new [ ] { content } , cancellationToken ) ;
@@ -97,7 +97,7 @@ public Task<GenerateContentResponse> GenerateContentAsync(
9797 /// <param name="text">The text given to the model as a prompt.</param>
9898 /// <param name="cancellationToken">An optional token to cancel the operation.</param>
9999 /// <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>
101101 public Task < GenerateContentResponse > GenerateContentAsync (
102102 string text , CancellationToken cancellationToken = default ) {
103103 return GenerateContentAsync ( new [ ] { ModelContent . Text ( text ) } , cancellationToken ) ;
@@ -108,7 +108,7 @@ public Task<GenerateContentResponse> GenerateContentAsync(
108108 /// <param name="content">The input given to the model as a prompt.</param>
109109 /// <param name="cancellationToken">An optional token to cancel the operation.</param>
110110 /// <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>
112112 public Task < GenerateContentResponse > GenerateContentAsync (
113113 IEnumerable < ModelContent > content , CancellationToken cancellationToken = default ) {
114114 return GenerateContentAsyncInternal ( content , cancellationToken ) ;
@@ -120,7 +120,7 @@ public Task<GenerateContentResponse> GenerateContentAsync(
120120 /// <param name="content">The input given to the model as a prompt.</param>
121121 /// <param name="cancellationToken">An optional token to cancel the operation.</param>
122122 /// <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>
124124 public IAsyncEnumerable < GenerateContentResponse > GenerateContentStreamAsync (
125125 ModelContent content , CancellationToken cancellationToken = default ) {
126126 return GenerateContentStreamAsync ( new [ ] { content } , cancellationToken ) ;
@@ -131,7 +131,7 @@ public IAsyncEnumerable<GenerateContentResponse> GenerateContentStreamAsync(
131131 /// <param name="text">The text given to the model as a prompt.</param>
132132 /// <param name="cancellationToken">An optional token to cancel the operation.</param>
133133 /// <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>
135135 public IAsyncEnumerable < GenerateContentResponse > GenerateContentStreamAsync (
136136 string text , CancellationToken cancellationToken = default ) {
137137 return GenerateContentStreamAsync ( new [ ] { ModelContent . Text ( text ) } , cancellationToken ) ;
@@ -142,7 +142,7 @@ public IAsyncEnumerable<GenerateContentResponse> GenerateContentStreamAsync(
142142 /// <param name="content">The input given to the model as a prompt.</param>
143143 /// <param name="cancellationToken">An optional token to cancel the operation.</param>
144144 /// <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>
146146 public IAsyncEnumerable < GenerateContentResponse > GenerateContentStreamAsync (
147147 IEnumerable < ModelContent > content , CancellationToken cancellationToken = default ) {
148148 return GenerateContentStreamAsyncInternal ( content , cancellationToken ) ;
@@ -153,7 +153,7 @@ public IAsyncEnumerable<GenerateContentResponse> GenerateContentStreamAsync(
153153 /// </summary>
154154 /// <param name="content">The input given to the model as a prompt.</param>
155155 /// <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>
157157 public Task < CountTokensResponse > CountTokensAsync (
158158 ModelContent content , CancellationToken cancellationToken = default ) {
159159 return CountTokensAsync ( new [ ] { content } , cancellationToken ) ;
@@ -164,7 +164,7 @@ public Task<CountTokensResponse> CountTokensAsync(
164164 /// <param name="text">The text input given to the model as a prompt.</param>
165165 /// <param name="cancellationToken">An optional token to cancel the operation.</param>
166166 /// <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>
168168 public Task < CountTokensResponse > CountTokensAsync (
169169 string text , CancellationToken cancellationToken = default ) {
170170 return CountTokensAsync ( new [ ] { ModelContent . Text ( text ) } , cancellationToken ) ;
@@ -175,7 +175,7 @@ public Task<CountTokensResponse> CountTokensAsync(
175175 /// <param name="content">The input given to the model as a prompt.</param>
176176 /// <param name="cancellationToken">An optional token to cancel the operation.</param>
177177 /// <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>
179179 public Task < CountTokensResponse > CountTokensAsync (
180180 IEnumerable < ModelContent > content , CancellationToken cancellationToken = default ) {
181181 return CountTokensAsyncInternal ( content , cancellationToken ) ;
@@ -213,16 +213,8 @@ private async Task<GenerateContentResponse> GenerateContentAsyncInternal(
213213 UnityEngine . Debug . Log ( "Request:\n " + bodyJson ) ;
214214#endif
215215
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 ) ;
226218
227219 string result = await response . Content . ReadAsStringAsync ( ) ;
228220
@@ -233,6 +225,33 @@ private async Task<GenerateContentResponse> GenerateContentAsyncInternal(
233225 return GenerateContentResponse . FromJson ( result , _backend . Provider ) ;
234226 }
235227
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+
236255 private async IAsyncEnumerable < GenerateContentResponse > GenerateContentStreamAsyncInternal (
237256 IEnumerable < ModelContent > content ,
238257 [ EnumeratorCancellation ] CancellationToken cancellationToken ) {
@@ -249,16 +268,8 @@ private async IAsyncEnumerable<GenerateContentResponse> GenerateContentStreamAsy
249268 UnityEngine . Debug . Log ( "Request:\n " + bodyJson ) ;
250269#endif
251270
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 ) ;
262273
263274 // We are expecting a Stream as the response, so handle that.
264275 using var stream = await response . Content . ReadAsStreamAsync ( ) ;
@@ -293,16 +304,8 @@ private async Task<CountTokensResponse> CountTokensAsyncInternal(
293304 UnityEngine . Debug . Log ( "CountTokensRequest:\n " + bodyJson ) ;
294305#endif
295306
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 ) ;
306309
307310 string result = await response . Content . ReadAsStringAsync ( ) ;
308311
0 commit comments