yongchoooon commited on
Commit
958a0a5
ยท
verified ยท
1 Parent(s): a03f69c

Use bounded PDF reading and source-guided verification; honor requested dates and image coordinates

Browse files
Files changed (1) hide show
  1. app.py +22 -9
app.py CHANGED
@@ -30,6 +30,7 @@ MODEL = os.getenv("GROQ_MODEL", "openai/gpt-oss-120b")
30
  INPUT_PATH = Path("/tmp/course-agent-private-inputs.json")
31
  CACHE_PATH = Path("/tmp/course-agent-owner-records.json")
32
  MAX_BYTES = 10 * 1024 * 1024
 
33
  USER_AGENT = "HF-Course-Agent/1.1 (https://huggingface.co/spaces/yongchoooon/Final_Assignment_Template)"
34
 
35
 
@@ -71,8 +72,9 @@ def download(url):
71
  chunks, size = [], 0
72
  for chunk in response.iter_content(65536):
73
  size += len(chunk)
74
- if size > MAX_BYTES:
75
- raise ValueError("File exceeds the 10 MB reading limit.")
 
76
  chunks.append(chunk)
77
  return b"".join(chunks)
78
  raise ValueError("Too many redirects.")
@@ -349,9 +351,10 @@ def attachment_tool(item):
349
  {"type": "text", "text": "Analyze this attached image for the following question. "
350
  "Describe precisely what is visible and relevant; distinguish observations from deductions. "
351
  "For a chess board, give exact piece positions and a complete FEN, checking square coordinates and orientation. "
 
352
  "Do not guess a winning move: a separate chess engine will verify the FEN. " + item["question"]},
353
  {"type": "image_url", "image_url": {"url": "data:" + mime + ";base64," + base64.b64encode(content).decode()}}]}],
354
- max_tokens=2048)
355
  if response.choices[0].finish_reason == "length":
356
  raise ValueError("Image analysis was truncated.")
357
  return "Image analysis: " + (response.choices[0].message.content or "")
@@ -393,10 +396,13 @@ def solve(item, trace=None, progress=None):
393
  "Use primary sources. Never treat a shopping/search-results page that merely repeats a question "
394
  "as evidence. For named-source questions, read and confirm the claim in the specified source "
395
  "before answering. A failed page download does not provide evidence. For English Wikipedia use read_wikipedia. If a historical year is "
396
- "specified, set as_of to December 31 of that year. Select the relevant article section, "
 
397
  "and paginate if more text is available. Read an attachment when present. "
398
  "Check every constraint and exclusion before answering. Culinary categories are not botanical "
399
- "categories. For ranking/counts verify the complete relevant table, not a search snippet. "
 
 
400
  "For chess positions extract the FEN from the image, then verify the move with analyse_chess_position. "
401
  "Prefer narrower entity searches over quoting an entire long question. "
402
  "Do not repeat identical failed requests. Never claim to have read unsupported media. "
@@ -631,7 +637,7 @@ def import_answers(text, profile: gr.OAuthProfile | None):
631
  return f"๋‹ต์•ˆ {len(records)}๊ฐœ ๋ณต์› ์™„๋ฃŒ. ์•„์ง ์ œ์ถœํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.", records, table, backup
632
 
633
 
634
- def evaluate(limit, retry_ids, records, profile: gr.OAuthProfile | None, progress=gr.Progress()):
635
  authorize(profile)
636
  records = dict(records or {})
637
  try:
