SkinGPT-R1 / tests /test_evaluation.py
yuhos16's picture yuhao's picture
Upload code
be0c348
Raw History Blame Contribute Delete
5.54 kB
import json
from pathlib import Path
import subprocess
import sys
import tempfile
import unittest
from evaluation.check_reported_results import percent, wilson
from evaluation.judge_request import build_judge_request, validate_judge_response
from inference.evaluation.protocol import REPO_ROOT, build_messages, generation_kwargs, load_labels
from inference.evaluation.run_inference import check_local_checkpoint, validate_mask
class EvaluationContractTests(unittest.TestCase):
def test_ddi_system_role_and_no_reference(self):
messages = build_messages('lesion.jpg')
self.assertEqual([m['role'] for m in messages], ['system', 'user'])
self.assertIn('Do not invent findings', messages[0]['content'])
self.assertNotIn('ground_truth', json.dumps(messages))
with self.assertRaises(TypeError):
build_messages('lesion.jpg', ground_truth='Melanoma')
with self.assertRaises(ValueError):
build_messages('lesion.jpg', labels=['Melanoma'])
def test_classification_preserves_label_order_without_system(self):
labels = load_labels(REPO_ROOT / 'prompts/labels_160case.json')
self.assertEqual(len(labels), 23)
messages = build_messages('lesion.jpg', 'classification', list(reversed(labels)))
self.assertEqual([m['role'] for m in messages], ['user'])
text = messages[0]['content'][1]['text']
self.assertLess(text.index('Vascular Tumors'), text.index('Fungal infection - Tinea'))
self.assertNotIn('{candidate_labels}', text)
def test_duplicate_labels_rejected(self):
with tempfile.TemporaryDirectory() as tmp:
p = Path(tmp) / 'labels.json'
p.write_text('["Eczema", "Eczema"]')
with self.assertRaises(ValueError):
load_labels(p)
def test_greedy_does_not_pass_sampling_arguments(self):
config = json.loads((REPO_ROOT / 'configs/evaluation.json').read_text())
self.assertEqual(generation_kwargs(config)['temperature'], 0.7)
self.assertEqual(generation_kwargs(config)['max_new_tokens'], 4096)
config['do_sample'] = False
self.assertNotIn('temperature', generation_kwargs(config))
self.assertNotIn('top_p', generation_kwargs(config))
def test_judge_tail_and_separate_reference(self):
request = build_judge_request('Eczema', 'DROP_THIS' + 'A' * 20000)
text = request['messages'][1]['content']
self.assertNotIn('DROP_THIS', text)
self.assertIn('[DDI reference diagnosis]\nEczema', text)
self.assertEqual(request['temperature'], 0.0)
self.assertEqual(request['extra_body'], {'enable_thinking': False})
self.assertNotIn('seed', request)
def test_judge_schema_rejects_false_string_and_ambiguous_success(self):
valid = {'predicted_diagnosis': 'Eczema', 'correct': True, 'reason': 'Equivalent diagnosis.'}
self.assertEqual(validate_judge_response(json.dumps(valid)), valid)
for changed in [dict(valid, correct='false'), dict(valid, reason=''),
dict(valid, predicted_diagnosis=''), dict(valid, extra=1)]:
with self.assertRaises(ValueError):
validate_judge_response(json.dumps(changed))
def test_mask_requires_binary_nonempty_matching_vocabulary(self):
with tempfile.TemporaryDirectory() as tmp:
p = Path(tmp) / 'mask.json'
for value in [[1, 0], [1, 0, 2], [0, 0, 0]]:
p.write_text(json.dumps(value))
with self.assertRaises(ValueError):
validate_mask(p, 3)
p.write_text('[1, 0, 1]')
self.assertEqual(validate_mask(p, 3), [1, 0, 1])
def test_missing_and_lfs_pointer_weights_rejected(self):
with tempfile.TemporaryDirectory() as tmp:
root = Path(tmp)
(root / 'model.safetensors.index.json').write_text(json.dumps({'weight_map': {'a': 'model.safetensors'}}))
with self.assertRaises(FileNotFoundError):
check_local_checkpoint(root)
(root / 'model.safetensors').write_text('version https://git-lfs.github.com/spec/v1\noid sha256:example\n')
with self.assertRaises(ValueError):
check_local_checkpoint(root)
def test_dry_run_without_model_or_optional_imports(self):
with tempfile.TemporaryDirectory() as tmp:
image = Path(tmp) / 'lesion.jpg'
image.write_bytes(b'dry-run fixture; image decoding is not invoked')
result = subprocess.run([
sys.executable, '-S', '-m', 'inference.evaluation.run_inference',
'--image', str(image), '--model-path', str(Path(tmp) / 'no_weights'),
'--dry-run',
], cwd=REPO_ROOT, check=True, capture_output=True, text=True)
payload = json.loads(result.stdout)
self.assertTrue(payload['dry_run'])
self.assertEqual(payload['configuration']['min_pixels'], 3136)
self.assertEqual(payload['messages'][0][0]['role'], 'system')
self.assertNotIn('outputs', payload)
def test_reporting_uses_counts_before_rounding(self):
self.assertEqual(str(percent(81, 160)), '50.63')
self.assertEqual(str(percent(90, 160)), '56.25')
self.assertEqual(str(percent(9, 160)), '5.63')
low, high = wilson(72, 158)
self.assertLess(low, 100 * 72 / 158)
self.assertGreater(high, 100 * 72 / 158)
if __name__ == '__main__':
unittest.main()