@@ -412,14 +412,22 @@ def parts(self):
412
412
"""
413
413
candidates = self .candidates
414
414
if not candidates :
415
- raise ValueError (
415
+ msg = (
416
416
"Invalid operation: The `response.parts` quick accessor requires a single candidate, "
417
- "but none were returned. Please check the `response.prompt_feedback` to determine if the prompt was blocked ."
417
+ "but but `response.candidates` is empty ."
418
418
)
419
+ if self .prompt_feedback :
420
+ raise ValueError (
421
+ msg + "\n This appears to be caused by a blocked prompt, "
422
+ f"see `response.prompt_feedback`: { self .prompt_feedback } "
423
+ )
424
+ else :
425
+ raise ValueError (msg )
426
+
419
427
if len (candidates ) > 1 :
420
428
raise ValueError (
421
- "Invalid operation: The `response.parts` quick accessor requires a single candidate. "
422
- "For multiple candidates, please use `result.candidates[index].text`."
429
+ "Invalid operation: The `response.parts` quick accessor retrieves the parts for a single candidate. "
430
+ "This response contains multiple candidates, please use `result.candidates[index].text`."
423
431
)
424
432
parts = candidates [0 ].content .parts
425
433
return parts
@@ -433,10 +441,53 @@ def text(self):
433
441
"""
434
442
parts = self .parts
435
443
if not parts :
436
- raise ValueError (
437
- "Invalid operation: The `response.text` quick accessor requires the response to contain a valid `Part`, "
438
- "but none were returned. Please check the `candidate.safety_ratings` to determine if the response was blocked."
444
+ candidate = self .candidates [0 ]
445
+
446
+ fr = candidate .finish_reason
447
+ FinishReason = protos .Candidate .FinishReason
448
+
449
+ msg = (
450
+ "Invalid operation: The `response.text` quick accessor requires the response to contain a valid "
451
+ "`Part`, but none were returned. The candidate's "
452
+ f"[finish_reason](https://ai.google.dev/api/generate-content#finishreason) is { fr } ."
439
453
)
454
+ if candidate .finish_message :
455
+ msg += 'The `finish_message` is "{candidate.finish_message}".'
456
+
457
+ if fr is FinishReason .FINISH_REASON_UNSPECIFIED :
458
+ raise ValueError (msg )
459
+ elif fr is FinishReason .STOP :
460
+ raise ValueError (msg )
461
+ elif fr is FinishReason .MAX_TOKENS :
462
+ raise ValueError (msg )
463
+ elif fr is FinishReason .SAFETY :
464
+ raise ValueError (
465
+ msg + f" The candidate's safety_ratings are: { candidate .safety_ratings } ." ,
466
+ candidate .safety_ratings ,
467
+ )
468
+ elif fr is FinishReason .RECITATION :
469
+ raise ValueError (
470
+ msg + " Meaning that the model was reciting from copyrighted material."
471
+ )
472
+ elif fr is FinishReason .LANGUAGE :
473
+ raise ValueError (msg + " Meaning the response was using an unsupported language." )
474
+ elif fr is FinishReason .OTHER :
475
+ raise ValueError (msg )
476
+ elif fr is FinishReason .BLOCKLIST :
477
+ raise ValueError (msg )
478
+ elif fr is FinishReason .PROHIBITED_CONTENT :
479
+ raise ValueError (msg )
480
+ elif fr is FinishReason .SPII :
481
+ raise ValueError (msg + " SPII - Sensitive Personally Identifiable Information." )
482
+ elif fr is FinishReason .MALFORMED_FUNCTION_CALL :
483
+ raise ValueError (
484
+ msg + " Meaning that model generated a `FunctionCall` that was invalid. "
485
+ "Setting the "
486
+ "[Function calling mode](https://ai.google.dev/gemini-api/docs/function-calling#function_calling_mode) "
487
+ "to `ANY` can fix this because it enables constrained decoding."
488
+ )
489
+ else :
490
+ raise ValueError (msg )
440
491
441
492
texts = []
442
493
for part in parts :
0 commit comments