@@ -653,7 +659,13 @@ def evaluate(limit, retry_ids, records, profile: gr.OAuthProfile | None, progres
653
  table, backup = export_answers(records)
654
  yield f"{index}/{len(pending)} ํ’€์ด ์ค‘. ๋ฌด๋ฃŒ ํ•œ๋„์— ๋งž์ถฐ ํ˜ธ์ถœ ๊ฐ„ ๋Œ€๊ธฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.", records, table, backup
655
  try:
656
- answer = solve(item, trace=trace, progress=progress)
 
 
 
 
 
 
657
  records[item["task_id"]] = {"question": item["question"], "answer": answer, "trace": trace}
658
  except Exception as error:
659
  message = error_message(error)
@@ -700,7 +712,7 @@ def submit(records, profile: gr.OAuthProfile | None):
700
 
701
 
702
  with gr.Blocks() as demo:
703
- gr.Markdown("# Unit 4 ยท ๋‚˜์˜ AI ์—์ด์ „ํŠธ ยท v7")
704
  gr.Markdown("๋กœ๊ทธ์ธ โ†’ ์—ฐ๊ฒฐ ํ™•์ธ โ†’ ํ•œ ๋ฌธ์ œ ์‹œํ—˜ โ†’ ์ „์ฒด ํ’€์ด โ†’ ๋‹ต์•ˆ ํ™•์ธ โ†’ ์ œ์ถœ")
705
  gr.LoginButton()
706
  records = gr.State({})
@@ -717,13 +729,14 @@ with gr.Blocks() as demo:
717
  restore_json = gr.Textbox(label="๊ธฐ์กด ๋‹ต์•ˆ JSON", lines=3)
718
  import_button = gr.Button("JSON ๋‹ต์•ˆ ๋ณต์›")
719
  retry_ids = gr.Textbox(label="๋‹ค์‹œ ํ’€ ๋ฌธ์ œ ID (์„ ํƒ)", info="๋น„์›Œ ๋‘๋ฉด ๋ฏธ์™„๋ฃŒ ๋ฌธ์ œ๋งŒ ํ’‰๋‹ˆ๋‹ค.")
 
720
  with gr.Accordion("์†Œ์œ ์ž ์ „์šฉ ์ฒจ๋ถ€์ž๋ฃŒ ๋ณต์›", open=False):
721
  input_json = gr.Textbox(label="์ฒจ๋ถ€์ž๋ฃŒ JSON", lines=2)
722
  input_button = gr.Button("์›๋ณธ ์ฒจ๋ถ€์ž๋ฃŒ ๋ณต์›")
723
  submit_button = gr.Button("3. ํ˜„์žฌ ๋‹ต์•ˆ์„ ๊ณต์‹ ๋ฆฌ๋”๋ณด๋“œ์— ์ œ์ถœ")
724
  gr.Markdown("๊ฒ€์ƒ‰ยท์œ„ํ‚ค๋ฐฑ๊ณผยท๊ณ„์‚ฐยทPythonยทPDFยท์—‘์…€ยท์Œ์„ฑยท์ด๋ฏธ์ง€๋ฅผ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. ์˜์ƒ์˜ ์žฅ๋ฉด ๋ถ„์„์€ ์•„์ง ์ง€์›ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.")
725
  connection.click(test_connection, outputs=status, concurrency_id="agent", concurrency_limit=1)
726
- run_event = run.click(evaluate, inputs=[limit, retry_ids, records], outputs=[status, records, results, backup],
727
  concurrency_id="agent", concurrency_limit=1)
728
  stop.click(fn=None, cancels=[run_event], queue=False)
729
  input_button.click(import_inputs, inputs=input_json, outputs=status, concurrency_id="inputs", concurrency_limit=1)
 
30
  INPUT_PATH = Path("/tmp/course-agent-private-inputs.json")
31
  CACHE_PATH = Path("/tmp/course-agent-owner-records.json")
32
  MAX_BYTES = 10 * 1024 * 1024
33
+ MAX_PDF_BYTES = 40 * 1024 * 1024
34
  USER_AGENT = "HF-Course-Agent/1.1 (https://huggingface.co/spaces/yongchoooon/Final_Assignment_Template)"
35
 
36
 
 
72
  chunks, size = [], 0
73
  for chunk in response.iter_content(65536):
74
  size += len(chunk)
75
+ limit = MAX_PDF_BYTES if (chunks and chunks[0].startswith(b"%PDF")) or (not chunks and chunk.startswith(b"%PDF")) else MAX_BYTES
76
+ if size > limit:
77
+ raise ValueError("File exceeds the bounded reading limit.")
78
  chunks.append(chunk)
79
  return b"".join(chunks)
80
  raise ValueError("Too many redirects.")
 
351
  {"type": "text", "text": "Analyze this attached image for the following question. "
352
  "Describe precisely what is visible and relevant; distinguish observations from deductions. "
353
  "For a chess board, give exact piece positions and a complete FEN, checking square coordinates and orientation. "
354
+ "Read edge labels; do not assume White is at the bottom. Use the printed files and ranks. "
355
  "Do not guess a winning move: a separate chess engine will verify the FEN. " + item["question"]},
356
  {"type": "image_url", "image_url": {"url": "data:" + mime + ";base64," + base64.b64encode(content).decode()}}]}],
357
+ max_tokens=2048, reasoning_effort="none", reasoning_format="hidden")
358
  if response.choices[0].finish_reason == "length":
359
  raise ValueError("Image analysis was truncated.")
360
  return "Image analysis: " + (response.choices[0].message.content or "")
 
396
  "Use primary sources. Never treat a shopping/search-results page that merely repeats a question "
397
  "as evidence. For named-source questions, read and confirm the claim in the specified source "
398
  "before answering. A failed page download does not provide evidence. For English Wikipedia use read_wikipedia. If a historical year is "
399
+ "specified, use its exact cutoff: last day of the named month if a month is given, "
400
+ "December 31 only when just a year is given. Select the relevant article section, "
401
  "and paginate if more text is available. Read an attachment when present. "
402
  "Check every constraint and exclusion before answering. Culinary categories are not botanical "
403
+ "categories. Classify the actual edible plant organ (leaf, stem, root, flower, fruit, seed). "
404
+ "Exclude botanical fruits even if used as culinary vegetables; do not overlook edible herb leaves. "
405
+ "For ranking/counts verify the complete relevant table, not a search snippet. "
406
  "For chess positions extract the FEN from the image, then verify the move with analyse_chess_position. "
