Spaces:
Running
Running
Update main.py
Browse files
main.py
CHANGED
@@ -844,20 +844,15 @@ async def _call_groq(text: str, summary_type: str) -> Tuple[Optional[str], Optio
|
|
844 |
|
845 |
async def _call_gemini(text: str, summary_type: str, model_name: str) -> Tuple[Optional[str], Optional[str]]:
|
846 |
"""Internal function to call Gemini API. Returns (summary, error_message)."""
|
847 |
-
# Initial check (already present)
|
848 |
global _gemini_api_enabled, HarmCategory, HarmBlockThreshold
|
849 |
if not _gemini_api_enabled:
|
850 |
logger.error(f"[Gemini {model_name}] Called but API is disabled.");
|
851 |
return None, f"Error: AI service (Gemini API) not configured/available."
|
852 |
|
853 |
-
#
|
854 |
-
# Double-check if the necessary types were imported correctly, even if _gemini_api_enabled is True.
|
855 |
-
# This guards against cases where the SDK might be partially available or the import didn't fully register.
|
856 |
if HarmCategory is None or HarmBlockThreshold is None:
|
857 |
logger.error(f"[Gemini {model_name}] Safety setting types (HarmCategory/HarmBlockThreshold) are None. SDK might be partially loaded or initial import failed.")
|
858 |
-
# Provide a user-facing error that points to configuration/internal issue
|
859 |
return None, f"Sorry, an internal configuration error occurred with the AI service ({model_name}). Safety types missing."
|
860 |
-
# --- END ADDED CHECK ---
|
861 |
|
862 |
logger.info(f"[Gemini {model_name}] Generating {summary_type} summary using {model_name}. Input length: {len(text)}")
|
863 |
|
@@ -870,93 +865,118 @@ async def _call_gemini(text: str, summary_type: str, model_name: str) -> Tuple[O
|
|
870 |
text = text[:MAX_INPUT_LENGTH_GEMINI] + "... (Content truncated)"
|
871 |
full_prompt = f"{prompt}\n\n{text}"
|
872 |
|
873 |
-
# Define safety_settings
|
|
|
874 |
try:
|
875 |
-
#
|
|
|
|
|
|
|
876 |
safety_settings = {
|
877 |
HarmCategory.HARM_CATEGORY_HARASSMENT: HarmBlockThreshold.BLOCK_NONE,
|
878 |
HarmCategory.HARM_CATEGORY_HATE_SPEECH: HarmBlockThreshold.BLOCK_NONE,
|
879 |
HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT: HarmBlockThreshold.BLOCK_NONE,
|
880 |
HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT: HarmBlockThreshold.BLOCK_NONE,
|
881 |
-
HarmCategory.HARM_CATEGORY_CIVIC_INTEGRITY
|
882 |
}
|
883 |
-
|
884 |
-
|
885 |
-
|
|
|
886 |
except (NameError, AttributeError) as e:
|
887 |
-
#
|
888 |
-
logger.error(f"[Gemini {model_name}] Unexpected error defining safety settings ({type(e).__name__}): {e}. HarmCategory/HarmBlockThreshold might be invalid
|
889 |
return None, f"Sorry, an internal error occurred configuring the AI service ({model_name}). Safety settings definition failed unexpectedly."
|
890 |
|
891 |
-
#
|
|
|
|
|
|
|
|
|
|
|
892 |
try:
|
893 |
logger.debug(f"[Gemini {model_name}] Initializing model {model_name}")
|
|
|
|
|
|
|
|
|
|
|
894 |
model = genai.GenerativeModel(model_name)
|
895 |
logger.info(f"[Gemini {model_name}] Sending request to Gemini ({model_name})...")
|
896 |
-
request_options = {"timeout": 120}
|
897 |
response = await model.generate_content_async(
|
898 |
-
full_prompt, generation_config=genai.types.GenerationConfig(),
|
899 |
safety_settings=safety_settings, request_options=request_options )
|
900 |
logger.info(f"[Gemini {model_name}] Received response from Gemini.")
|
901 |
|
902 |
-
#
|
903 |
if response.prompt_feedback and response.prompt_feedback.block_reason:
|
904 |
block_reason_str = getattr(response.prompt_feedback.block_reason, 'name', str(response.prompt_feedback.block_reason))
|
905 |
logger.warning(f"[Gemini {model_name}] Request blocked by API. Reason: {block_reason_str}");
|
906 |
return None, f"Sorry, the AI model ({model_name}) blocked the request (Reason: {block_reason_str})."
|
907 |
|
908 |
summary = None; finish_reason_str = 'UNKNOWN'
|
909 |
-
# Check candidates for content and safety blocking
|
910 |
if response.candidates:
|
911 |
candidate = response.candidates[0]
|
912 |
finish_reason_name = getattr(candidate.finish_reason, 'name', None)
|
913 |
-
finish_reason_str = finish_reason_name or 'N/A'
|
914 |
|
915 |
-
# Check if the *candidate* was blocked by safety
|
916 |
if finish_reason_name == 'SAFETY':
|
917 |
-
|
|
|
|
|
|
|
918 |
logger.warning(f"[Gemini {model_name}] Candidate blocked due to SAFETY. Finish Reason: {finish_reason_str}. Ratings: [{safety_ratings_str}]")
|
919 |
-
# Provide a more specific error message about response blocking
|
920 |
return None, f"Sorry, the AI model ({model_name}) generated a response that was blocked due to safety filters ({finish_reason_str})."
|
921 |
-
elif finish_reason_name not in ['STOP', 'MAX_TOKENS', None]:
|
922 |
logger.warning(f"[Gemini {model_name}] Candidate finished with non-standard reason: {finish_reason_str}")
|
923 |
|
924 |
-
#
|
925 |
if candidate.content and candidate.content.parts:
|
926 |
summary = "".join(part.text for part in candidate.content.parts if hasattr(part, 'text'))
|
927 |
|
928 |
-
# Fallback
|
929 |
if summary is None:
|
930 |
-
try:
|
931 |
-
|
932 |
-
|
933 |
-
|
934 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
935 |
if summary:
|
936 |
logger.info(f"[Gemini {model_name}] Success generating summary. Finish Reason: {finish_reason_str}. Output len: {len(summary)}");
|
937 |
return summary.strip(), None
|
938 |
else:
|
939 |
-
# If no summary after checking candidate and response.text, report failure
|
940 |
logger.warning(f"[Gemini {model_name}] Gemini returned empty summary or content was blocked. Final Finish Reason: {finish_reason_str}");
|
|
|
|
|
|
|
941 |
return None, f"Sorry, the AI model ({model_name}) did not provide a summary (Finish Reason: {finish_reason_str})."
|
942 |
|
|
|
943 |
except AttributeError as ae:
|
944 |
-
# This can happen if the response structure changes or expected fields are missing
|
945 |
logger.error(f"[Gemini {model_name}] AttributeError during Gemini response processing: {ae}. SDK might be incompatible or response structure unexpected.", exc_info=True);
|
946 |
return None, f"Sorry, there was an issue processing the response from the AI service ({model_name}). Attribute error."
|
947 |
-
#
|
948 |
-
except ImportError:
|
949 |
logger.warning("[Gemini] google.api_core.exceptions not available for specific error handling.")
|
950 |
-
pass
|
951 |
except google.api_core.exceptions.InvalidArgument as iae:
|
952 |
-
|
953 |
-
logger.error(f"[Gemini {model_name}] Invalid Argument error from Gemini API: {iae}", exc_info=False) # Don't need full stack trace often
|
954 |
error_detail = str(iae)
|
955 |
user_message = f"Sorry, the AI service ({model_name}) reported an invalid argument."
|
956 |
-
# Add more specific messages based on common InvalidArgument errors
|
957 |
if "safety_settings" in error_detail: user_message += " (Safety Settings issue)."
|
958 |
elif "temperature" in error_detail: user_message += " (Generation Parameter issue)."
|
959 |
elif "API key not valid" in error_detail: user_message = f"Error: The API key for the AI service ({model_name}) is invalid."
|
|
|
|
|
960 |
return None, user_message
|
961 |
except google.api_core.exceptions.PermissionDenied as pde:
|
962 |
logger.error(f"[Gemini {model_name}] Permission Denied error from Gemini API: {pde}", exc_info=False)
|
@@ -967,18 +987,17 @@ async def _call_gemini(text: str, summary_type: str, model_name: str) -> Tuple[O
|
|
967 |
user_message = f"Sorry, the AI model ({model_name}) is busy or quota exceeded. Please try again later."
|
968 |
return None, user_message
|
969 |
except google.api_core.exceptions.GoogleAPIError as gae:
|
970 |
-
# Catch other potential Google API errors
|
971 |
logger.error(f"[Gemini {model_name}] Google API error during Gemini call: {gae}", exc_info=False)
|
972 |
-
status_code = getattr(gae, 'code', 'Unknown')
|
973 |
user_message = f"Sorry, the AI service ({model_name}) encountered an API error (Code: {status_code})."
|
974 |
-
# Add specific known codes if needed, ResourceExhausted (429) handled above
|
975 |
if status_code == 500: user_message = f"Sorry, the AI service ({model_name}) had an internal server error."
|
976 |
-
elif status_code == 400: user_message = f"Sorry, there was a bad request sending data to the AI service ({model_name})."
|
977 |
return None, user_message
|
978 |
except Exception as e:
|
979 |
-
# General catch-all for unexpected issues (e.g., network errors not caught by httpx inside SDK)
|
980 |
logger.error(f"[Gemini {model_name}] Unexpected error during Gemini API call: {e}", exc_info=True);
|
981 |
error_msg = f"Sorry, an unexpected error occurred while using the AI service ({model_name})."
|
|
|
|
|
982 |
return None, error_msg
|
983 |
|
984 |
|
|
|
844 |
|
845 |
async def _call_gemini(text: str, summary_type: str, model_name: str) -> Tuple[Optional[str], Optional[str]]:
|
846 |
"""Internal function to call Gemini API. Returns (summary, error_message)."""
|
|
|
847 |
global _gemini_api_enabled, HarmCategory, HarmBlockThreshold
|
848 |
if not _gemini_api_enabled:
|
849 |
logger.error(f"[Gemini {model_name}] Called but API is disabled.");
|
850 |
return None, f"Error: AI service (Gemini API) not configured/available."
|
851 |
|
852 |
+
# Explicit check for the types themselves
|
|
|
|
|
853 |
if HarmCategory is None or HarmBlockThreshold is None:
|
854 |
logger.error(f"[Gemini {model_name}] Safety setting types (HarmCategory/HarmBlockThreshold) are None. SDK might be partially loaded or initial import failed.")
|
|
|
855 |
return None, f"Sorry, an internal configuration error occurred with the AI service ({model_name}). Safety types missing."
|
|
|
856 |
|
857 |
logger.info(f"[Gemini {model_name}] Generating {summary_type} summary using {model_name}. Input length: {len(text)}")
|
858 |
|
|
|
865 |
text = text[:MAX_INPUT_LENGTH_GEMINI] + "... (Content truncated)"
|
866 |
full_prompt = f"{prompt}\n\n{text}"
|
867 |
|
868 |
+
# Define safety_settings
|
869 |
+
safety_settings = {} # Initialize empty dict
|
870 |
try:
|
871 |
+
# --- CORRECTED SAFETY SETTINGS ---
|
872 |
+
# Define settings only for categories confirmed to exist in the SDK version being used.
|
873 |
+
# HARM_CATEGORY_CIVIC_INTEGRITY caused an AttributeError, so it's removed.
|
874 |
+
# The goal is to set all *available* categories to BLOCK_NONE.
|
875 |
safety_settings = {
|
876 |
HarmCategory.HARM_CATEGORY_HARASSMENT: HarmBlockThreshold.BLOCK_NONE,
|
877 |
HarmCategory.HARM_CATEGORY_HATE_SPEECH: HarmBlockThreshold.BLOCK_NONE,
|
878 |
HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT: HarmBlockThreshold.BLOCK_NONE,
|
879 |
HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT: HarmBlockThreshold.BLOCK_NONE,
|
880 |
+
# HarmCategory.HARM_CATEGORY_CIVIC_INTEGRITY is REMOVED as it's not defined in the SDK version
|
881 |
}
|
882 |
+
# Log the actual settings being used
|
883 |
+
settings_applied_str = ", ".join([k.name for k in safety_settings.keys()])
|
884 |
+
logger.debug(f"[Gemini {model_name}] Applying BLOCK_NONE to available safety categories: [{settings_applied_str}]")
|
885 |
+
|
886 |
except (NameError, AttributeError) as e:
|
887 |
+
# This catch block remains as a safeguard.
|
888 |
+
logger.error(f"[Gemini {model_name}] Unexpected error defining safety settings ({type(e).__name__}): {e}. HarmCategory/HarmBlockThreshold might be invalid.", exc_info=True)
|
889 |
return None, f"Sorry, an internal error occurred configuring the AI service ({model_name}). Safety settings definition failed unexpectedly."
|
890 |
|
891 |
+
# Check if safety_settings dictionary is empty (shouldn't happen if the 4 base categories exist)
|
892 |
+
if not safety_settings:
|
893 |
+
logger.error(f"[Gemini {model_name}] Failed to define any safety settings. Cannot proceed with Gemini call.")
|
894 |
+
return None, f"Sorry, an internal error occurred configuring the AI service ({model_name}). No safety settings could be defined."
|
895 |
+
|
896 |
+
# --- API call logic remains the same ---
|
897 |
try:
|
898 |
logger.debug(f"[Gemini {model_name}] Initializing model {model_name}")
|
899 |
+
# Make sure genai is accessible
|
900 |
+
if genai is None:
|
901 |
+
logger.error(f"[Gemini {model_name}] GenAI SDK object (genai) is None. Cannot initialize model.")
|
902 |
+
return None, f"Sorry, the AI service ({model_name}) SDK is not properly initialized."
|
903 |
+
|
904 |
model = genai.GenerativeModel(model_name)
|
905 |
logger.info(f"[Gemini {model_name}] Sending request to Gemini ({model_name})...")
|
906 |
+
request_options = {"timeout": 120}
|
907 |
response = await model.generate_content_async(
|
908 |
+
full_prompt, generation_config=genai.types.GenerationConfig(),
|
909 |
safety_settings=safety_settings, request_options=request_options )
|
910 |
logger.info(f"[Gemini {model_name}] Received response from Gemini.")
|
911 |
|
912 |
+
# Response handling logic (prompt feedback, candidates, summary extraction) remains unchanged...
|
913 |
if response.prompt_feedback and response.prompt_feedback.block_reason:
|
914 |
block_reason_str = getattr(response.prompt_feedback.block_reason, 'name', str(response.prompt_feedback.block_reason))
|
915 |
logger.warning(f"[Gemini {model_name}] Request blocked by API. Reason: {block_reason_str}");
|
916 |
return None, f"Sorry, the AI model ({model_name}) blocked the request (Reason: {block_reason_str})."
|
917 |
|
918 |
summary = None; finish_reason_str = 'UNKNOWN'
|
|
|
919 |
if response.candidates:
|
920 |
candidate = response.candidates[0]
|
921 |
finish_reason_name = getattr(candidate.finish_reason, 'name', None)
|
922 |
+
finish_reason_str = finish_reason_name or 'N/A'
|
923 |
|
|
|
924 |
if finish_reason_name == 'SAFETY':
|
925 |
+
# Log safety ratings if available
|
926 |
+
safety_ratings_str = "N/A"
|
927 |
+
if hasattr(candidate, 'safety_ratings'):
|
928 |
+
safety_ratings_str = ", ".join([f"{rating.category.name}: {rating.probability.name}" for rating in candidate.safety_ratings])
|
929 |
logger.warning(f"[Gemini {model_name}] Candidate blocked due to SAFETY. Finish Reason: {finish_reason_str}. Ratings: [{safety_ratings_str}]")
|
|
|
930 |
return None, f"Sorry, the AI model ({model_name}) generated a response that was blocked due to safety filters ({finish_reason_str})."
|
931 |
+
elif finish_reason_name not in ['STOP', 'MAX_TOKENS', None]:
|
932 |
logger.warning(f"[Gemini {model_name}] Candidate finished with non-standard reason: {finish_reason_str}")
|
933 |
|
934 |
+
# Check for content within the candidate
|
935 |
if candidate.content and candidate.content.parts:
|
936 |
summary = "".join(part.text for part in candidate.content.parts if hasattr(part, 'text'))
|
937 |
|
938 |
+
# Fallback to response.text if no summary extracted from candidate
|
939 |
if summary is None:
|
940 |
+
try:
|
941 |
+
# Check if response.text exists before accessing
|
942 |
+
if hasattr(response, 'text'):
|
943 |
+
summary = response.text
|
944 |
+
else:
|
945 |
+
logger.warning(f"[Gemini {model_name}] Response object lacks 'text' attribute.")
|
946 |
+
summary = None # Ensure summary remains None
|
947 |
+
except ValueError as e: # This often indicates blocked content
|
948 |
+
logger.warning(f"[Gemini {model_name}] Error accessing response.text (likely blocked/no content): {e}"); summary = None
|
949 |
+
except Exception as e:
|
950 |
+
logger.warning(f"[Gemini {model_name}] Unexpected error accessing response.text: {e}"); summary = None
|
951 |
+
|
952 |
+
# Final check and return
|
953 |
if summary:
|
954 |
logger.info(f"[Gemini {model_name}] Success generating summary. Finish Reason: {finish_reason_str}. Output len: {len(summary)}");
|
955 |
return summary.strip(), None
|
956 |
else:
|
|
|
957 |
logger.warning(f"[Gemini {model_name}] Gemini returned empty summary or content was blocked. Final Finish Reason: {finish_reason_str}");
|
958 |
+
# Provide a reason if known (e.g., Safety block)
|
959 |
+
if finish_reason_str == 'SAFETY':
|
960 |
+
return None, f"Sorry, the AI model ({model_name}) response was blocked by safety filters."
|
961 |
return None, f"Sorry, the AI model ({model_name}) did not provide a summary (Finish Reason: {finish_reason_str})."
|
962 |
|
963 |
+
# --- Error handling logic remains the same ---
|
964 |
except AttributeError as ae:
|
|
|
965 |
logger.error(f"[Gemini {model_name}] AttributeError during Gemini response processing: {ae}. SDK might be incompatible or response structure unexpected.", exc_info=True);
|
966 |
return None, f"Sorry, there was an issue processing the response from the AI service ({model_name}). Attribute error."
|
967 |
+
# Import google.api_core.exceptions if needed (should be loaded if SDK works)
|
968 |
+
except ImportError:
|
969 |
logger.warning("[Gemini] google.api_core.exceptions not available for specific error handling.")
|
970 |
+
pass
|
971 |
except google.api_core.exceptions.InvalidArgument as iae:
|
972 |
+
logger.error(f"[Gemini {model_name}] Invalid Argument error from Gemini API: {iae}", exc_info=False)
|
|
|
973 |
error_detail = str(iae)
|
974 |
user_message = f"Sorry, the AI service ({model_name}) reported an invalid argument."
|
|
|
975 |
if "safety_settings" in error_detail: user_message += " (Safety Settings issue)."
|
976 |
elif "temperature" in error_detail: user_message += " (Generation Parameter issue)."
|
977 |
elif "API key not valid" in error_detail: user_message = f"Error: The API key for the AI service ({model_name}) is invalid."
|
978 |
+
elif "model" in error_detail and ("not found" in error_detail or "does not exist" in error_detail): user_message = f"Error: The AI model name '{model_name}' was not found by the API."
|
979 |
+
elif "HarmCategory" in error_detail: user_message += " (Unsupported safety category passed)." # More specific error if it happens again
|
980 |
return None, user_message
|
981 |
except google.api_core.exceptions.PermissionDenied as pde:
|
982 |
logger.error(f"[Gemini {model_name}] Permission Denied error from Gemini API: {pde}", exc_info=False)
|
|
|
987 |
user_message = f"Sorry, the AI model ({model_name}) is busy or quota exceeded. Please try again later."
|
988 |
return None, user_message
|
989 |
except google.api_core.exceptions.GoogleAPIError as gae:
|
|
|
990 |
logger.error(f"[Gemini {model_name}] Google API error during Gemini call: {gae}", exc_info=False)
|
991 |
+
status_code = getattr(gae, 'code', 'Unknown')
|
992 |
user_message = f"Sorry, the AI service ({model_name}) encountered an API error (Code: {status_code})."
|
|
|
993 |
if status_code == 500: user_message = f"Sorry, the AI service ({model_name}) had an internal server error."
|
994 |
+
elif status_code == 400: user_message = f"Sorry, there was a bad request sending data to the AI service ({model_name})."
|
995 |
return None, user_message
|
996 |
except Exception as e:
|
|
|
997 |
logger.error(f"[Gemini {model_name}] Unexpected error during Gemini API call: {e}", exc_info=True);
|
998 |
error_msg = f"Sorry, an unexpected error occurred while using the AI service ({model_name})."
|
999 |
+
if isinstance(e, NameError) and 'genai' in str(e):
|
1000 |
+
error_msg = "Sorry, the AI service SDK is not properly initialized (internal error)."
|
1001 |
return None, error_msg
|
1002 |
|
1003 |
|