Download backend/tests/test_jarvis.py from Shwy1001/BioPatch: direct link, hf CLI and curl.
- Browser
- Download file 11.2 kB
-
https://huggingface.co/spaces/Shwy1001/BioPatch/resolve/main/backend/tests/test_jarvis.py
- Command line
-
hf download hf://spaces/Shwy1001/BioPatch/backend/tests/test_jarvis.py
-
curl -L -o test_jarvis.py https://huggingface.co/spaces/Shwy1001/BioPatch/resolve/main/backend/tests/test_jarvis.py
11.2 kB
| """Tests for the BioPatch Jarvis explainability + RAG layer.""" | |
| from __future__ import annotations | |
| import sys | |
| from datetime import datetime, timedelta, timezone | |
| from pathlib import Path | |
| sys.path.insert(0, str(Path(__file__).resolve().parent.parent)) | |
| import pytest | |
| from ai.features import PatientFactors # noqa: E402 | |
| from ai.jarvis import ( # noqa: E402 | |
| AutonomousMonitor, | |
| ClinicalBrain, | |
| JarvisPredictor, | |
| PatientSummary, | |
| ReasonerInputs, | |
| active_risk_factors, | |
| cite, | |
| get_brain, | |
| get_monitor, | |
| get_rag, | |
| get_reasoner, | |
| get_store, | |
| ontology_snapshot, | |
| ) | |
| from ai.jarvis.knowledge_store import KnowledgeStore # noqa: E402 | |
| from ai.jarvis.medical_rag import MedicalRAG, expand_query # noqa: E402 | |
| from ai.jarvis.recommendations import build_recommendations # noqa: E402 | |
| # --------------------------------------------------------------------------- | |
| # Ontology | |
| # --------------------------------------------------------------------------- | |
| def test_ontology_snapshot_has_three_sensors() -> None: | |
| snap = ontology_snapshot() | |
| assert set(snap["sensors"].keys()) == {"ph", "temperature_c", "humidity"} | |
| for sensor in snap["sensors"].values(): | |
| assert sensor["bands"], "every sensor must declare bands" | |
| for band in sensor["bands"]: | |
| assert band["severity"] in {"stable", "watch", "warning", "critical"} | |
| assert band["rationale"] | |
| assert band["citations"], "every band must point at a citation" | |
| def test_active_risk_factors_diabetic_elderly() -> None: | |
| rfs = active_risk_factors(age=82, diabetes=True, smoker=False) | |
| keys = {rf.key for rf in rfs} | |
| assert keys == {"diabetes", "elderly_80"} | |
| def test_cite_returns_known_metadata() -> None: | |
| out = cite("WHO_SSI_2018") | |
| assert out and out[0]["publisher"] == "World Health Organization" | |
| def test_cite_skips_unknown_keys() -> None: | |
| assert cite("does-not-exist") == [] | |
| # --------------------------------------------------------------------------- | |
| # Knowledge store + RAG | |
| # --------------------------------------------------------------------------- | |
| def test_store_bootstrapped_from_corpus() -> None: | |
| store = get_store() | |
| assert store.count() >= 25 | |
| assert "ph" in store.topics() | |
| def test_search_returns_relevant_passages() -> None: | |
| rag = get_rag() | |
| hits = rag.search("Why is alkaline pH a sign of infection?", top_k=3) | |
| assert hits, "expected at least one hit" | |
| # Top result should be ph-related | |
| assert hits[0].passage.topic == "ph" | |
| def test_search_supports_language_filter() -> None: | |
| rag = get_rag() | |
| hits_fr = rag.search("humidite cicatrisation", top_k=3, lang="fr") | |
| assert any(h.passage.lang == "fr" for h in hits_fr) | |
| def test_expand_query_adds_synonyms() -> None: | |
| expanded = expand_query("pH infection") | |
| assert "alkaline" in expanded | |
| assert "SSI" in expanded | |
| def test_writable_upsert(tmp_path: Path) -> None: | |
| store = KnowledgeStore(store_path=tmp_path / "jarvis.sqlite") | |
| passage = store.upsert( | |
| id="custom-test-1", | |
| topic="custom", | |
| tags=["unit-test"], | |
| lang="en", | |
| text="Unit-test snippet describing arbitrary clinical content for retrieval.", | |
| citations=["WHO_SSI_2018"], | |
| ) | |
| assert store.get_by_id("custom-test-1") is not None | |
| rag = MedicalRAG(store) | |
| hits = rag.search("arbitrary clinical content", top_k=1, expand=False) | |
| assert hits and hits[0].passage.id == passage.id | |
| def test_explain_threshold_returns_context() -> None: | |
| rag = get_rag() | |
| ctx = rag.explain_threshold("ph", lang="en") | |
| assert ctx.hits, "expected at least one passage" | |
| assert ctx.citation_keys | |
| # --------------------------------------------------------------------------- | |
| # Explainable predictor | |
| # --------------------------------------------------------------------------- | |
| def test_predictor_marginal_attribution_signs() -> None: | |
| from core.ai_engine import get_clinical_model | |
| predictor = JarvisPredictor(get_clinical_model()) | |
| pred = predictor.predict( | |
| temperature_c=38.4, | |
| ph=8.0, | |
| humidity=88.0, | |
| velocity={"temp_delta_6h": 1.4, "ph_delta_6h": 0.7, "humidity_volatility_6h": 4.0}, | |
| factors=PatientFactors(age=70, diabetes=True, smoker=False), | |
| ) | |
| assert pred.base.severity in ("warning", "critical") | |
| top = pred.top_features(3) | |
| assert len(top) == 3 | |
| # At least one of the temperature / pH features should push the score up | |
| assert any(a.delta_score > 0 for a in top if a.feature in {"ph", "temperature_c"}) | |
| def test_predictor_returns_band_hits_in_order() -> None: | |
| from core.ai_engine import get_clinical_model | |
| predictor = JarvisPredictor(get_clinical_model()) | |
| pred = predictor.predict( | |
| temperature_c=34.0, | |
| ph=6.0, | |
| humidity=78.0, | |
| velocity={"temp_delta_6h": 0.0, "ph_delta_6h": 0.0, "humidity_volatility_6h": 1.0}, | |
| factors=PatientFactors(), | |
| ) | |
| params = [bh.parameter for bh in pred.band_hits] | |
| assert params == ["temperature_c", "ph", "humidity"] | |
| assert all(bh.band.severity == "stable" for bh in pred.band_hits) | |
| # --------------------------------------------------------------------------- | |
| # Recommendations | |
| # --------------------------------------------------------------------------- | |
| def test_recommendations_for_critical_include_culture() -> None: | |
| from core.ai_engine import get_clinical_model | |
| predictor = JarvisPredictor(get_clinical_model()) | |
| pred = predictor.predict( | |
| temperature_c=38.8, | |
| ph=8.2, | |
| humidity=90.0, | |
| velocity={"temp_delta_6h": 1.6, "ph_delta_6h": 0.8, "humidity_volatility_6h": 5.0}, | |
| factors=PatientFactors(age=72, diabetes=True, smoker=False), | |
| ) | |
| recs = build_recommendations(prediction=pred, bacterial_signature="Pseudomonas aeruginosa (suspected)") | |
| assert any("culture" in r.text_en.lower() for r in recs) | |
| assert any(r.tier == "immediate" for r in recs) | |
| assert any("piperacillin" in r.text_en.lower() or "pseudomonas" in r.text_en.lower() for r in recs) | |
| def test_recommendations_for_stable_are_empty_or_routine() -> None: | |
| from core.ai_engine import get_clinical_model | |
| predictor = JarvisPredictor(get_clinical_model()) | |
| pred = predictor.predict( | |
| temperature_c=33.8, | |
| ph=6.0, | |
| humidity=78.0, | |
| velocity={"temp_delta_6h": 0.0, "ph_delta_6h": 0.0, "humidity_volatility_6h": 0.5}, | |
| factors=PatientFactors(), | |
| ) | |
| recs = build_recommendations(prediction=pred) | |
| assert all(r.tier in {"monitoring", "prevention"} for r in recs) | |
| assert not any(r.tier == "immediate" for r in recs) | |
| # --------------------------------------------------------------------------- | |
| # Clinical brain | |
| # --------------------------------------------------------------------------- | |
| def test_clinical_note_languages(lang: str) -> None: | |
| from core.ai_engine import get_clinical_model | |
| predictor = JarvisPredictor(get_clinical_model()) | |
| pred = predictor.predict( | |
| temperature_c=38.0, | |
| ph=7.7, | |
| humidity=89.0, | |
| velocity={"temp_delta_6h": 1.0, "ph_delta_6h": 0.5, "humidity_volatility_6h": 4.0}, | |
| factors=PatientFactors(age=68, diabetes=True, smoker=False), | |
| ) | |
| recs = build_recommendations(prediction=pred, bacterial_signature="Staphylococcus aureus (suspected)") | |
| brain = get_brain() | |
| note = brain.generate_note( | |
| patient=PatientSummary( | |
| name="Test Patient", | |
| age=68, | |
| sex="F", | |
| surgery_type="colectomy", | |
| days_post_op=3, | |
| diabetes=True, | |
| smoker=False, | |
| ), | |
| prediction=pred, | |
| recommendations=recs, | |
| rag_context=None, | |
| bacterial_signature="Staphylococcus aureus (suspected)", | |
| signature_confidence=0.72, | |
| lang=lang, | |
| ) | |
| assert note.lang == lang | |
| assert note.headline | |
| assert len(note.body) > 200 | |
| assert note.citations | |
| assert note.summary_bullets | |
| # --------------------------------------------------------------------------- | |
| # Reasoner orchestration | |
| # --------------------------------------------------------------------------- | |
| def test_reasoner_full_pipeline_serialisable() -> None: | |
| reasoner = get_reasoner() | |
| inputs = ReasonerInputs( | |
| patient=PatientSummary( | |
| name="Maria Silva", | |
| age=66, | |
| sex="F", | |
| surgery_type="appendectomy", | |
| days_post_op=2, | |
| diabetes=True, | |
| smoker=False, | |
| ), | |
| temperature_c=37.9, | |
| ph=7.7, | |
| humidity=88.0, | |
| velocity={"temp_delta_6h": 1.2, "ph_delta_6h": 0.6, "humidity_volatility_6h": 4.5}, | |
| factors=PatientFactors(age=66, diabetes=True, smoker=False), | |
| bacterial_signature="Staphylococcus aureus (suspected)", | |
| signature_confidence=0.72, | |
| tissue_state="inflammatory", | |
| tissue_confidence=0.81, | |
| lang="en", | |
| ) | |
| analysis = reasoner.analyze(inputs) | |
| payload = analysis.to_dict() | |
| # All nested dataclasses serialise cleanly | |
| assert payload["prediction"]["severity"] in {"warning", "critical"} | |
| assert payload["clinical_note"]["body"] | |
| assert payload["recommendations"] | |
| assert payload["rag_context"]["passages"] | |
| # The serialised dict should be JSON-roundtrippable | |
| import json | |
| json.loads(json.dumps(payload)) | |
| # --------------------------------------------------------------------------- | |
| # Autonomous monitor | |
| # --------------------------------------------------------------------------- | |
| def _synthetic_history(values: list[tuple[float, float, float]], start: datetime | None = None): | |
| start = start or datetime.now(timezone.utc) - timedelta(hours=5) | |
| rows = [] | |
| for i, (t, p, h) in enumerate(values): | |
| rows.append( | |
| { | |
| "timestamp": start + timedelta(minutes=i * 5), | |
| "temperature_c": t, | |
| "ph": p, | |
| "humidity": h, | |
| } | |
| ) | |
| return rows | |
| def test_autonomous_monitor_flags_coupled_climb() -> None: | |
| monitor = get_monitor() | |
| values = [] | |
| for i in range(20): | |
| values.append((33.0 + i * 0.1, 6.5 + i * 0.05, 78.0)) | |
| report = monitor.analyze(_synthetic_history(values), factors=PatientFactors()) | |
| assert report.findings, "expected at least one finding" | |
| kinds = {f.kind for f in report.findings} | |
| assert "coupled_climb" in kinds | |
| def test_autonomous_monitor_stable_baseline_quiet() -> None: | |
| monitor = AutonomousMonitor() | |
| values = [(34.0, 6.2, 80.0)] * 30 | |
| report = monitor.analyze(_synthetic_history(values), factors=PatientFactors()) | |
| assert report.composite_score < 0.2 | |
| assert report.severity_hint == "stable" | |
| def test_autonomous_monitor_detects_humidity_drift() -> None: | |
| monitor = AutonomousMonitor() | |
| values = [] | |
| for i in range(24): | |
| values.append((34.0, 6.2, 75.0 + i * 0.7)) | |
| report = monitor.analyze(_synthetic_history(values), factors=PatientFactors()) | |
| kinds = {f.kind for f in report.findings} | |
| assert "humidity_drift" in kinds | |