atz21 commited on
Commit
419e7c7
Β·
verified Β·
1 Parent(s): 2b866da

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +88 -67
app.py CHANGED
@@ -1,22 +1,26 @@
 
 
 
 
 
1
  import os
2
  import re
3
  import json
4
  import subprocess
5
- import tempfile
6
  import time
7
  import img2pdf
8
  import gradio as gr
9
- import google.generativeai as genai
10
  from markdown_pdf import MarkdownPdf, Section
11
  from pdf2image import convert_from_path
12
  from PIL import Image, ImageDraw, ImageFont
13
  import cv2
14
  import numpy as np
15
- from concurrent.futures import ThreadPoolExecutor, as_completed
16
  from PyPDF2 import PdfReader, PdfWriter
17
 
18
  # ---------------- CONFIG ----------------
19
- genai.configure(api_key=os.getenv("GEMINI_API_KEY"))
 
20
  GRID_ROWS, GRID_COLS = 20, 14
21
 
22
  # ---------------- PROMPTS ----------------
@@ -144,37 +148,24 @@ def compress_pdf(input_path, output_path=None, max_size=20*1024*1024):
144
  print("❌ Compression error:", e)
145
  return input_path
146
 
147
- def create_model():
148
- """
149
- Create the Gemini model and print which model is selected.
150
- """
151
- try:
152
- print("⚑ Attempting to use gemini-2.0-flash-exp model")
153
- model = genai.GenerativeModel("gemini-2.0-flash-exp", generation_config={"temperature": 0})
154
- print("βœ… Selected model: gemini-2.0-flash-exp")
155
- return model
156
- except Exception as e:
157
- print("⚠️ Could not use gemini-2.0-flash-exp:", e)
158
- try:
159
- print("⚑ Falling back to gemini-1.5-flash model")
160
- model = genai.GenerativeModel("gemini-1.5-flash", generation_config={"temperature": 0})
161
- print("βœ… Selected model: gemini-1.5-flash")
162
- return model
163
- except Exception as e:
164
- print("❌ Failed to create any Gemini model:", e)
165
- raise
166
-
167
  def upload_to_gemini(path, display_name=None):
168
  """
169
- Upload a file to Gemini using the standard File API (no RAG).
170
  """
171
  print(f"πŸ“€ Uploading {path} to Gemini...")
172
  try:
173
- uploaded_file = genai.upload_file(
174
- path=path,
175
- display_name=display_name or os.path.basename(path)
176
- )
177
- print(f"βœ… Uploaded: {uploaded_file.display_name} (URI: {uploaded_file.uri})")
 
 
 
 
 
 
 
178
  return uploaded_file
179
  except Exception as e:
180
  print(f"❌ Upload failed for {path}: {e}")
@@ -190,37 +181,56 @@ def merge_pdfs(paths, output_path):
190
  writer.write(f)
191
  return output_path
192
 
193
- def gemini_generate_content(model, prompt_text, file_upload_obj=None, image_obj=None):
194
  """
195
- Send prompt_text and optionally an uploaded file (or an image object/list) to the model.
196
  Returns textual response and prints progress.
197
  """
198
- inputs = [prompt_text]
 
199
  if file_upload_obj:
200
- inputs.append(file_upload_obj)
 
201
  if image_obj:
202
  if isinstance(image_obj, list):
203
  for img_path in image_obj:
204
  if isinstance(img_path, str):
205
  pil_img = Image.open(img_path)
206
- inputs.append(pil_img)
207
  else:
208
- inputs.append(img_path)
209
  else:
210
  if isinstance(image_obj, str):
211
  pil_img = Image.open(image_obj)
212
- inputs.append(pil_img)
213
  else:
214
- inputs.append(image_obj)
 
215
  print("πŸ“‘ Sending request to Gemini (prompt length:", len(prompt_text), "chars )")
216
- response = model.generate_content(inputs)
217
- raw_text = getattr(response, "text", None)
218
- if not raw_text and getattr(response, "candidates", None):
219
- raw_text = response.candidates[0].content.parts[0].text
220
- if raw_text is None:
221
- raw_text = str(response)
222
- print("πŸ“₯ Received response (chars):", len(raw_text))
223
- return raw_text
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
224
 
225
  # ---------------- PARSERS ----------------
