Provide Gemini with the list of IDs in correction request
This commit is contained in:
1 parent
f978310c4c
commit
2d0f7a219f
9 files changed
+242
-9
No files matched your search
@@ -0,0 +1,69 @@
|
||||
import json
|
||||
import tempfile
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
from copienator import EvaluationWorkspace, atomic_write_json
|
||||
from copienator.commands import correction
|
||||
|
||||
|
||||
class CorrectionIdTests(unittest.TestCase):
|
||||
def test_mismatched_ids_are_rejected_before_any_side_effects(self):
|
||||
for ids, diagnostic in (
|
||||
(["37", "03", "05"], "unexpected IDs: ['03']"),
|
||||
(["37"], "missing IDs: ['05']"),
|
||||
(["37", "05", "05"], "duplicate IDs: ['05']"),
|
||||
):
|
||||
with self.subTest(ids=ids), tempfile.TemporaryDirectory() as directory:
|
||||
root = Path(directory)
|
||||
group = root / "Par label" / "Ex 1" / "Group_1.jpg"
|
||||
group.parent.mkdir(parents=True)
|
||||
group.touch()
|
||||
atomic_write_json(group.with_suffix(".json"), [
|
||||
["37", 0, 500, 1], ["05", 550, 1000, 1],
|
||||
])
|
||||
pdf = root / "Copies" / "Copie37" / "Ex 1.pdf"
|
||||
pdf.parent.mkdir(parents=True)
|
||||
pdf.write_bytes(b"original")
|
||||
workspace = EvaluationWorkspace(root)
|
||||
prior = {"Other": [[{"id": "22", "result": {"score": 4}}]]}
|
||||
atomic_write_json(workspace.correction_file, prior)
|
||||
before = workspace.correction_file.read_bytes()
|
||||
args = correction.build_parser().parse_args([str(root)])
|
||||
correction.configure_runtime(workspace, [(str(group), "Ex 1")], args, api_client=Mock())
|
||||
response = [{"id": pid, "result": {
|
||||
"score": 0, "error": "empty-answer", "feedback": [],
|
||||
}} for pid in ids]
|
||||
with patch.object(correction.prompting, "generate_request", return_value=([], {})), patch.object(
|
||||
correction, "handle_label_errors"
|
||||
) as handle:
|
||||
correction.process_single_task((str(group), "Ex 1"), json.dumps(response))
|
||||
handle.assert_not_called()
|
||||
self.assertEqual(pdf.read_bytes(), b"original")
|
||||
self.assertFalse(pdf.with_name("Ex 1_old.pdf").exists())
|
||||
self.assertEqual(workspace.correction_file.read_bytes(), before)
|
||||
self.assertEqual(correction.completed_tasks, [])
|
||||
self.assertEqual(correction.results, prior)
|
||||
self.assertIn(diagnostic, correction.errors_summary[0][0])
|
||||
|
||||
def test_valid_ids_can_be_returned_in_different_order(self):
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
root = Path(directory)
|
||||
group = root / "Par label" / "Ex 1" / "Group_1.jpg"
|
||||
group.parent.mkdir(parents=True)
|
||||
group.touch()
|
||||
atomic_write_json(group.with_suffix(".json"), [
|
||||
["37", 0, 500, 1], ["05", 550, 1000, 1],
|
||||
])
|
||||
workspace = EvaluationWorkspace(root)
|
||||
args = correction.build_parser().parse_args([str(root)])
|
||||
correction.configure_runtime(workspace, [(str(group), "Ex 1")], args, api_client=Mock())
|
||||
response = [{"id": pid, "result": {
|
||||
"score": 4, "error": "", "feedback": [],
|
||||
}} for pid in ["05", "37"]]
|
||||
with patch.object(correction.prompting, "generate_request", return_value=([], {})):
|
||||
correction.process_single_task((str(group), "Ex 1"), json.dumps(response))
|
||||
self.assertEqual(correction.errors_summary, [])
|
||||
self.assertEqual(correction.results["Ex 1"], [response])
|
||||
self.assertEqual(correction.completed_tasks, [(str(group), "Ex 1")])
|
||||
@@ -0,0 +1,73 @@
|
||||
import tempfile
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
from unittest.mock import patch
|
||||
|
||||
import pymupdf
|
||||
from PIL import Image
|
||||
|
||||
from copienator.commands import cutleft
|
||||
|
||||
|
||||
class NarrowPageCropTests(unittest.TestCase):
|
||||
def test_mixed_pages_crop_narrow_margin_from_very_left(self):
|
||||
normal = Image.new('RGB', (900, 600), 'red')
|
||||
normal.paste('green', (100, 0, 400, 600))
|
||||
narrow = Image.new('RGB', (450, 600), 'red')
|
||||
narrow.paste('blue', (0, 0, 300, 600))
|
||||
thin = Image.new('RGB', (200, 600), 'yellow')
|
||||
with patch.object(cutleft, 'get_pdf_pages', return_value=[normal, narrow, thin]):
|
||||
result = cutleft.process_single_pdf(Path('/tmp/no-original/Copy.pdf'))
|
||||
_, images, schema = result
|
||||
image = images[0]
|
||||
self.assertEqual(schema['full_width_pages'], [3])
|
||||
self.assertEqual(schema['columns_per_file'], [3])
|
||||
self.assertEqual(image.size, (910, 600))
|
||||
self.assertEqual(image.getpixel((0, 0)), (0, 128, 0))
|
||||
self.assertEqual(image.getpixel((299, 0)), (0, 128, 0))
|
||||
self.assertEqual(image.getpixel((300, 0)), (0, 0, 0))
|
||||
self.assertEqual(image.getpixel((305, 0)), (0, 0, 255))
|
||||
self.assertEqual(image.getpixel((604, 0)), (0, 0, 255))
|
||||
self.assertEqual(image.getpixel((610, 0)), (255, 255, 0))
|
||||
self.assertEqual(image.getpixel((809, 0)), (255, 255, 0))
|
||||
self.assertEqual(image.getpixel((810, 0)), (255, 255, 255))
|
||||
self.assertEqual(image.getpixel((909, 0)), (255, 255, 255))
|
||||
|
||||
def test_narrow_margin_can_be_shifted_and_enlarged(self):
|
||||
normal = Image.new('RGB', (900, 600), 'white')
|
||||
narrow = Image.new('RGB', (450, 600), 'red')
|
||||
narrow.paste('blue', (50, 0, 450, 600))
|
||||
with patch.object(cutleft, 'get_pdf_pages', return_value=[normal, narrow]):
|
||||
result = cutleft.process_single_pdf(
|
||||
Path('/tmp/no-original/Copy.pdf'), shift_offset=50, width_offset=100)
|
||||
self.assertEqual(result[1][0].size, (805, 600))
|
||||
self.assertEqual(result[1][0].getpixel((405, 0)), (0, 0, 255))
|
||||
self.assertEqual(result[1][0].getpixel((804, 0)), (0, 0, 255))
|
||||
|
||||
def test_legacy_original_identifies_narrow_pages_without_normal_siblings(self):
|
||||
with tempfile.TemporaryDirectory() as temporary:
|
||||
root = Path(temporary)
|
||||
originals = root / 'Copies Originales'
|
||||
originals.mkdir()
|
||||
with pymupdf.open() as document:
|
||||
document.new_page(width=1191, height=842)
|
||||
document.save(originals / 'Copy.pdf')
|
||||
images = [Image.new('RGB', (900, 2339)), Image.new('RGB', (750, 2339))]
|
||||
with patch.object(cutleft, 'get_pdf_pages', return_value=images):
|
||||
result = cutleft.process_single_pdf(root / 'Copies' / 'Copy.pdf')
|
||||
self.assertEqual(result[2]['full_width_pages'], [])
|
||||
self.assertEqual(result[1][0].width, 2 * int((1191 / 2 * 200 / 72) // 3) + 5)
|
||||
|
||||
def test_small_main_split_adjustments_keep_margin_cropping(self):
|
||||
images = [Image.new('RGB', (1600, 2339)), Image.new('RGB', (1694, 2339))]
|
||||
with patch.object(cutleft, 'get_pdf_pages', return_value=images):
|
||||
result = cutleft.process_single_pdf(Path('/tmp/no-original/Copy.pdf'))
|
||||
self.assertEqual(result[2]['full_width_pages'], [])
|
||||
self.assertEqual(result[1][0].width, 2 * (1694 // 3) + 5)
|
||||
|
||||
def test_fullpage_option_still_keeps_every_page_separately(self):
|
||||
images = [Image.new('RGB', (900, 600)), Image.new('RGB', (450, 600))]
|
||||
with patch.object(cutleft, 'get_pdf_pages', return_value=images):
|
||||
result = cutleft.process_single_pdf(Path('/tmp/no-original/Copy.pdf'), max_per_file=1)
|
||||
self.assertEqual([image.size for image in result[1]], [(900, 600), (450, 600)])
|
||||
self.assertEqual(result[2]['columns_per_file'], [1, 1])
|
||||
@@ -26,6 +26,7 @@ class GeminiConfigTests(unittest.TestCase):
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
image = Path(directory) / "answer.jpg"
|
||||
image.write_bytes(b"image")
|
||||
image.with_suffix(".json").write_text('[["01", 0, 400, 1.0, "Ex 1"]]')
|
||||
for level in ("minimal", "low", "medium", "high", None):
|
||||
with self.subTest(level=level), patch.object(prompting, "make_prompt", return_value="Grade"):
|
||||
_, config = prompting.generate_request(directory, image, "Ex 1", thinking_level=level)
|
||||
|
||||
@@ -1775,8 +1775,9 @@ class StandardCliTests(unittest.TestCase):
|
||||
args,
|
||||
api_client=Mock(),
|
||||
)
|
||||
with patch.object(module.prompting, "make_prompt", return_value="prompt"):
|
||||
with patch.object(module.prompting, "make_prompt", return_value="prompt") as make_prompt:
|
||||
self.assertEqual(module.run_configured(args), 0)
|
||||
make_prompt.assert_called_once_with(evaluation, "Ex 1", expected_ids=["01"])
|
||||
lines = (evaluation / "batch_requests_flash.jsonl").read_text().splitlines()
|
||||
self.assertEqual(len(lines), 1)
|
||||
self.assertEqual(json.loads(lines[0])["key"], str(image))
|
||||
|
||||
@@ -1,10 +1,26 @@
|
||||
import unittest
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
from unittest.mock import patch
|
||||
|
||||
from copienator import prompting
|
||||
|
||||
|
||||
class PromptingTests(unittest.TestCase):
|
||||
def test_live_prompt_includes_only_metadata_ids_in_order(self) -> None:
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
image = Path(directory) / "Group_16.jpg"
|
||||
image.write_bytes(b"image")
|
||||
image.with_suffix(".json").write_text(
|
||||
'[["22", 50, 834, 1], ["37", 904, 3250, 1], '
|
||||
'["13", 3320, 4072, 1], ["05", 4142, 4892, 1]]'
|
||||
)
|
||||
contents, _ = prompting.generate_request(directory, image, "I)12)")
|
||||
prompt = contents[0].parts[1].text
|
||||
self.assertIn('["22", "37", "13", "05"]', prompt)
|
||||
self.assertIn("exactement un résultat par identifiant", prompt)
|
||||
self.assertIn("ignore les numéros manuscrits", prompt)
|
||||
|
||||
def test_perspective_is_inserted_with_alternative_method_guidance(self) -> None:
|
||||
with patch.object(
|
||||
prompting, "get_label_text_content", return_value="Question"
|
||||
|
||||
Reference in new issue
Block a user