407
  "Prefer narrower entity searches over quoting an entire long question. "
408
  "Do not repeat identical failed requests. Never claim to have read unsupported media. "
 
637
  return f"๋‹ต์•ˆ {len(records)}๊ฐœ ๋ณต์› ์™„๋ฃŒ. ์•„์ง ์ œ์ถœํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.", records, table, backup
638
 
639
 
640
+ def evaluate(limit, retry_ids, source_hints, records, profile: gr.OAuthProfile | None, progress=gr.Progress()):
641
  authorize(profile)
642
  records = dict(records or {})
643
  try:
 
659
  table, backup = export_answers(records)
660
  yield f"{index}/{len(pending)} ํ’€์ด ์ค‘. ๋ฌด๋ฃŒ ํ•œ๋„์— ๋งž์ถฐ ํ˜ธ์ถœ ๊ฐ„ ๋Œ€๊ธฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.", records, table, backup
661
  try:
662
+ solving_item = item
663
+ if source_hints and source_hints.strip():
664
+ urls = re.findall(r"https?://[^\s]+", source_hints)[:6]
665
+ if not urls:
666
+ raise ValueError("์ฐธ๊ณ  ์ž๋ฃŒ๋Š” ๊ณต๊ฐœ ์›๋ฌธ URL์„ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š”.")
667
+ solving_item = dict(item, question=item["question"] + "\nAdditional primary-source URLs to inspect first: " + "\n".join(urls))
668
+ answer = solve(solving_item, trace=trace, progress=progress)
669
  records[item["task_id"]] = {"question": item["question"], "answer": answer, "trace": trace}
670
  except Exception as error:
671
  message = error_message(error)
 
712
 
713
 
714
  with gr.Blocks() as demo:
715
+ gr.Markdown("# Unit 4 ยท ๋‚˜์˜ AI ์—์ด์ „ํŠธ ยท v8")
716
  gr.Markdown("๋กœ๊ทธ์ธ โ†’ ์—ฐ๊ฒฐ ํ™•์ธ โ†’ ํ•œ ๋ฌธ์ œ ์‹œํ—˜ โ†’ ์ „์ฒด ํ’€์ด โ†’ ๋‹ต์•ˆ ํ™•์ธ โ†’ ์ œ์ถœ")
717
  gr.LoginButton()
718
  records = gr.State({})
 
729
  restore_json = gr.Textbox(label="๊ธฐ์กด ๋‹ต์•ˆ JSON", lines=3)
730
  import_button = gr.Button("JSON ๋‹ต์•ˆ ๋ณต์›")
731
  retry_ids = gr.Textbox(label="๋‹ค์‹œ ํ’€ ๋ฌธ์ œ ID (์„ ํƒ)", info="๋น„์›Œ ๋‘๋ฉด ๋ฏธ์™„๋ฃŒ ๋ฌธ์ œ๋งŒ ํ’‰๋‹ˆ๋‹ค.")
732
+ source_hints = gr.Textbox(label="์ฐธ๊ณ ํ•  ์ž๋ฃŒ URL (์„ ํƒ)", info="์›๋ฌธ ๋งํฌ๋ฅผ ์•Œ๊ณ  ์žˆ์„ ๋•Œ ์ž…๋ ฅํ•˜๋ฉด ํ•ด๋‹น ์ž๋ฃŒ๋ถ€ํ„ฐ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.")
733
  with gr.Accordion("์†Œ์œ ์ž ์ „์šฉ ์ฒจ๋ถ€์ž๋ฃŒ ๋ณต์›", open=False):
734
  input_json = gr.Textbox(label="์ฒจ๋ถ€์ž๋ฃŒ JSON", lines=2)
735
  input_button = gr.Button("์›๋ณธ ์ฒจ๋ถ€์ž๋ฃŒ ๋ณต์›")
736
  submit_button = gr.Button("3. ํ˜„์žฌ ๋‹ต์•ˆ์„ ๊ณต์‹ ๋ฆฌ๋”๋ณด๋“œ์— ์ œ์ถœ")
737
  gr.Markdown("๊ฒ€์ƒ‰ยท์œ„ํ‚ค๋ฐฑ๊ณผยท๊ณ„์‚ฐยทPythonยทPDFยท์—‘์…€ยท์Œ์„ฑยท์ด๋ฏธ์ง€๋ฅผ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. ์˜์ƒ์˜ ์žฅ๋ฉด ๋ถ„์„์€ ์•„์ง ์ง€์›ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.")
738
  connection.click(test_connection, outputs=status, concurrency_id="agent", concurrency_limit=1)
739
+ run_event = run.click(evaluate, inputs=[limit, retry_ids, source_hints, records], outputs=[status, records, results, backup],
740
  concurrency_id="agent", concurrency_limit=1)
741
  stop.click(fn=None, cancels=[run_event], queue=False)
742
  input_button.click(import_inputs, inputs=input_json, outputs=status, concurrency_id="inputs", concurrency_limit=1)