Tri-Netra-AI / tests /e2e_explain.py
anannyavyas1's picture
Upload folder using huggingface_hub (part 2)
3366f95 verified
Raw
History Blame Contribute Delete
5.06 kB
"""E2E test of the /explain pipeline.
Hits build_explanation() directly (skipping HTTP) on a known tumor sample.
Two passes:
1. backend='none' -> validates segmentation + classifier + feature extractor
(deterministic fallback narrative; no LLM call).
2. backend='ollama' -> validates real LLM call against qwen2.5vl:7b.
Saves the response JSON to e2e_explain_<backend>.json for inspection and prints
a one-line PASS/FAIL summary per stage.
"""
from __future__ import annotations
import json
import sys
import time
from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent
sys.path.insert(0, str(ROOT))
from dashboard import build_explanation # noqa: E402
def _sample_bytes() -> bytes:
candidates = [
ROOT / 'dataset_real' / 'test' / 'tumor' / 'tumor_00000.jpg',
ROOT / 'dataset_real' / 'val' / 'tumor' / 'tumor_00000.jpg',
ROOT / 'dataset_real' / 'train' / 'tumor' / 'tumor_00000.jpg',
]
for p in candidates:
if p.exists():
print(f'[e2e] using sample: {p}')
return p.read_bytes()
raise FileNotFoundError('No tumor_00000.jpg found in dataset_real/{test,val,train}/tumor')
def _check(label: str, ok: bool, extra: str = ''):
print(f' [{"PASS" if ok else "FAIL"}] {label}{(" - " + extra) if extra else ""}')
return ok
def run_one(image_bytes: bytes, backend: str | None):
tag = backend or 'auto'
print(f'\n=== /explain backend={tag} ===')
t0 = time.time()
result = build_explanation(image_bytes, threshold=0.5, modality=None, backend=backend)
elapsed = time.time() - t0
print(f' elapsed: {elapsed:.1f}s')
# Persist for inspection.
out_path = ROOT / f'e2e_explain_{tag}.json'
# Strip giant data URLs before writing to disk (kept in memory for assertions).
redacted = json.loads(json.dumps(result, default=str))
for k in ('mask', 'overlay'):
if isinstance(redacted.get('segmentation'), dict) and k in redacted['segmentation']:
redacted['segmentation'][k] = f'<data:image/png;base64 {len(str(result["segmentation"].get(k,"")))} chars>'
if isinstance(redacted.get('classifiers'), dict):
for name, c in redacted['classifiers'].items():
if isinstance(c, dict):
if c.get('gradcam'):
c['gradcam'] = f'<data:image/png;base64 {len(c["gradcam"])} chars>'
out_path.write_text(json.dumps(redacted, indent=2, default=str), encoding='utf-8')
print(f' saved: {out_path.name}')
ok = True
ok &= _check('success', bool(result.get('success')), str(result.get('error')))
seg = result.get('segmentation', {}) or {}
ok &= _check('segmentation.success', bool(seg.get('success')))
ok &= _check('segmentation has mask', bool(seg.get('mask')))
ok &= _check('segmentation has overlay', bool(seg.get('overlay')))
cls = result.get('classifiers', {}) or {}
for m in ('cnn', 'transfer', 'vit'):
c = cls.get(m, {}) or {}
ok &= _check(f'classifier[{m}] probability', isinstance(c.get('probability'), (int, float)))
feats = result.get('features', {}) or {}
ok &= _check('features.geometry', 'geometry' in feats)
ok &= _check('features.intensity_per_channel', 'intensity_per_channel' in feats)
ok &= _check('features.texture', 'texture' in feats)
# New medical features:
ok &= _check('features.morphology', 'morphology' in feats)
ok &= _check('features.mass_effect', 'mass_effect' in feats)
ok &= _check('features.internal_architecture', 'internal_architecture' in feats)
ok &= _check('features.grade_evidence', 'grade_evidence' in feats)
ok &= _check('features.overall_confidence', 'overall_confidence' in feats)
exp = result.get('explanation', {}) or {}
ok &= _check(f'explanation.backend == "{tag}"', exp.get('backend') == (backend or exp.get('backend')),
f'got "{exp.get("backend")}"')
ok &= _check('explanation.summary non-empty', bool(exp.get('summary')))
ok &= _check('explanation.impression non-empty', bool(exp.get('impression')))
ok &= _check('explanation.differential_with_citations',
isinstance(exp.get('differential_with_citations'), list))
ok &= _check('explanation.recommendation non-empty', bool(exp.get('recommendation')))
ok &= _check('explanation.hallucination_safety set', bool(exp.get('hallucination_safety')))
if backend != 'none':
ok &= _check('explanation.llm_passes set', isinstance(exp.get('llm_passes'), dict))
return ok
def main():
image_bytes = _sample_bytes()
pass_none = run_one(image_bytes, 'none')
pass_ollama = run_one(image_bytes, 'ollama')
print('\n=== overall ===')
print(f' backend=none : {"PASS" if pass_none else "FAIL"}')
print(f' backend=ollama : {"PASS" if pass_ollama else "FAIL"}')
sys.exit(0 if (pass_none and pass_ollama) else 1)
if __name__ == '__main__':
main()