1414import java .util .UUID ;
1515import java .util .concurrent .CompletableFuture ;
1616import java .util .concurrent .ConcurrentHashMap ;
17+ import java .util .stream .Collectors ;
1718import java .util .stream .Stream ;
1819
1920import static java .util .Collections .emptyList ;
@@ -173,11 +174,11 @@ public CompletableFuture<Long> countTokens(GenerativeModel model) {
173174 try {
174175 var ctr = jsonParser .fromJson (body , CountTokenResponse .class );
175176 if (ctr .totalTokens () == null ) {
176- throw new RuntimeException ("No token field in response" );
177+ throw new GeminiException ("No token field in response" );
177178 }
178179 return ctr .totalTokens ();
179180 } catch (Exception e ) {
180- throw new RuntimeException ("Unexpected body:\n " + body , e );
181+ throw new GeminiException ("Unexpected body:\n " + body , e );
181182 }
182183 });
183184 });
@@ -208,6 +209,27 @@ public Stream<GeneratedContent> generateContentStream(GenerativeModel model) {
208209 request ,
209210 HttpResponse .BodyHandlers .ofLines ()
210211 );
212+ // e.g. Response code: 503 (Service Unavailable); Time: 5813ms (5 s 813 ms)
213+ //
214+ //{
215+ // "error": {
216+ // "code": 503,
217+ // "message": "The model is overloaded. Please try again later.",
218+ // "status": "UNAVAILABLE"
219+ // }
220+ //}
221+
222+ if (response .statusCode () != 200 ) {
223+ // in case of an error, we don't stream, but block and give the whole response, because
224+ // we don't want to parse it and potentially cause more errors
225+ String error = response .body ()
226+ .collect (Collectors .joining ("\n " ));
227+ throw new GeminiException (
228+ "Unexpected stream response:\n %s" .formatted (error ),
229+ response .statusCode ()
230+ );
231+ }
232+
211233 return response .body ()
212234 .filter (l -> l .length () > STREAM_LINE_PREFIX_LENGTH )
213235 .map (line -> parse (line .substring (STREAM_LINE_PREFIX_LENGTH ), uuid ));
@@ -295,12 +317,12 @@ public CompletableFuture<List<ContentEmbedding>> embedContents(
295317 try {
296318 BatchEmbedContentResponse becr = jsonParser .fromJson (body , BatchEmbedContentResponse .class );
297319 if (becr .embeddings () == null ) {
298- throw new RuntimeException ( );
320+ throw new GeminiException ( "No embeddings field in response: \n " + body );
299321 }
300322 return becr
301323 .embeddings ();
302324 } catch (Exception e ) {
303- throw new RuntimeException ("Unexpected body:\n " + body , e );
325+ throw new GeminiException ("Unexpected body:\n " + body , e );
304326 }
305327 });
306328
@@ -371,7 +393,7 @@ private static List<GenerationContent> convertGenerationContents(GenerativeModel
371393 ).toList ()
372394 );
373395 } else {
374- throw new RuntimeException ("Unexpected content:\n " + content );
396+ throw new GeminiException ("Unexpected content:\n " + content );
375397 }
376398 })
377399 .toList ();
@@ -384,7 +406,7 @@ private <T> T execute(ThrowingSupplier<T> supplier) {
384406 throw new UncheckedIOException (e );
385407 } catch (InterruptedException e ) {
386408 Thread .currentThread ().interrupt ();
387- throw new RuntimeException ( e );
409+ throw new GeminiException ( "Thread was interrupted." , e );
388410 }
389411 }
390412
@@ -537,7 +559,7 @@ private GeneratedContent parse(String body, UUID uuid) {
537559 }
538560 return new GeneratedContent (uuid , candidate .content ().parts ().get (0 ).text (), candidate .finishReason ());
539561 } catch (Exception e ) {
540- throw new RuntimeException ("Unexpected body:\n " + body , e );
562+ throw new GeminiException ("Unexpected body:\n " + body , e );
541563 }
542564 }
543565
0 commit comments