226
  def extract_question_ids_from_qpms(text: str):
@@ -336,7 +346,7 @@ def extract_marks_from_grading(grading_text):
336
  return grading_json
337
 
338
  # ---------------- MAPPING/IMPRINT HELPERS ----------------
339
- def ask_gemini_for_mapping_batch(model, image_paths, grading_json, expected_ids=None, rows=GRID_ROWS, cols=GRID_COLS):
340
  """
341
  Send multiple page images together to Gemini for batch mapping processing.
342
  """
@@ -363,13 +373,22 @@ Grading JSON:
363
  images = [Image.open(p) for p in image_paths]
364
 
365
  print(f"πŸ“‘ Sending batch mapping request for {len(image_paths)} pages to Gemini...")
366
- response = model.generate_content([prompt, *images])
367
 
368
- raw_text = getattr(response, "text", None)
369
- if not raw_text and getattr(response, "candidates", None):
370
- raw_text = response.candidates[0].content.parts[0].text
371
- if not raw_text:
372
- raw_text = str(response)
 
 
 
 
 
 
 
 
 
 
373
 
374
  print("πŸ“₯ Batch mapping response (chars):", len(raw_text))
375
  print("πŸ”Ž Gemini raw batch output:")
@@ -388,7 +407,7 @@ Grading JSON:
388
  print(f"❌ Failed to parse Gemini JSON mapping: {e}")
389
  return []
390
 
391
- def imprint_marks_using_mapping(pdf_path, grading_json, output_pdf, model, expected_ids=None, rows=GRID_ROWS, cols=GRID_COLS):
392
  """
393
  Convert PDF to images, create grid-numbered images for batch sending to Gemini,
394
  then annotate and produce imprinted PDF.
@@ -432,7 +451,7 @@ def imprint_marks_using_mapping(pdf_path, grading_json, output_pdf, model, expec
432
 
433
  for start in range(0, len(temp_grid_images), batch_size):
434
  batch_paths = temp_grid_images[start:start+batch_size]
435
- batch_mapping = ask_gemini_for_mapping_batch(model, batch_paths, grading_json, expected_ids, rows, cols)
436
  all_mappings.extend(batch_mapping)
437
  print(f"βœ… Processed batch {start//batch_size + 1}: pages {start+1}-{start+len(batch_paths)}")
438
 
@@ -504,7 +523,7 @@ def extract_pdf_pages_as_images(pdf_path, page_numbers, prefix):
504
  # ---------------- PIPELINE ----------------
505
  def align_and_grade_pipeline(qp_path, ms_path, ans_path, imprint=False):
506
  """
