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