Spaces:
Paused
Paused
Download backend/evidence/fusion.py from Sahil-nith/SatQuery-api: direct link, hf CLI and curl.
- Browser
- Download file 5.8 kB
-
https://huggingface.co/spaces/Sahil-nith/SatQuery-api/resolve/main/backend/evidence/fusion.py
- Command line
-
hf download hf://spaces/Sahil-nith/SatQuery-api/backend/evidence/fusion.py
-
curl -L -o fusion.py https://huggingface.co/spaces/Sahil-nith/SatQuery-api/resolve/main/backend/evidence/fusion.py
5.8 kB
| """ | |
| CONFLICTING evidence state. | |
| Phase 12. Named in the master brief Β§4B as one of four Evidence | |
| Sufficiency Engine outcomes (SUFFICIENT / INSUFFICIENT / CONFLICTING / | |
| UNSUPPORTED) but never implemented through Phase 11 β PHASE11.md Β§5 | |
| flagged it explicitly as deferred, not forgotten. | |
| SCOPE β what this deliberately is and is not: | |
| This is NOT a new model, and it does NOT invent a second opinion where | |
| only one signal exists. It cross-checks two signals SatQuery ALREADY | |
| computes independently for the same request: | |
| 1. change_vqa: the deterministic pixel-diff change_map (magnitude: | |
| high/medium/low) vs. the free-text answer from the composite-image | |
| VQA bridge (models/change.py's _ChangeVQABridge). These are already | |
| shown side-by-side in the API response (see PHASE7 notes) but were | |
| never checked against each other for agreement. | |
| 2. optical_sar: the fused agreement_pct vs. how much of the target | |
| each modality flags independently (optical_only_pct / sar_only_pct). | |
| If the two modalities are pointing at substantially different areas | |
| rather than overlapping, that is a real cross-modal disagreement, | |
| not a fusion result. | |
| Both checks are plain arithmetic on numbers/strings SatQuery already | |
| has β no external verification model, no fabricated second confidence | |
| score. If a judge asks "what verifies the verification", the honest | |
| answer is: these are two views of the same deterministic pipeline | |
| outputs, not two independent models. Real multi-model verification | |
| (master brief Β§13B) is a larger, separate feature β not built here. | |
| """ | |
| from __future__ import annotations | |
| from dataclasses import dataclass | |
| # Words that, if present in a change-VQA free-text answer, assert that | |
| # something DID change. Deliberately small and literal β this is a | |
| # keyword heuristic, not an NLU classifier; it can be wrong on unusual | |
| # phrasing, which is fine because CONFLICTING is advisory, not blocking. | |
| _AFFIRMS_CHANGE = ( | |
| "increased", "decreased", "expanded", "reduced", "grew", "shrank", | |
| "new construction", "new built-up", "damage", "damaged", "flooded", | |
| "flooding", "cleared", "deforest", "significant change", "yes,", | |
| ) | |
| _DENIES_CHANGE = ( | |
| "no change", "no significant change", "unchanged", "remained the same", | |
| "no evidence of change", "not changed", "no visible change", "stable", | |
| ) | |
| # Pixel-diff change_level below which we call it "the heuristic saw | |
| # almost nothing" for conflict purposes. Matches models/change.py's own | |
| # _MEDIUM_CHANGE_PCT boundary so this stays consistent with the change | |
| # map's own high/medium/low labeling rather than inventing a new cutoff. | |
| _LOW_LEVEL = "low" | |
| _HIGH_LEVEL = "high" | |
| class ConflictResult: | |
| conflicting: bool | |
| reason: str | None # human-readable, goes straight into the trace/note | |
| def check_change_conflict(change_level: str, changed_pixel_pct: float, vqa_answer: str) -> ConflictResult: | |
| """Compare the deterministic pixel-diff change magnitude against the | |
| change-VQA free-text answer's implied direction. Only ever flags a | |
| conflict on a clear mismatch β an ambiguous/unmatched answer is left | |
| alone rather than guessed at.""" | |
| answer_l = vqa_answer.lower() | |
| affirms = any(k in answer_l for k in _AFFIRMS_CHANGE) | |
| denies = any(k in answer_l for k in _DENIES_CHANGE) | |
| if affirms and denies: | |
| # Answer hedges both ways ("no major change, but some new | |
| # structures") β not a clean signal either way, don't flag. | |
| return ConflictResult(False, None) | |
| if change_level == _LOW_LEVEL and affirms: | |
| return ConflictResult( | |
| True, | |
| f"pixel-diff heuristic measured only {changed_pixel_pct}% of pixels changed (LOW), " | |
| "but the composite-VQA answer describes a specific change having occurred β " | |
| "the two signals disagree on whether meaningful change happened.", | |
| ) | |
| if change_level == _HIGH_LEVEL and denies: | |
| return ConflictResult( | |
| True, | |
| f"pixel-diff heuristic measured {changed_pixel_pct}% of pixels changed (HIGH), " | |
| "but the composite-VQA answer states no meaningful change occurred β " | |
| "the two signals disagree on whether meaningful change happened.", | |
| ) | |
| return ConflictResult(False, None) | |
| def check_optical_sar_conflict( | |
| agreement_pct: float, optical_only_pct: float, sar_only_pct: float | |
| ) -> ConflictResult: | |
| """Flag cross-modal disagreement when optical and SAR are each | |
| flagging a non-trivial, largely NON-overlapping area for the same | |
| target β i.e. the two sensors are pointing at different things, not | |
| confirming/complementing each other. A small disagreement is normal | |
| and expected (see models/optical_sar.py's own disclosure); this only | |
| fires when the disagreement dominates over the agreement.""" | |
| disagreement = optical_only_pct + sar_only_pct | |
| if disagreement < 1.0: | |
| # Too little signal from either modality to call this a real | |
| # conflict β more likely just an empty/near-empty scene. | |
| return ConflictResult(False, None) | |
| # Conflict when each modality's independent signal is at least ~3x | |
| # the overlap β i.e. agreement is a minority of what either sensor | |
| # flagged on its own. Prototype policy threshold, not validated. | |
| if agreement_pct * 3 < optical_only_pct and agreement_pct * 3 < sar_only_pct: | |
| return ConflictResult( | |
| True, | |
| f"optical flags {optical_only_pct}% and SAR flags {sar_only_pct}% independently, " | |
| f"but they only overlap on {agreement_pct}% β the two modalities are pointing at " | |
| "substantially different areas rather than confirming each other for this target.", | |
| ) | |
| return ConflictResult(False, None) | |