507
- Final pipeline with graph-aware grading logic.
508
  """
509
  try:
510
  print("πŸ” Starting pipeline...")
@@ -517,15 +536,13 @@ def align_and_grade_pipeline(qp_path, ms_path, ans_path, imprint=False):
517
  print("πŸ“Ž Merged QP + MS ->", merged_qpms_path)
518
 
519
  print("πŸ”Ό Uploading files to Gemini...")
520
- merged_uploaded = upload_to_gemini(merged_qpms_path, "QP+MS (merged)")
521
- ans_uploaded = upload_to_gemini(ans_path, "Answer Sheet")
522
  print("βœ… Upload complete.")
523
 
524
- model = create_model()
525
-
526
  print("1.i) Transcribing QP+MS (questions first, then full markscheme, with graph detection)...")
527
  qpms_prompt = PROMPTS["QP_MS_TRANSCRIPTION"]["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 ====)"
528
- qpms_text = gemini_generate_content(model, qpms_prompt, file_upload_obj=merged_uploaded)
529
  print("πŸ“„ QP+MS transcription received. Saving debug file: debug_qpms_transcript.txt")
530
  with open("debug_qpms_transcript.txt", "w", encoding="utf-8") as f:
531
  f.write(qpms_text)
@@ -543,7 +560,7 @@ def align_and_grade_pipeline(qp_path, ms_path, ans_path, imprint=False):
543
 
544
  print("1.ii) Building AS transcription prompt with expected question IDs and graph detection, sending to Gemini...")
545
  as_prompt = build_as_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)"
546
- as_text = gemini_generate_content(model, as_prompt, file_upload_obj=ans_uploaded)
547
  print("πŸ“ AS transcription received. Saving debug file: debug_as_transcript.txt")
548
  with open("debug_as_transcript.txt", "w", encoding="utf-8") as f:
549
  f.write(as_text)
@@ -569,7 +586,7 @@ def align_and_grade_pipeline(qp_path, ms_path, ans_path, imprint=False):
569
  grading_input += graph_note
570
  grading_prompt_system = PROMPTS["GRADING_PROMPT"]["content"]
571
  grading_images = ms_graph_images + as_graph_images
572
- grading_text = gemini_generate_content(model, grading_prompt_system + "\n\nPlease grade the following transcripts:\n" + grading_input, image_obj=grading_images if grading_images else None)
573
  print("🧾 Grading output received. Saving debug file: debug_grading.md")
574
  with open("debug_grading.md", "w", encoding="utf-8") as f:
575
  f.write(grading_text)
@@ -585,9 +602,9 @@ def align_and_grade_pipeline(qp_path, ms_path, ans_path, imprint=False):
585
 
586
  imprinted_pdf_path = None
587
  if imprint:
588
- print("✍ Imprint option enabled. Starting imprinting process (parallel mapping requests)...")
589
  imprinted_pdf_path = f"{base_name}_imprinted.pdf"
590
- imprinted_pdf_path = imprint_marks_using_mapping(ans_path, grading_json, imprinted_pdf_path, model, extracted_ids)
591
  print("βœ… Imprinting finished. Imprinted PDF at:", imprinted_pdf_path)
592
 
593
  print("🏁 Pipeline finished successfully.")
@@ -600,8 +617,9 @@ def align_and_grade_pipeline(qp_path, ms_path, ans_path, imprint=False):
600
  return f"❌ Error: {e}", None, None, None, None
601
 
602
  # ---------------- GRADIO UI ----------------
603
- with gr.Blocks(title="AI Grading (Final Flow)") as demo:
604
- gr.Markdown("## πŸ“˜ AI Grading β€” Final Flow")
 
605
 
606
  with gr.Row():
607
  qp_file = gr.File(label="πŸ“„ Upload Question Paper (PDF)")
@@ -620,6 +638,9 @@ with gr.Blocks(title="AI Grading (Final Flow)") as demo:
620
  imprint_pdf_file = gr.File(label="πŸ“₯ Download Imprinted PDF (Optional)")
621
 
622
  def run_pipeline(qp_file_obj, ms_file_obj, ans_file_obj, imprint_flag):
 
 
 
623
  qp_path = qp_file_obj.name
624
  ms_path = ms_file_obj.name
625
  ans_path = ans_file_obj.name
 
1
+ Based on this new information, the issue is even clearer! The `google-generativeai` library has breaking changes that route requests to Vertex AI incorrectly. The solution is to migrate to the new `google-genai` SDK.
2
+
3
+ Here's your code completely rewritten using the **new official `google-genai` SDK**:
4
+
5
+ ```python
6
  import os
7
  import re
8
  import json
9
  import subprocess
 
10
  import time
11
  import img2pdf
12
  import gradio as gr
13
+ from google import genai # NEW SDK
14
  from markdown_pdf import MarkdownPdf, Section
15
  from pdf2image import convert_from_path
16
  from PIL import Image, ImageDraw, ImageFont
17
  import cv2
18
  import numpy as np
 
19
  from PyPDF2 import PdfReader, PdfWriter
20
 
21
  # ---------------- CONFIG ----------------
22
+ # Create client with new SDK
23
+ client = genai.Client(api_key=os.getenv("GEMINI_API_KEY"))
24
  GRID_ROWS, GRID_COLS = 20, 14
25
 
26
  # ---------------- PROMPTS ----------------
 
148
  print("❌ Compression error:", e)
149
  return input_path
150
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
151
  def upload_to_gemini(path, display_name=None):
152
  """
