atz21 commited on
Commit
306e0ab
Β·
verified Β·
1 Parent(s): 49a5da9

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +106 -28
app.py CHANGED
@@ -581,17 +581,27 @@ def merge_pdfs(paths, output_path):
581
  writer.write(f)
582
  return output_path
583
 
584
- def gemini_generate_content(prompt_text, file_upload_obj=None, image_obj=None, model_name="gemini-2.5-pro", fallback_model="gemini-2.5-flash"):
585
  """
586
  Send prompt_text and optionally an uploaded file (or an image object/list) to the model using NEW SDK.
587
  Automatically rotates through available API keys on RESOURCE_EXHAUSTED errors.
588
- Note: Files uploaded with one API key cannot be accessed by another API key.
 
 
 
 
 
 
 
 
 
589
  Returns textual response and prints progress.
590
  """
591
  contents = [prompt_text]
 
592
 
593
- if file_upload_obj:
594
- contents.append(file_upload_obj)
595
 
596
  if image_obj:
597
  if isinstance(image_obj, list):
@@ -618,6 +628,25 @@ def gemini_generate_content(prompt_text, file_upload_obj=None, image_obj=None, m
618
  current_client = client_manager.get_current_client()
619
  current_key_num = client_manager.current_key_index + 1
620
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
621
  # Try primary model first
622
  try:
623
  print(f"πŸ”‘ Using API key #{current_key_num} with model {model_name}")
@@ -661,17 +690,33 @@ def gemini_generate_content(prompt_text, file_upload_obj=None, image_obj=None, m
661
 
662
  # Now try next API key if available
663
  if attempt < max_attempts - 1:
664
- # Check if we have file uploads - they won't work with different keys
665
- if file_upload_obj:
666
- print("⚠️ WARNING: File uploads cannot be shared across API keys!")
667
- print(" You need to re-upload files with each new API key.")
668
- print(" Stopping rotation to avoid PERMISSION_DENIED errors.")
669
- raise Exception(f"All models exhausted for API key #{current_key_num}. Cannot rotate keys with file uploads.")
670
-
671
- client_manager.rotate_to_next_key()
672
- attempt += 1
673
- print(f"πŸ”„ Trying next API key (attempt {attempt + 1}/{max_attempts})...")
674
- continue
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
675
  else:
676
  raise Exception(f"All {max_attempts} API key(s) exhausted with both models.")
677
  else:
@@ -681,7 +726,24 @@ def gemini_generate_content(prompt_text, file_upload_obj=None, image_obj=None, m
681
  elif "403" in error_str or "PERMISSION_DENIED" in error_str:
682
  # This happens when trying to access a file uploaded with a different API key
683
  print(f"⚠️ Permission denied - likely due to file uploaded with different API key")
684
- raise Exception(f"File access denied. Files uploaded with one API key cannot be accessed by another. Error: {e}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
685
 
686
  else:
687
  # Other error - try fallback model with same key
@@ -698,23 +760,39 @@ def gemini_generate_content(prompt_text, file_upload_obj=None, image_obj=None, m
698
  except Exception as e2:
699
  print(f"❌ Fallback also failed: {e2}")
700
 
701
- # If we have more keys and no file uploads, try them
702
- if attempt < max_attempts - 1 and not file_upload_obj:
703
- client_manager.rotate_to_next_key()
704
- attempt += 1
705
- print(f"πŸ”„ Trying next API key (attempt {attempt + 1}/{max_attempts})...")
706
- continue
707
- else:
708
- if file_upload_obj:
709
- raise Exception(f"All models failed. Cannot rotate keys with file uploads. Last error: {e2}")
 
 
 
 
 
 
 
 
 
 
710
  else:
711
- raise Exception(f"All attempts failed. Last error: {e2}")
 
 
 
 
 
712
 
713
  # If we exhausted all attempts
714
  raise Exception(f"❌ All {max_attempts} API key(s) exhausted. Please check your quota or try again later.")
715
 
716
 
717
 
 
718
  # ---------------- PARSERS ----------------
719
  def extract_question_ids_from_qpms(text: str):
720
  """Extract question IDs from QP+MS transcript."""
@@ -1270,7 +1348,7 @@ def align_and_grade_pipeline(qp_path, ms_path, ans_path, subject="Maths", imprin
1270
 
1271
  print("1.i) Transcribing QP+MS (questions first, then full markscheme, with graph detection)...")
1272
  qpms_prompt = QP_MS_TRANSCRIPTION_PROMPT["content"] + "\nAt the end, also list all questions in the markscheme where a graph is expected, in the format:\nGraph expected in:\n- Question <number> β†’ Page <number>\n(One per line, after ==== MARKSCHEME END ====)"
1273
- qpms_text = gemini_generate_content(qpms_prompt, file_upload_obj=merged_uploaded, model_name="gemini-2.5-flash", fallback_model="gemini-2.5-flash-preview-09-2025")
1274
  print("πŸ“„ QP+MS transcription received. Saving debug file: debug_qpms_transcript.txt")
1275
  with open("debug_qpms_transcript.txt", "w", encoding="utf-8") as f:
1276
  f.write(qpms_text)
@@ -1288,7 +1366,7 @@ def align_and_grade_pipeline(qp_path, ms_path, ans_path, subject="Maths", imprin
1288
 
1289
  print("1.ii) Building AS transcription prompt with expected question IDs and graph detection, sending to Gemini...")
1290
  as_prompt = build_as_cot_prompt_with_expected_ids(extracted_ids, qpms_text) + "\nAt the end, also list all answers where a graph is found, in the format:\nGraph found in:\n- Answer <number> β†’ Page <number>\n(One per line, after all answers)"
1291
- as_text = gemini_generate_content(as_prompt, file_upload_obj=ans_uploaded, model_name="gemini-2.5-flash", fallback_model="gemini-2.5-flash-preview-09-2025")
1292
  print("πŸ“ AS transcription received. Saving debug file: debug_as_transcript.txt")
1293
  with open("debug_as_transcript.txt", "w", encoding="utf-8") as f:
1294
  f.write(as_text)
 
581
  writer.write(f)
582
  return output_path
583
 
584
+ def gemini_generate_content(prompt_text, file_upload_obj=None, image_obj=None, model_name="gemini-2.5-pro", fallback_model="gemini-2.5-flash", file_path=None):
585
  """
586
  Send prompt_text and optionally an uploaded file (or an image object/list) to the model using NEW SDK.
587
  Automatically rotates through available API keys on RESOURCE_EXHAUSTED errors.
588
+ When rotating keys with file uploads, re-uploads the file with the new API key.
589
+
590
+ Args:
591
+ prompt_text: The prompt to send
592
+ file_upload_obj: Previously uploaded file object (optional)
593
+ image_obj: Image or list of images (optional)
594
+ model_name: Primary model to use
595
+ fallback_model: Fallback model if primary fails
596
+ file_path: Local file path (needed for re-upload when rotating keys)
597
+
598
  Returns textual response and prints progress.
599
  """
600
  contents = [prompt_text]
601
+ current_file_obj = file_upload_obj
602
 
603
+ if current_file_obj:
604
+ contents.append(current_file_obj)
605
 
606
  if image_obj:
607
  if isinstance(image_obj, list):
 
628
  current_client = client_manager.get_current_client()
629
  current_key_num = client_manager.current_key_index + 1
630
 
631
+ # Update contents with current file object
632
+ contents = [prompt_text]
633
+ if current_file_obj:
634
+ contents.append(current_file_obj)
635
+ if image_obj:
636
+ if isinstance(image_obj, list):
637
+ for img_path in image_obj:
638
+ if isinstance(img_path, str):
639
+ pil_img = Image.open(img_path)
640
+ contents.append(pil_img)
641
+ else:
642
+ contents.append(img_path)
643
+ else:
644
+ if isinstance(image_obj, str):
645
+ pil_img = Image.open(image_obj)
646
+ contents.append(pil_img)
647
+ else:
648
+ contents.append(image_obj)
649
+
650
  # Try primary model first
651
  try:
652
  print(f"πŸ”‘ Using API key #{current_key_num} with model {model_name}")
 
690
 
691
  # Now try next API key if available
692
  if attempt < max_attempts - 1:
693
+ # Check if we have file uploads and can re-upload
694
+ if file_upload_obj and file_path:
695
+ print(f"πŸ”„ Rotating to next API key and re-uploading file...")
696
+ client_manager.rotate_to_next_key()
697
+
698
+ # Re-upload file with new API key
699
+ try:
700
+ print(f"πŸ“€ Re-uploading file with API key #{client_manager.current_key_index + 1}...")
701
+ current_file_obj = upload_to_gemini(file_path)
702
+ print(f"βœ… File re-uploaded successfully")
703
+ except Exception as upload_error:
704
+ print(f"❌ Failed to re-upload file: {upload_error}")
705
+ raise Exception(f"Failed to re-upload file with new API key: {upload_error}")
706
+
707
+ attempt += 1
708
+ print(f"πŸ”„ Retrying with next API key (attempt {attempt + 1}/{max_attempts})...")
709
+ continue
710
+ elif file_upload_obj and not file_path:
711
+ print("⚠️ WARNING: Cannot rotate API keys - file_path not provided for re-upload!")
712
+ print(" To enable API key rotation with file uploads, pass file_path parameter.")
713
+ raise Exception(f"All models exhausted for API key #{current_key_num}. Cannot rotate without file_path.")
714
+ else:
715
+ # No file uploads, safe to rotate
716
+ client_manager.rotate_to_next_key()
717
+ attempt += 1
718
+ print(f"πŸ”„ Trying next API key (attempt {attempt + 1}/{max_attempts})...")
719
+ continue
720
  else:
721
  raise Exception(f"All {max_attempts} API key(s) exhausted with both models.")
722
  else:
 
726
  elif "403" in error_str or "PERMISSION_DENIED" in error_str:
727
  # This happens when trying to access a file uploaded with a different API key
728
  print(f"⚠️ Permission denied - likely due to file uploaded with different API key")
729
+
730
+ # Try to re-upload if we have the file path
731
+ if file_path and attempt < max_attempts - 1:
732
+ print(f"πŸ”„ Attempting to re-upload file with next API key...")
733
+ client_manager.rotate_to_next_key()
734
+
735
+ try:
736
+ print(f"πŸ“€ Re-uploading file with API key #{client_manager.current_key_index + 1}...")
737
+ current_file_obj = upload_to_gemini(file_path)
738
+ print(f"βœ… File re-uploaded successfully")
739
+ attempt += 1
740
+ print(f"πŸ”„ Retrying with next API key (attempt {attempt + 1}/{max_attempts})...")
741
+ continue
742
+ except Exception as upload_error:
743
+ print(f"❌ Failed to re-upload file: {upload_error}")
744
+ raise Exception(f"Failed to re-upload file with new API key: {upload_error}")
745
+ else:
746
+ raise Exception(f"File access denied. Cannot re-upload without file_path. Error: {e}")
747
 
748
  else:
749
  # Other error - try fallback model with same key
 
760
  except Exception as e2:
761
  print(f"❌ Fallback also failed: {e2}")
762
 
763
+ # If we have more keys, try them
764
+ if attempt < max_attempts - 1:
765
+ if file_upload_obj and file_path:
766
+ print(f"πŸ”„ Rotating to next API key and re-uploading file...")
767
+ client_manager.rotate_to_next_key()
768
+
769
+ try:
770
+ print(f"πŸ“€ Re-uploading file with API key #{client_manager.current_key_index + 1}...")
771
+ current_file_obj = upload_to_gemini(file_path)
772
+ print(f"βœ… File re-uploaded successfully")
773
+ except Exception as upload_error:
774
+ print(f"❌ Failed to re-upload file: {upload_error}")
775
+ raise Exception(f"Failed to re-upload file with new API key: {upload_error}")
776
+
777
+ attempt += 1
778
+ print(f"πŸ”„ Retrying with next API key (attempt {attempt + 1}/{max_attempts})...")
779
+ continue
780
+ elif file_upload_obj and not file_path:
781
+ raise Exception(f"All models failed. Cannot rotate keys without file_path. Last error: {e2}")
782
  else:
783
+ client_manager.rotate_to_next_key()
784
+ attempt += 1
785
+ print(f"πŸ”„ Trying next API key (attempt {attempt + 1}/{max_attempts})...")
786
+ continue
787
+ else:
788
+ raise Exception(f"All attempts failed. Last error: {e2}")
789
 
790
  # If we exhausted all attempts
791
  raise Exception(f"❌ All {max_attempts} API key(s) exhausted. Please check your quota or try again later.")
792
 
793
 
794
 
795
+
796
  # ---------------- PARSERS ----------------
797
  def extract_question_ids_from_qpms(text: str):
798
  """Extract question IDs from QP+MS transcript."""
 
1348
 
1349
  print("1.i) Transcribing QP+MS (questions first, then full markscheme, with graph detection)...")
1350
  qpms_prompt = QP_MS_TRANSCRIPTION_PROMPT["content"] + "\nAt the end, also list all questions in the markscheme where a graph is expected, in the format:\nGraph expected in:\n- Question <number> β†’ Page <number>\n(One per line, after ==== MARKSCHEME END ====)"
1351
+ qpms_text = gemini_generate_content(qpms_prompt, file_upload_obj=merged_uploaded, model_name="gemini-2.5-flash", fallback_model="gemini-2.5-flash-preview-09-2025", file_path=merged_qpms_path)
1352
  print("πŸ“„ QP+MS transcription received. Saving debug file: debug_qpms_transcript.txt")
1353
  with open("debug_qpms_transcript.txt", "w", encoding="utf-8") as f:
1354
  f.write(qpms_text)
 
1366
 
1367
  print("1.ii) Building AS transcription prompt with expected question IDs and graph detection, sending to Gemini...")
1368
  as_prompt = build_as_cot_prompt_with_expected_ids(extracted_ids, qpms_text) + "\nAt the end, also list all answers where a graph is found, in the format:\nGraph found in:\n- Answer <number> β†’ Page <number>\n(One per line, after all answers)"
1369
+ as_text = gemini_generate_content(as_prompt, file_upload_obj=ans_uploaded, model_name="gemini-2.5-flash", fallback_model="gemini-2.5-flash-preview-09-2025", file_path=ans_path)
1370
  print("πŸ“ AS transcription received. Saving debug file: debug_as_transcript.txt")
1371
  with open("debug_as_transcript.txt", "w", encoding="utf-8") as f:
1372
  f.write(as_text)