fmab777 commited on
Commit
6970f77
·
verified ·
1 Parent(s): ac0ddee

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +131 -71
main.py CHANGED
@@ -851,73 +851,105 @@ async def _call_gemini(text: str, summary_type: str, model_name: str) -> Tuple[O
851
  logger.info(f"[Gemini {model_name}] Generating {summary_type} summary using {model_name}. Input length: {len(text)}")
852
 
853
  prompt = PROMPT_PARAGRAPH if summary_type == "paragraph" else PROMPT_POINTS
 
 
854
  MAX_INPUT_LENGTH_GEMINI = 900000
 
 
855
  if len(text) > MAX_INPUT_LENGTH_GEMINI:
856
- logger.warning(f"[Gemini {model_name}] Input length ({len(text)}) exceeds limit ({MAX_INPUT_LENGTH_GEMINI}). Truncating.");
857
  text = text[:MAX_INPUT_LENGTH_GEMINI] + "... (Content truncated)"
858
  full_prompt = f"{prompt}\n\n{text}"
859
 
860
- # FIX: Define safety_settings explicitly with the 5 categories from the error log
861
- # Removed the problematic loop that dynamically added potentially unsupported categories.
862
  try:
863
  safety_settings = {
864
  HarmCategory.HARM_CATEGORY_HARASSMENT: HarmBlockThreshold.BLOCK_NONE,
865
  HarmCategory.HARM_CATEGORY_HATE_SPEECH: HarmBlockThreshold.BLOCK_NONE,
866
  HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT: HarmBlockThreshold.BLOCK_NONE,
867
  HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT: HarmBlockThreshold.BLOCK_NONE,
868
- HarmCategory.HARM_CATEGORY_CIVIC_INTEGRITY: HarmBlockThreshold.BLOCK_NONE, # Added based on error msg
869
  }
870
  logger.debug(f"[Gemini {model_name}] Using safety settings: { {k.name: v.name for k, v in safety_settings.items()} }")
871
- except AttributeError:
872
- # Handle case where HarmCategory or HarmBlockThreshold might not be loaded correctly
873
- # This shouldn't happen if _gemini_sdk_available is True, but defensive coding
874
- logger.error(f"[Gemini {model_name}] Failed to define safety settings. HarmCategory/HarmBlockThreshold missing?")
875
- return None, f"Sorry, an internal error occurred configuring the AI service ({model_name})."
876
 
877
  try:
878
  logger.debug(f"[Gemini {model_name}] Initializing model {model_name}")
879
  model = genai.GenerativeModel(model_name)
880
  logger.info(f"[Gemini {model_name}] Sending request to Gemini ({model_name})...")
881
- request_options = {"timeout": 120}
882
  response = await model.generate_content_async(
883
- full_prompt, generation_config=genai.types.GenerationConfig(),
884
  safety_settings=safety_settings, request_options=request_options )
885
  logger.info(f"[Gemini {model_name}] Received response from Gemini.")
 
 
886
  if response.prompt_feedback and response.prompt_feedback.block_reason:
887
  block_reason_str = getattr(response.prompt_feedback.block_reason, 'name', str(response.prompt_feedback.block_reason))
888
  logger.warning(f"[Gemini {model_name}] Request blocked by API. Reason: {block_reason_str}");
889
  return None, f"Sorry, the AI model ({model_name}) blocked the request (Reason: {block_reason_str})."
 
890
  summary = None; finish_reason_str = 'UNKNOWN'
 
891
  if response.candidates:
892
  candidate = response.candidates[0]
893
  finish_reason_name = getattr(candidate.finish_reason, 'name', None)
894
- finish_reason_str = finish_reason_name or 'N/A'
 
 
895
  if finish_reason_name == 'SAFETY':
896
  safety_ratings_str = ", ".join([f"{rating.category.name}: {rating.probability.name}" for rating in candidate.safety_ratings])
897
  logger.warning(f"[Gemini {model_name}] Candidate blocked due to SAFETY. Finish Reason: {finish_reason_str}. Ratings: [{safety_ratings_str}]")
898
- return None, f"Sorry, the AI model ({model_name}) blocked the response due to safety filters ({finish_reason_str})."
899
- elif finish_reason_name not in ['STOP', 'MAX_TOKENS', None]:
 
900
  logger.warning(f"[Gemini {model_name}] Candidate finished with non-standard reason: {finish_reason_str}")
 
 
901
  if candidate.content and candidate.content.parts:
902
  summary = "".join(part.text for part in candidate.content.parts if hasattr(part, 'text'))
 
 
903
  if summary is None:
904
  try: summary = response.text
905
- except ValueError as e: logger.warning(f"[Gemini {model_name}] Error accessing response.text (likely blocked): {e}"); summary = None
 
 
 
906
  if summary:
907
  logger.info(f"[Gemini {model_name}] Success generating summary. Finish Reason: {finish_reason_str}. Output len: {len(summary)}");
908
  return summary.strip(), None
909
  else:
 
910
  logger.warning(f"[Gemini {model_name}] Gemini returned empty summary or content was blocked. Final Finish Reason: {finish_reason_str}");
911
  return None, f"Sorry, the AI model ({model_name}) did not provide a summary (Finish Reason: {finish_reason_str})."
 
912
  except AttributeError as ae:
 
913
  logger.error(f"[Gemini {model_name}] AttributeError during Gemini response processing: {ae}. SDK might be incompatible or response structure unexpected.", exc_info=True);
914
- return None, f"Sorry, there was an issue processing the response from the AI service ({model_name})."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
915
  except Exception as e:
 
916
  logger.error(f"[Gemini {model_name}] Unexpected error during Gemini API call: {e}", exc_info=True);
917
- # Check if it's the specific InvalidArgument error we were seeing
918
- if isinstance(e, google.api_core.exceptions.InvalidArgument) and "safety_settings" in str(e):
919
- logger.error(f"[Gemini {model_name}] Safety settings InvalidArgument error persisted: {e}")
920
- return None, f"Sorry, the AI service ({model_name}) reported an invalid configuration (Safety Settings). Please report this issue."
921
  error_msg = f"Sorry, an unexpected error occurred while using the AI service ({model_name})."
922
  return None, error_msg
923
 
@@ -984,80 +1016,96 @@ async def _call_openrouter(text: str, summary_type: str) -> Tuple[Optional[str],
984
 
985
  async def generate_summary(text: str, summary_type: str) -> str:
986
  """
987
- Generates summary using the specific model hierarchy (April 2025):
988
- 1. Groq (Llama 4 Scout)
989
- 2. Gemini (2.5 Pro Exp)
990
- 3. Gemini (2.0 Flash)
991
  4. OpenRouter (DeepSeek V3 Free)
 
992
  Returns the summary text or a comprehensive error message.
993
  """
994
- global _groq_enabled, _gemini_api_enabled, _openrouter_fallback_enabled
995
- global GROQ_LLAMA4_MODEL, GEMINI_PRO_EXP_MODEL, GEMINI_FLASH_MODEL, OPENROUTER_DEEPSEEK_MODEL
996
 
997
- logger.info("[Summary Generation] Starting process with specific April 2025 model hierarchy.")
998
  summary: Optional[str] = None
 
999
  errors: Dict[str, Optional[str]] = {
1000
- "Llama4Scout": None,
1001
- "GeminiProExp": None,
1002
- "GeminiFlash": None,
1003
- "DeepSeekV3": None,
 
1004
  }
1005
 
1006
- # --- Attempt 1: Groq (Llama 4 Scout) ---
1007
- if _groq_enabled:
1008
- logger.info(f"[Summary Generation] Attempting 1: Groq ({GROQ_LLAMA4_MODEL})")
1009
- summary, errors["Llama4Scout"] = await _call_groq(text, summary_type)
1010
  if summary:
1011
- logger.info(f"[Summary Generation] Success with Groq ({GROQ_LLAMA4_MODEL}).")
1012
  return summary
1013
  else:
1014
- logger.warning(f"[Summary Generation] Groq Llama 4 Scout failed. Error: {errors['Llama4Scout']}. Proceeding to Gemini 2.5 Pro Exp.")
1015
  else:
1016
- logger.warning("[Summary Generation] Groq (Llama 4 Scout) is disabled or unavailable. Skipping.")
1017
- errors["Llama4Scout"] = "Service disabled/unavailable."
 
 
1018
 
1019
  # --- Attempt 2: Gemini 2.5 Pro Exp ---
1020
- if _gemini_api_enabled:
 
1021
  logger.info(f"[Summary Generation] Attempting 2: Gemini ({GEMINI_PRO_EXP_MODEL})")
1022
- summary, errors["GeminiProExp"] = await _call_gemini(text, summary_type, GEMINI_PRO_EXP_MODEL)
1023
  if summary:
1024
  logger.info(f"[Summary Generation] Success with Gemini ({GEMINI_PRO_EXP_MODEL}).")
1025
  return summary
1026
  else:
1027
- logger.warning(f"[Summary Generation] Gemini 2.5 Pro Exp failed. Error: {errors['GeminiProExp']}. Proceeding to Gemini 2.0 Flash.")
1028
- else:
1029
- logger.warning("[Summary Generation] Gemini API is disabled or unavailable. Skipping Gemini 2.5 Pro Exp & 2.0 Flash.")
1030
- errors["GeminiProExp"] = "Service disabled/unavailable."
1031
- errors["GeminiFlash"] = "Service disabled/unavailable."
1032
 
1033
  # --- Attempt 3: Gemini 2.0 Flash ---
1034
- if _gemini_api_enabled and errors["GeminiFlash"] is None: # Check if Gemini API is enabled AND wasn't already marked as failed/skipped
 
1035
  logger.info(f"[Summary Generation] Attempting 3: Gemini ({GEMINI_FLASH_MODEL})")
1036
- summary, errors["GeminiFlash"] = await _call_gemini(text, summary_type, GEMINI_FLASH_MODEL)
1037
  if summary:
1038
  logger.info(f"[Summary Generation] Success with Gemini ({GEMINI_FLASH_MODEL}).")
1039
  return summary
1040
  else:
1041
- logger.warning(f"[Summary Generation] Gemini 2.0 Flash failed. Error: {errors['GeminiFlash']}. Proceeding to OpenRouter DeepSeek V3.")
1042
- elif errors["GeminiFlash"] is None: # Only log skip message if it wasn't already marked as disabled
1043
- logger.warning("[Summary Generation] Skipping Gemini 2.0 Flash (API was disabled).")
1044
- errors["GeminiFlash"] = "Service disabled/unavailable."
1045
 
1046
- # --- Attempt 4: OpenRouter (DeepSeek V3 Free - Final Fallback) ---
1047
  if _openrouter_fallback_enabled:
1048
  logger.info(f"[Summary Generation] Attempting 4: OpenRouter ({OPENROUTER_DEEPSEEK_MODEL})")
1049
- summary, errors["DeepSeekV3"] = await _call_openrouter(text, summary_type)
1050
  if summary:
1051
  logger.info(f"[Summary Generation] Success with OpenRouter ({OPENROUTER_DEEPSEEK_MODEL}).")
1052
  return summary
1053
  else:
1054
- logger.error(f"[Summary Generation] OpenRouter DeepSeek V3 (Final Fallback) also failed. Error: {errors['DeepSeekV3']}")
 
 
 
 
 
 
 
 
 
 
 
 
 
1055
  else:
1056
- logger.error("[Summary Generation] OpenRouter fallback (DeepSeek V3) is disabled or unavailable. Cannot proceed.")
1057
- errors["DeepSeekV3"] = "Service disabled/unavailable."
1058
 
1059
  # --- All Attempts Failed ---
1060
  logger.error("[Summary Generation] All summarization models failed.")
 
1061
  error_details = "\n".join([f"- {model}: {err}" for model, err in errors.items() if err])
1062
  return f"Sorry, I couldn't generate a summary after trying all available AI models.\nDetails:\n{error_details}"
1063
 
@@ -1219,12 +1267,13 @@ async def help_command(update: Update, context: ContextTypes.DEFAULT_TYPE) -> No
1219
  user = update.effective_user
1220
  if not user or not update.message: return
1221
  logger.info(f"User {user.id} ({user.username or 'no_username'}) used /help.")
 
1222
  help_text = ( "🔍 **How to use this bot:**\n\n"
1223
  "1. Send me any YouTube video link or website URL.\n"
1224
  "2. I'll ask how you want it summarised (paragraph or points).\n"
1225
  "3. Click the button for your choice.\n"
1226
  "4. Wait while I fetch the content and generate the summary!\n\n"
1227
- "⚙️ I try multiple methods to get content, especially for tricky websites or YouTube videos without standard transcripts. I then use a sequence of AI models (Llama 4 Scout, Gemini 2.5 Pro, Gemini 2.0 Flash, DeepSeek V3) to summarise.\n\n" # Updated help text
1228
  "**Commands:**\n"
1229
  "`/start` - Display the welcome message\n"
1230
  "`/help` - Show this help message" )
@@ -1388,48 +1437,59 @@ async def lifespan(app: Starlette):
1388
 
1389
  async def health_check(request: Request) -> PlainTextResponse:
1390
  """Simple health check endpoint."""
1391
- global GROQ_LLAMA4_MODEL, GEMINI_PRO_EXP_MODEL, GEMINI_FLASH_MODEL, OPENROUTER_DEEPSEEK_MODEL
1392
- global APIFY_ACTOR_ID, APIFY_STRUCTURED_YT_ACTOR_ID # Add new ID here
 
1393
  global _groq_enabled, _gemini_api_enabled, _openrouter_fallback_enabled
1394
  global _apify_token_exists, _urltotext_key_exists, _rapidapi_key_exists, SUPADATA_API_KEY
1395
 
1396
- # ... (rest of the bot status checking logic remains the same) ...
1397
  bot_status = "Not Initialized"; bot_username = "N/A"
1398
  if ptb_app:
1399
  try:
 
1400
  if ptb_app.running:
1401
- bot_info = await ptb_app.bot.get_me(); bot_username = f"@{bot_info.username}" if bot_info and bot_info.username else "Info Fetch Error"; bot_status = "Running"
 
 
 
 
 
1402
  else:
1403
  bot_status = "Initialized but Not Running"
 
1404
  if ptb_app.bot:
1405
  try: bot_info = await ptb_app.bot.get_me(); bot_username = f"@{bot_info.username}" if bot_info and bot_info.username else "Info Fetch Error"
1406
- except Exception: bot_username = "Info Fetch Error (Not Running)"
1407
- except (TimedOut, NetworkError, Exception) as e:
1408
- bot_status = f"Error checking status: {type(e).__name__}"; logger.warning(f"Health check: Error getting bot info: {e}")
 
 
 
1409
  else: bot_status = "Not Initialized"; bot_username = "N/A"
1410
 
1411
 
1412
- # <<< Update response string with specific model names AND YT Fallback order >>>
1413
  return PlainTextResponse(
1414
  f"TG Bot Summariser - Status: {bot_status} ({bot_username})\n"
1415
  f"---\n"
1416
- f"Summarizer Priority (April 2025 - Specific):\n"
1417
- f"1. Groq API: {GROQ_LLAMA4_MODEL if _groq_enabled else 'DISABLED'}\n"
1418
  f"2. Gemini API: {GEMINI_PRO_EXP_MODEL if _gemini_api_enabled else 'DISABLED'}\n"
1419
  f"3. Gemini API: {GEMINI_FLASH_MODEL if _gemini_api_enabled else 'DISABLED'}\n"
1420
  f"4. OpenRouter API: {OPENROUTER_DEEPSEEK_MODEL if _openrouter_fallback_enabled else 'DISABLED'}\n"
 
1421
  f"---\n"
1422
  f"Content Fetching Status:\n"
1423
- # --- Updated YT Fallback List ---
1424
  f"YT Primary (Lib): Enabled\n"
1425
  f"YT Fallback 1 (Apify Structured): {APIFY_STRUCTURED_YT_ACTOR_ID if _apify_token_exists else 'DISABLED'}\n"
1426
  f"YT Fallback 2 (Supadata): {'Enabled' if SUPADATA_API_KEY else 'Disabled'}\n"
1427
  f"YT Fallback 3 (Apify Default): {APIFY_ACTOR_ID if _apify_token_exists else 'DISABLED'}\n"
1428
- # --- End Updated List ---
1429
  f"Web Scrape 1 (Direct+BS4): Enabled\n"
1430
  f"Web Scrape 2 (urltotext): {'Enabled' if _urltotext_key_exists else 'Disabled'}\n"
1431
  f"Web Scrape 3/4 (RapidAPI): {'Enabled' if _rapidapi_key_exists else 'Disabled'}\n"
1432
- f"Web Scrape 5/6 (Apify Actors): {'Enabled' if _apify_token_exists else 'Disabled'}" # Web scrape order already updated previously
1433
  )
1434
 
1435
  async def telegram_webhook(request: Request) -> Response:
 
851
  logger.info(f"[Gemini {model_name}] Generating {summary_type} summary using {model_name}. Input length: {len(text)}")
852
 
853
  prompt = PROMPT_PARAGRAPH if summary_type == "paragraph" else PROMPT_POINTS
854
+ # Determine max input length based on model - Flash Preview is likely 1M like Flash 2.0, Pro Exp is 2M
855
+ # Use a conservative estimate for safety, but larger than Llama/Deepseek
856
  MAX_INPUT_LENGTH_GEMINI = 900000
857
+ if 'pro-exp' in model_name.lower(): MAX_INPUT_LENGTH_GEMINI = 1800000 # Higher for Pro
858
+
859
  if len(text) > MAX_INPUT_LENGTH_GEMINI:
860
+ logger.warning(f"[Gemini {model_name}] Input length ({len(text)}) exceeds estimated limit ({MAX_INPUT_LENGTH_GEMINI}). Truncating.");
861
  text = text[:MAX_INPUT_LENGTH_GEMINI] + "... (Content truncated)"
862
  full_prompt = f"{prompt}\n\n{text}"
863
 
864
+ # Define safety_settings explicitly with the 5 known categories set to BLOCK_NONE.
865
+ # This satisfies the requirement "make sure that all safety censors are set to none".
866
  try:
867
  safety_settings = {
868
  HarmCategory.HARM_CATEGORY_HARASSMENT: HarmBlockThreshold.BLOCK_NONE,
869
  HarmCategory.HARM_CATEGORY_HATE_SPEECH: HarmBlockThreshold.BLOCK_NONE,
870
  HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT: HarmBlockThreshold.BLOCK_NONE,
871
  HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT: HarmBlockThreshold.BLOCK_NONE,
872
+ HarmCategory.HARM_CATEGORY_CIVIC_INTEGRITY: HarmBlockThreshold.BLOCK_NONE, # Present in previous code
873
  }
874
  logger.debug(f"[Gemini {model_name}] Using safety settings: { {k.name: v.name for k, v in safety_settings.items()} }")
875
+ except (NameError, AttributeError): # Catch if HarmCategory/Threshold are not loaded (SDK issue)
876
+ logger.error(f"[Gemini {model_name}] Failed to define safety settings. HarmCategory/HarmBlockThreshold missing or SDK not fully available?")
877
+ return None, f"Sorry, an internal error occurred configuring the AI service ({model_name}). Safety settings unavailable."
 
 
878
 
879
  try:
880
  logger.debug(f"[Gemini {model_name}] Initializing model {model_name}")
881
  model = genai.GenerativeModel(model_name)
882
  logger.info(f"[Gemini {model_name}] Sending request to Gemini ({model_name})...")
883
+ request_options = {"timeout": 120} # Keep timeout reasonable
884
  response = await model.generate_content_async(
885
+ full_prompt, generation_config=genai.types.GenerationConfig(), # Use default config
886
  safety_settings=safety_settings, request_options=request_options )
887
  logger.info(f"[Gemini {model_name}] Received response from Gemini.")
888
+
889
+ # Check for prompt blocking first
890
  if response.prompt_feedback and response.prompt_feedback.block_reason:
891
  block_reason_str = getattr(response.prompt_feedback.block_reason, 'name', str(response.prompt_feedback.block_reason))
892
  logger.warning(f"[Gemini {model_name}] Request blocked by API. Reason: {block_reason_str}");
893
  return None, f"Sorry, the AI model ({model_name}) blocked the request (Reason: {block_reason_str})."
894
+
895
  summary = None; finish_reason_str = 'UNKNOWN'
896
+ # Check candidates for content and safety blocking
897
  if response.candidates:
898
  candidate = response.candidates[0]
899
  finish_reason_name = getattr(candidate.finish_reason, 'name', None)
900
+ finish_reason_str = finish_reason_name or 'N/A' # Default if None
901
+
902
+ # Check if the *candidate* was blocked by safety
903
  if finish_reason_name == 'SAFETY':
904
  safety_ratings_str = ", ".join([f"{rating.category.name}: {rating.probability.name}" for rating in candidate.safety_ratings])
905
  logger.warning(f"[Gemini {model_name}] Candidate blocked due to SAFETY. Finish Reason: {finish_reason_str}. Ratings: [{safety_ratings_str}]")
906
+ # Provide a more specific error message about response blocking
907
+ return None, f"Sorry, the AI model ({model_name}) generated a response that was blocked due to safety filters ({finish_reason_str})."
908
+ elif finish_reason_name not in ['STOP', 'MAX_TOKENS', None]: # Log other unusual finish reasons
909
  logger.warning(f"[Gemini {model_name}] Candidate finished with non-standard reason: {finish_reason_str}")
910
+
911
+ # Extract content if available
912
  if candidate.content and candidate.content.parts:
913
  summary = "".join(part.text for part in candidate.content.parts if hasattr(part, 'text'))
914
+
915
+ # Fallback: try accessing response.text directly (might raise ValueError if blocked)
916
  if summary is None:
917
  try: summary = response.text
918
+ except ValueError as e: logger.warning(f"[Gemini {model_name}] Error accessing response.text (likely blocked/no content): {e}"); summary = None
919
+ except Exception as e: logger.warning(f"[Gemini {model_name}] Unexpected error accessing response.text: {e}"); summary = None
920
+
921
+ # Final check if we got a summary
922
  if summary:
923
  logger.info(f"[Gemini {model_name}] Success generating summary. Finish Reason: {finish_reason_str}. Output len: {len(summary)}");
924
  return summary.strip(), None
925
  else:
926
+ # If no summary after checking candidate and response.text, report failure
927
  logger.warning(f"[Gemini {model_name}] Gemini returned empty summary or content was blocked. Final Finish Reason: {finish_reason_str}");
928
  return None, f"Sorry, the AI model ({model_name}) did not provide a summary (Finish Reason: {finish_reason_str})."
929
+
930
  except AttributeError as ae:
931
+ # This can happen if the response structure changes or expected fields are missing
932
  logger.error(f"[Gemini {model_name}] AttributeError during Gemini response processing: {ae}. SDK might be incompatible or response structure unexpected.", exc_info=True);
933
+ return None, f"Sorry, there was an issue processing the response from the AI service ({model_name}). Attribute error."
934
+ except google.api_core.exceptions.InvalidArgument as iae:
935
+ # Catch specific InvalidArgument, often related to safety settings or model params
936
+ logger.error(f"[Gemini {model_name}] Invalid Argument error from Gemini API: {iae}", exc_info=False) # Don't need full stack trace often
937
+ error_detail = str(iae)
938
+ user_message = f"Sorry, the AI service ({model_name}) reported an invalid argument."
939
+ if "safety_settings" in error_detail: user_message += " (Safety Settings issue)."
940
+ elif "temperature" in error_detail: user_message += " (Generation Parameter issue)."
941
+ return None, user_message
942
+ except google.api_core.exceptions.GoogleAPIError as gae:
943
+ # Catch other potential Google API errors (like permission denied, quota exceeded, etc.)
944
+ logger.error(f"[Gemini {model_name}] Google API error during Gemini call: {gae}", exc_info=False)
945
+ status_code = getattr(gae, 'code', 'Unknown')
946
+ user_message = f"Sorry, the AI service ({model_name}) encountered an API error (Code: {status_code})."
947
+ if status_code == 429: user_message = f"Sorry, the AI model ({model_name}) is busy (Rate Limit/Quota Exceeded). Try again later."
948
+ elif status_code == 500: user_message = f"Sorry, the AI service ({model_name}) had an internal server error."
949
+ return None, user_message
950
  except Exception as e:
951
+ # General catch-all for unexpected issues
952
  logger.error(f"[Gemini {model_name}] Unexpected error during Gemini API call: {e}", exc_info=True);
 
 
 
 
953
  error_msg = f"Sorry, an unexpected error occurred while using the AI service ({model_name})."
954
  return None, error_msg
955
 
 
1016
 
1017
  async def generate_summary(text: str, summary_type: str) -> str:
1018
  """
1019
+ Generates summary using the specific model hierarchy (April 2025 - Updated):
1020
+ 1. Gemini 2.5 Flash Preview (NEW)
1021
+ 2. Gemini 2.5 Pro Exp
1022
+ 3. Gemini 2.0 Flash
1023
  4. OpenRouter (DeepSeek V3 Free)
1024
+ 5. Groq (Llama 4 Scout)
1025
  Returns the summary text or a comprehensive error message.
1026
  """
1027
+ global _gemini_api_enabled, _openrouter_fallback_enabled, _groq_enabled
1028
+ global GEMINI_FLASH_PREVIEW_MODEL, GEMINI_PRO_EXP_MODEL, GEMINI_FLASH_MODEL, OPENROUTER_DEEPSEEK_MODEL, GROQ_LLAMA4_MODEL
1029
 
1030
+ logger.info("[Summary Generation] Starting process with updated April 2025 model hierarchy.")
1031
  summary: Optional[str] = None
1032
+ # Use more descriptive keys matching the model variables
1033
  errors: Dict[str, Optional[str]] = {
1034
+ GEMINI_FLASH_PREVIEW_MODEL: None,
1035
+ GEMINI_PRO_EXP_MODEL: None,
1036
+ GEMINI_FLASH_MODEL: None,
1037
+ OPENROUTER_DEEPSEEK_MODEL: None,
1038
+ GROQ_LLAMA4_MODEL: None,
1039
  }
1040
 
1041
+ # --- Attempt 1: Gemini 2.5 Flash Preview (NEW) ---
1042
+ if _gemini_api_enabled:
1043
+ logger.info(f"[Summary Generation] Attempting 1: Gemini ({GEMINI_FLASH_PREVIEW_MODEL})")
1044
+ summary, errors[GEMINI_FLASH_PREVIEW_MODEL] = await _call_gemini(text, summary_type, GEMINI_FLASH_PREVIEW_MODEL)
1045
  if summary:
1046
+ logger.info(f"[Summary Generation] Success with Gemini ({GEMINI_FLASH_PREVIEW_MODEL}).")
1047
  return summary
1048
  else:
1049
+ logger.warning(f"[Summary Generation] Gemini Flash Preview failed. Error: {errors[GEMINI_FLASH_PREVIEW_MODEL]}. Proceeding to Gemini 2.5 Pro Exp.")
1050
  else:
1051
+ logger.warning("[Summary Generation] Gemini API is disabled or unavailable. Skipping all Gemini models.")
1052
+ errors[GEMINI_FLASH_PREVIEW_MODEL] = "Service disabled/unavailable."
1053
+ errors[GEMINI_PRO_EXP_MODEL] = "Service disabled/unavailable."
1054
+ errors[GEMINI_FLASH_MODEL] = "Service disabled/unavailable."
1055
 
1056
  # --- Attempt 2: Gemini 2.5 Pro Exp ---
1057
+ # Only attempt if API is enabled AND the previous step didn't mark it as disabled
1058
+ if _gemini_api_enabled and errors[GEMINI_PRO_EXP_MODEL] is None:
1059
  logger.info(f"[Summary Generation] Attempting 2: Gemini ({GEMINI_PRO_EXP_MODEL})")
1060
+ summary, errors[GEMINI_PRO_EXP_MODEL] = await _call_gemini(text, summary_type, GEMINI_PRO_EXP_MODEL)
1061
  if summary:
1062
  logger.info(f"[Summary Generation] Success with Gemini ({GEMINI_PRO_EXP_MODEL}).")
1063
  return summary
1064
  else:
1065
+ logger.warning(f"[Summary Generation] Gemini 2.5 Pro Exp failed. Error: {errors[GEMINI_PRO_EXP_MODEL]}. Proceeding to Gemini 2.0 Flash.")
1066
+ # No separate 'else' needed here, handled by the initial Gemini API check
 
 
 
1067
 
1068
  # --- Attempt 3: Gemini 2.0 Flash ---
1069
+ # Only attempt if API is enabled AND it wasn't marked as disabled earlier
1070
+ if _gemini_api_enabled and errors[GEMINI_FLASH_MODEL] is None:
1071
  logger.info(f"[Summary Generation] Attempting 3: Gemini ({GEMINI_FLASH_MODEL})")
1072
+ summary, errors[GEMINI_FLASH_MODEL] = await _call_gemini(text, summary_type, GEMINI_FLASH_MODEL)
1073
  if summary:
1074
  logger.info(f"[Summary Generation] Success with Gemini ({GEMINI_FLASH_MODEL}).")
1075
  return summary
1076
  else:
1077
+ logger.warning(f"[Summary Generation] Gemini 2.0 Flash failed. Error: {errors[GEMINI_FLASH_MODEL]}. Proceeding to OpenRouter DeepSeek V3.")
1078
+ # No separate 'else' needed here
 
 
1079
 
1080
+ # --- Attempt 4: OpenRouter (DeepSeek V3 Free) ---
1081
  if _openrouter_fallback_enabled:
1082
  logger.info(f"[Summary Generation] Attempting 4: OpenRouter ({OPENROUTER_DEEPSEEK_MODEL})")
1083
+ summary, errors[OPENROUTER_DEEPSEEK_MODEL] = await _call_openrouter(text, summary_type)
1084
  if summary:
1085
  logger.info(f"[Summary Generation] Success with OpenRouter ({OPENROUTER_DEEPSEEK_MODEL}).")
1086
  return summary
1087
  else:
1088
+ logger.warning(f"[Summary Generation] OpenRouter DeepSeek V3 failed. Error: {errors[OPENROUTER_DEEPSEEK_MODEL]}. Proceeding to Groq Llama 4 Scout.")
1089
+ else:
1090
+ logger.warning("[Summary Generation] OpenRouter fallback (DeepSeek V3) is disabled or unavailable. Skipping.")
1091
+ errors[OPENROUTER_DEEPSEEK_MODEL] = "Service disabled/unavailable."
1092
+
1093
+ # --- Attempt 5: Groq (Llama 4 Scout - Final Fallback) ---
1094
+ if _groq_enabled:
1095
+ logger.info(f"[Summary Generation] Attempting 5: Groq ({GROQ_LLAMA4_MODEL})")
1096
+ summary, errors[GROQ_LLAMA4_MODEL] = await _call_groq(text, summary_type)
1097
+ if summary:
1098
+ logger.info(f"[Summary Generation] Success with Groq ({GROQ_LLAMA4_MODEL}).")
1099
+ return summary
1100
+ else:
1101
+ logger.error(f"[Summary Generation] Groq Llama 4 Scout (Final Fallback) also failed. Error: {errors[GROQ_LLAMA4_MODEL]}")
1102
  else:
1103
+ logger.error("[Summary Generation] Groq (Llama 4 Scout) is disabled or unavailable. Cannot proceed.")
1104
+ errors[GROQ_LLAMA4_MODEL] = "Service disabled/unavailable."
1105
 
1106
  # --- All Attempts Failed ---
1107
  logger.error("[Summary Generation] All summarization models failed.")
1108
+ # Use the model names as keys for clearer error reporting
1109
  error_details = "\n".join([f"- {model}: {err}" for model, err in errors.items() if err])
1110
  return f"Sorry, I couldn't generate a summary after trying all available AI models.\nDetails:\n{error_details}"
1111
 
 
1267
  user = update.effective_user
1268
  if not user or not update.message: return
1269
  logger.info(f"User {user.id} ({user.username or 'no_username'}) used /help.")
1270
+ # Updated help text reflecting the new model order
1271
  help_text = ( "🔍 **How to use this bot:**\n\n"
1272
  "1. Send me any YouTube video link or website URL.\n"
1273
  "2. I'll ask how you want it summarised (paragraph or points).\n"
1274
  "3. Click the button for your choice.\n"
1275
  "4. Wait while I fetch the content and generate the summary!\n\n"
1276
+ "⚙️ I try multiple methods to get content, especially for tricky websites or YouTube videos without standard transcripts. I then use a sequence of AI models (Gemini 2.5 Flash Preview, Gemini 2.5 Pro, Gemini 2.0 Flash, DeepSeek V3, Llama 4 Scout) to summarise.\n\n" # Updated model list here
1277
  "**Commands:**\n"
1278
  "`/start` - Display the welcome message\n"
1279
  "`/help` - Show this help message" )
 
1437
 
1438
  async def health_check(request: Request) -> PlainTextResponse:
1439
  """Simple health check endpoint."""
1440
+ # Include the new model variable
1441
+ global GEMINI_FLASH_PREVIEW_MODEL, GROQ_LLAMA4_MODEL, GEMINI_PRO_EXP_MODEL, GEMINI_FLASH_MODEL, OPENROUTER_DEEPSEEK_MODEL
1442
+ global APIFY_ACTOR_ID, APIFY_STRUCTURED_YT_ACTOR_ID
1443
  global _groq_enabled, _gemini_api_enabled, _openrouter_fallback_enabled
1444
  global _apify_token_exists, _urltotext_key_exists, _rapidapi_key_exists, SUPADATA_API_KEY
1445
 
 
1446
  bot_status = "Not Initialized"; bot_username = "N/A"
1447
  if ptb_app:
1448
  try:
1449
+ # Check if bot is running and get info
1450
  if ptb_app.running:
1451
+ # Wrap get_me in a try-except in case the bot disconnects during check
1452
+ try:
1453
+ bot_info = await ptb_app.bot.get_me(); bot_username = f"@{bot_info.username}" if bot_info and bot_info.username else "Info Fetch Error"; bot_status = "Running"
1454
+ except (TimedOut, NetworkError, TelegramError) as bot_err:
1455
+ logger.warning(f"Health check: Error getting bot info while running: {bot_err}")
1456
+ bot_status = "Running (Info Error)"; bot_username = "Fetch Error"
1457
  else:
1458
  bot_status = "Initialized but Not Running"
1459
+ # Try getting info even if not 'running' (might be initializing/shutting down)
1460
  if ptb_app.bot:
1461
  try: bot_info = await ptb_app.bot.get_me(); bot_username = f"@{bot_info.username}" if bot_info and bot_info.username else "Info Fetch Error"
1462
+ except (TimedOut, NetworkError, TelegramError) as bot_err:
1463
+ logger.warning(f"Health check: Error getting bot info while not running: {bot_err}")
1464
+ bot_username = "Info Fetch Error (Not Running)"
1465
+ except Exception as e: # Catch broader exceptions during status check
1466
+ bot_status = f"Error checking status: {type(e).__name__}"; logger.warning(f"Health check: General error getting bot status: {e}")
1467
+ bot_username = "Error"
1468
  else: bot_status = "Not Initialized"; bot_username = "N/A"
1469
 
1470
 
1471
+ # <<< Update response string with the new model order >>>
1472
  return PlainTextResponse(
1473
  f"TG Bot Summariser - Status: {bot_status} ({bot_username})\n"
1474
  f"---\n"
1475
+ f"Summarizer Priority (April 2025 - Updated):\n"
1476
+ f"1. Gemini API: {GEMINI_FLASH_PREVIEW_MODEL if _gemini_api_enabled else 'DISABLED'} (NEW)\n"
1477
  f"2. Gemini API: {GEMINI_PRO_EXP_MODEL if _gemini_api_enabled else 'DISABLED'}\n"
1478
  f"3. Gemini API: {GEMINI_FLASH_MODEL if _gemini_api_enabled else 'DISABLED'}\n"
1479
  f"4. OpenRouter API: {OPENROUTER_DEEPSEEK_MODEL if _openrouter_fallback_enabled else 'DISABLED'}\n"
1480
+ f"5. Groq API: {GROQ_LLAMA4_MODEL if _groq_enabled else 'DISABLED'} (Last Fallback)\n"
1481
  f"---\n"
1482
  f"Content Fetching Status:\n"
1483
+ # --- YT Fallback List (Order already correct) ---
1484
  f"YT Primary (Lib): Enabled\n"
1485
  f"YT Fallback 1 (Apify Structured): {APIFY_STRUCTURED_YT_ACTOR_ID if _apify_token_exists else 'DISABLED'}\n"
1486
  f"YT Fallback 2 (Supadata): {'Enabled' if SUPADATA_API_KEY else 'Disabled'}\n"
1487
  f"YT Fallback 3 (Apify Default): {APIFY_ACTOR_ID if _apify_token_exists else 'DISABLED'}\n"
1488
+ # --- Web Scrape Fallback List (Order already correct) ---
1489
  f"Web Scrape 1 (Direct+BS4): Enabled\n"
1490
  f"Web Scrape 2 (urltotext): {'Enabled' if _urltotext_key_exists else 'Disabled'}\n"
1491
  f"Web Scrape 3/4 (RapidAPI): {'Enabled' if _rapidapi_key_exists else 'Disabled'}\n"
1492
+ f"Web Scrape 5/6 (Apify Actors): {'Enabled' if _apify_token_exists else 'Disabled'}"
1493
  )
1494
 
1495
  async def telegram_webhook(request: Request) -> Response: