fix max token error
Browse files- app/gemini_client.py +20 -18
app/gemini_client.py
CHANGED
|
@@ -87,41 +87,43 @@ class GeminiClient:
|
|
| 87 |
# --- START: Cải tiến logic xử lý response ---
|
| 88 |
# 1. Kiểm tra response có hợp lệ không
|
| 89 |
if not response.candidates:
|
| 90 |
-
#
|
| 91 |
-
raise ValueError("Gemini response is missing 'candidates' field.")
|
| 92 |
|
| 93 |
candidate = response.candidates[0]
|
| 94 |
finish_reason_name = getattr(getattr(candidate, 'finish_reason', None), 'name', 'UNKNOWN')
|
| 95 |
# Kiểm tra xem có nội dung thực sự không
|
| 96 |
-
|
|
|
|
| 97 |
|
| 98 |
-
# 2. Phân loại lỗi
|
| 99 |
-
#
|
| 100 |
-
if finish_reason_name != "STOP"
|
| 101 |
usage_metadata = response.usage_metadata if hasattr(response, 'usage_metadata') else None
|
| 102 |
-
|
| 103 |
-
error_message = "Gemini response finished with STOP but has no content parts."
|
| 104 |
-
else:
|
| 105 |
-
error_message = f"Gemini response finished with non-OK reason: {finish_reason_name}."
|
| 106 |
raise GeminiResponseError(
|
| 107 |
-
error_message,
|
| 108 |
-
finish_reason=finish_reason_name if finish_reason_name != "STOP" else "STOP_NO_CONTENT",
|
| 109 |
-
usage_metadata=usage_metadata
|
| 110 |
)
|
| 111 |
|
| 112 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 113 |
self.limit_manager.log_request(key, model, success=True)
|
| 114 |
if hasattr(response, 'usage_metadata'):
|
| 115 |
logger.info(f"[GEMINI][USAGE] Prompt Token Count: {response.usage_metadata.prompt_token_count} - Candidate Token Count: {response.usage_metadata.candidates_token_count} - Total Token Count: {response.usage_metadata.total_token_count}")
|
| 116 |
|
| 117 |
-
# Bọc lại phần truy cập .text để bắt lỗi ValueError một cách an toàn nhất
|
| 118 |
try:
|
| 119 |
logger.info(f"[GEMINI][TEXT_RESPONSE] {_safe_truncate(response.text)}")
|
| 120 |
return response.text
|
| 121 |
except ValueError as ve:
|
| 122 |
-
# Nếu truy cập .text thất bại dù các kiểm tra trước đó đã qua,
|
| 123 |
-
#
|
| 124 |
-
# Chuyển đổi nó thành GeminiResponseError để tầng trên xử lý đúng.
|
| 125 |
usage_metadata = response.usage_metadata if hasattr(response, 'usage_metadata') else None
|
| 126 |
raise GeminiResponseError(
|
| 127 |
f"Gemini response has no valid content part. Original error: {ve}",
|
|
|
|
| 87 |
# --- START: Cải tiến logic xử lý response ---
|
| 88 |
# 1. Kiểm tra response có hợp lệ không
|
| 89 |
if not response.candidates:
|
| 90 |
+
# Lỗi này nên được coi là lỗi tạm thời, thử lại với key/model khác
|
| 91 |
+
raise ValueError("Gemini response is missing 'candidates' field. Retrying...")
|
| 92 |
|
| 93 |
candidate = response.candidates[0]
|
| 94 |
finish_reason_name = getattr(getattr(candidate, 'finish_reason', None), 'name', 'UNKNOWN')
|
| 95 |
# Kiểm tra xem có nội dung thực sự không
|
| 96 |
+
# Sửa: Dùng getattr để tránh AttributeError nếu 'parts' không tồn tại
|
| 97 |
+
has_content = bool(candidate.content and getattr(candidate.content, 'parts', None))
|
| 98 |
|
| 99 |
+
# 2. Phân loại lỗi và xử lý
|
| 100 |
+
# Case 1: Lỗi nội dung không thể thử lại (SAFETY, MAX_TOKENS, etc.)
|
| 101 |
+
if finish_reason_name != "STOP":
|
| 102 |
usage_metadata = response.usage_metadata if hasattr(response, 'usage_metadata') else None
|
| 103 |
+
error_message = f"Gemini response finished with non-OK reason: {finish_reason_name}."
|
|
|
|
|
|
|
|
|
|
| 104 |
raise GeminiResponseError(
|
| 105 |
+
error_message, finish_reason=finish_reason_name, usage_metadata=usage_metadata
|
|
|
|
|
|
|
| 106 |
)
|
| 107 |
|
| 108 |
+
# Case 2: Lỗi có thể thử lại (STOP nhưng không có nội dung)
|
| 109 |
+
if not has_content: # Tại đây, ta biết chắc chắn finish_reason_name là "STOP"
|
| 110 |
+
usage_metadata = response.usage_metadata if hasattr(response, 'usage_metadata') else None
|
| 111 |
+
last_error = GeminiResponseError("Gemini response finished with STOP but has no content parts.", finish_reason='STOP_NO_CONTENT', usage_metadata=usage_metadata)
|
| 112 |
+
logger.warning(f"[GEMINI] Model returned STOP with no content. Retrying with another key/model... (Attempt {attempt + 1}/{max_retries})")
|
| 113 |
+
self.limit_manager.log_request(key, model, success=False, retry_delay=5)
|
| 114 |
+
continue # Thử lại vòng lặp với key/model mới
|
| 115 |
+
|
| 116 |
+
# Case 3: Thành công (STOP và có nội dung)
|
| 117 |
self.limit_manager.log_request(key, model, success=True)
|
| 118 |
if hasattr(response, 'usage_metadata'):
|
| 119 |
logger.info(f"[GEMINI][USAGE] Prompt Token Count: {response.usage_metadata.prompt_token_count} - Candidate Token Count: {response.usage_metadata.candidates_token_count} - Total Token Count: {response.usage_metadata.total_token_count}")
|
| 120 |
|
|
|
|
| 121 |
try:
|
| 122 |
logger.info(f"[GEMINI][TEXT_RESPONSE] {_safe_truncate(response.text)}")
|
| 123 |
return response.text
|
| 124 |
except ValueError as ve:
|
| 125 |
+
# Safety net: Nếu truy cập .text thất bại dù các kiểm tra trước đó đã qua,
|
| 126 |
+
# coi như đây là lỗi STOP_NO_CONTENT và ném ra để tầng trên xử lý.
|
|
|
|
| 127 |
usage_metadata = response.usage_metadata if hasattr(response, 'usage_metadata') else None
|
| 128 |
raise GeminiResponseError(
|
| 129 |
f"Gemini response has no valid content part. Original error: {ve}",
|