153
+ Upload a file to Gemini using the NEW google-genai SDK.
154
  """
155
  print(f"πŸ“€ Uploading {path} to Gemini...")
156
  try:
157
+ uploaded_file = client.files.upload(file=path)
158
+
159
+ # Wait for processing to complete
160
+ print(f"⏳ Waiting for file processing: {uploaded_file.name}")
161
+ while uploaded_file.state.name == "PROCESSING":
162
+ time.sleep(2)
163
+ uploaded_file = client.files.get(name=uploaded_file.name)
164
+
165
+ if uploaded_file.state.name == "FAILED":
166
+ raise Exception(f"File processing failed: {uploaded_file.name}")
167
+
168
+ print(f"βœ… Uploaded and processed: {uploaded_file.name}")
169
  return uploaded_file
170
  except Exception as e:
171
  print(f"❌ Upload failed for {path}: {e}")
 
181
  writer.write(f)
182
  return output_path
183
 
184
+ def gemini_generate_content(prompt_text, file_upload_obj=None, image_obj=None, model_name="gemini-2.0-flash-exp"):
185
  """
186
+ Send prompt_text and optionally an uploaded file (or an image object/list) to the model using NEW SDK.
187
  Returns textual response and prints progress.
188
  """
189
+ contents = [prompt_text]
190
+
191
  if file_upload_obj:
192
+ contents.append(file_upload_obj)
193
+
194
  if image_obj:
195
  if isinstance(image_obj, list):
196
  for img_path in image_obj:
197
  if isinstance(img_path, str):
198
  pil_img = Image.open(img_path)
199
+ contents.append(pil_img)
200
  else:
201
+ contents.append(img_path)
202
  else:
203
  if isinstance(image_obj, str):
204
  pil_img = Image.open(image_obj)
205
+ contents.append(pil_img)
206
  else:
207
+ contents.append(image_obj)
208
+
209
  print("πŸ“‘ Sending request to Gemini (prompt length:", len(prompt_text), "chars )")
210
+
211
+ try:
212
+ response = client.models.generate_content(
213
+ model=model_name,
214
+ contents=contents
215
+ )
216
+ raw_text = response.text
217
+ print("πŸ“₯ Received response (chars):", len(raw_text))
218
+ return raw_text
219
+ except Exception as e:
220
+ print(f"❌ Generation failed: {e}")
221
+ # Try fallback model
222
+ print("⚑ Trying fallback model: gemini-1.5-flash")
223
+ try:
224
+ response = client.models.generate_content(
225
+ model="gemini-1.5-flash",
226
+ contents=contents
227
+ )
228
+ raw_text = response.text
229
+ print("πŸ“₯ Received response (chars):", len(raw_text))
230
+ return raw_text
231
+ except Exception as e2:
232
+ print(f"❌ Fallback also failed: {e2}")
233
+ raise
234
 
235
  # ---------------- PARSERS ----------------
236
  def extract_question_ids_from_qpms(text: str):
 
346
  return grading_json
347
 
348
  # ---------------- MAPPING/IMPRINT HELPERS ----------------
349
+ def ask_gemini_for_mapping_batch(image_paths, grading_json, expected_ids=None, rows=GRID_ROWS, cols=GRID_COLS):
350
  """
351
  Send multiple page images together to Gemini for batch mapping processing.
352
  """
 
373
  images = [Image.open(p) for p in image_paths]
374
 
375
  print(f"πŸ“‘ Sending batch mapping request for {len(image_paths)} pages to Gemini...")
 
376
 
377
+ try:
378
+ contents = [prompt] + images
379
+ response = client.models.generate_content(
380
+ model="gemini-2.0-flash-exp",
381
+ contents=contents
382
+ )
383
+ raw_text = response.text
384
+ except:
385
+ print("⚠️ Trying fallback model for mapping...")
386
+ contents = [prompt] + images
387
+ response = client.models.generate_content(
388
+ model="gemini-1.5-flash",
389
+ contents=contents
390
+ )
391
+ raw_text = response.text
392
 
393
  print("πŸ“₯ Batch mapping response (chars):", len(raw_text))
394
  print("πŸ”Ž Gemini raw batch output:")
 
407
  print(f"❌ Failed to parse Gemini JSON mapping: {e}")
408
  return []
409
 
410
+ def imprint_marks_using_mapping(pdf_path, grading_json, output_pdf, expected_ids=None, rows=GRID_ROWS, cols=GRID_COLS):
411
  """
412
  Convert PDF to images, create grid-numbered images for batch sending to Gemini,
413
  then annotate and produce imprinted PDF.
 
451
 
452
  for start in range(0, len(temp_grid_images), batch_size):
453
  batch_paths = temp_grid_images[start:start+batch_size]
