fmab777 commited on
Commit
6c98f16
·
verified ·
1 Parent(s): 12d9ac3

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +63 -44
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
- # --- ADDED EXPLICIT CHECK ---
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 - Now guarded by the explicit check above
 
874
  try:
875
- # These constants should now be valid if the check above passed
 
 
 
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: HarmBlockThreshold.BLOCK_NONE, # Present in previous code and docs
882
  }
883
- logger.debug(f"[Gemini {model_name}] Using safety settings: { {k.name: v.name for k, v in safety_settings.items()} }")
884
- # Keep the original NameError/AttributeError check as a final fallback,
885
- # though the explicit None check should ideally prevent reaching here with this specific error.
 
886
  except (NameError, AttributeError) as e:
887
- # Log with more context if this somehow still happens
888
- logger.error(f"[Gemini {model_name}] Unexpected error defining safety settings ({type(e).__name__}): {e}. HarmCategory/HarmBlockThreshold might be invalid despite passing initial checks.", 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
- # --- Rest of the function (API call, response handling) remains the same as the previous version ---
 
 
 
 
 
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} # Keep timeout reasonable
897
  response = await model.generate_content_async(
898
- full_prompt, generation_config=genai.types.GenerationConfig(), # Use default config
899
  safety_settings=safety_settings, request_options=request_options )
900
  logger.info(f"[Gemini {model_name}] Received response from Gemini.")
901
 
902
- # Check for prompt blocking first
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' # Default if None
914
 
915
- # Check if the *candidate* was blocked by safety
916
  if finish_reason_name == 'SAFETY':
917
- safety_ratings_str = ", ".join([f"{rating.category.name}: {rating.probability.name}" for rating in candidate.safety_ratings])
 
 
 
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]: # Log other unusual finish reasons
922
  logger.warning(f"[Gemini {model_name}] Candidate finished with non-standard reason: {finish_reason_str}")
923
 
924
- # Extract content if available
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: try accessing response.text directly (might raise ValueError if blocked)
929
  if summary is None:
930
- try: summary = response.text
931
- except ValueError as e: logger.warning(f"[Gemini {model_name}] Error accessing response.text (likely blocked/no content): {e}"); summary = None
932
- except Exception as e: logger.warning(f"[Gemini {model_name}] Unexpected error accessing response.text: {e}"); summary = None
933
-
934
- # Final check if we got a summary
 
 
 
 
 
 
 
 
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
- # Use google.api_core.exceptions if available, otherwise use general Exception
948
- except ImportError: # Handle if google.api_core isn't available for some reason
949
  logger.warning("[Gemini] google.api_core.exceptions not available for specific error handling.")
950
- pass # Fall through to general Exception
951
  except google.api_core.exceptions.InvalidArgument as iae:
952
- # Catch specific InvalidArgument, often related to safety settings or model params
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') # Use code if available
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})." # Often model name typo or bad input structure
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