454
+ batch_mapping = ask_gemini_for_mapping_batch(batch_paths, grading_json, expected_ids, rows, cols)
455
  all_mappings.extend(batch_mapping)
456
  print(f"βœ… Processed batch {start//batch_size + 1}: pages {start+1}-{start+len(batch_paths)}")
457
 
 
523
  # ---------------- PIPELINE ----------------
524
  def align_and_grade_pipeline(qp_path, ms_path, ans_path, imprint=False):
525
  """
526
+ Final pipeline with graph-aware grading logic using NEW SDK.
527
  """
528
  try:
529
  print("πŸ” Starting pipeline...")
 
536
  print("πŸ“Ž Merged QP + MS ->", merged_qpms_path)
537
 
538
  print("πŸ”Ό Uploading files to Gemini...")
539
+ merged_uploaded = upload_to_gemini(merged_qpms_path)
540
+ ans_uploaded = upload_to_gemini(ans_path)
541
  print("βœ… Upload complete.")
542
 
 
 
543
  print("1.i) Transcribing QP+MS (questions first, then full markscheme, with graph detection)...")
544
  qpms_prompt = PROMPTS["QP_MS_TRANSCRIPTION"]["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 ====)"
545
+ qpms_text = gemini_generate_content(qpms_prompt, file_upload_obj=merged_uploaded)
546
  print("πŸ“„ QP+MS transcription received. Saving debug file: debug_qpms_transcript.txt")
547
  with open("debug_qpms_transcript.txt", "w", encoding="utf-8") as f:
548
  f.write(qpms_text)
 
560
 
561
  print("1.ii) Building AS transcription prompt with expected question IDs and graph detection, sending to Gemini...")
562
  as_prompt = build_as_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)"
563
+ as_text = gemini_generate_content(as_prompt, file_upload_obj=ans_uploaded)
564
  print("πŸ“ AS transcription received. Saving debug file: debug_as_transcript.txt")
565
  with open("debug_as_transcript.txt", "w", encoding="utf-8") as f:
566
  f.write(as_text)
 
586
  grading_input += graph_note
587
  grading_prompt_system = PROMPTS["GRADING_PROMPT"]["content"]
588
  grading_images = ms_graph_images + as_graph_images
589
+ grading_text = gemini_generate_content(grading_prompt_system + "\n\nPlease grade the following transcripts:\n" + grading_input, image_obj=grading_images if grading_images else None)
590
  print("🧾 Grading output received. Saving debug file: debug_grading.md")
591
  with open("debug_grading.md", "w", encoding="utf-8") as f:
592
  f.write(grading_text)
 
602
 
603
  imprinted_pdf_path = None
604
  if imprint:
605
+ print("✍ Imprint option enabled. Starting imprinting process...")
606
  imprinted_pdf_path = f"{base_name}_imprinted.pdf"
607
+ imprinted_pdf_path = imprint_marks_using_mapping(ans_path, grading_json, imprinted_pdf_path, extracted_ids)
608
  print("βœ… Imprinting finished. Imprinted PDF at:", imprinted_pdf_path)
609
 
610
  print("🏁 Pipeline finished successfully.")
 
617
  return f"❌ Error: {e}", None, None, None, None
618
 
619
  # ---------------- GRADIO UI ----------------
620
+ with gr.Blocks(title="AI Grading (Fixed - google-genai SDK)") as demo:
621
+ gr.Markdown("## πŸ“˜ AI Grading β€” Fixed with google-genai SDK")
622
+ gr.Markdown("**βœ… Now using the new official `google-genai` SDK (no more ragStoreName errors!)**")
623
 
624
  with gr.Row():
625
  qp_file = gr.File(label="πŸ“„ Upload Question Paper (PDF)")
 
638
  imprint_pdf_file = gr.File(label="πŸ“₯ Download Imprinted PDF (Optional)")
639
 
640
  def run_pipeline(qp_file_obj, ms_file_obj, ans_file_obj, imprint_flag):
641
+ if not qp_file_obj or not ms_file_obj or not ans_file_obj:
642
+ return "❌ Please upload all three files", "", "", None, None
643
+
644
  qp_path = qp_file_obj.name
645
  ms_path = ms_file_obj.name
646
  ans_path = ans_file_obj.name