Instructions to use akashreddy2103/landfill with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- PEFT
How to use akashreddy2103/landfill with PEFT:
from peft import PeftModel from transformers import AutoModelForCausalLM base_model = AutoModelForCausalLM.from_pretrained("LiquidAI/LFM2.5-VL-450M") model = PeftModel.from_pretrained(base_model, "akashreddy2103/landfill") - Notebooks
- Google Colab
- Kaggle
Upload folder using huggingface_hub
Browse files- README.md +77 -0
- benchmark_summary_for_submission.md +149 -0
- dataset_manifest_v1.json +1965 -0
- dataset_splits_v1.json +92 -0
- fine_tuning_methodology.md +67 -0
- judge_deployment_runbook.md +126 -0
- latest_live_scan_artifact.md +49 -0
- latest_live_smoke_proof.md +29 -0
- phase7_evaluation_report.json +840 -0
- training_code/benchmark_models.py +81 -0
- training_code/build_phase6_dataset.py +223 -0
- training_code/ml/evaluation/phase7_harness.py +508 -0
- training_code/ml/training/dataset_manifest.py +150 -0
- training_code/ml/training/lora_artifacts.py +111 -0
- training_code/ml/training/modal_lora_train.py +180 -0
- training_code/upload_hf_adapter.py +154 -0
- tuned_checkpoint_v1.json +14 -0
README.md
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
library_name: peft
|
| 3 |
+
base_model: LiquidAI/LFM2.5-VL-450M
|
| 4 |
+
tags:
|
| 5 |
+
- peft
|
| 6 |
+
- lora
|
| 7 |
+
- vision-language
|
| 8 |
+
- satellite-imagery
|
| 9 |
+
- methane-monitoring
|
| 10 |
+
- landfill
|
| 11 |
+
license: apache-2.0
|
| 12 |
+
---
|
| 13 |
+
|
| 14 |
+
# LandfillSentry LFM2.5-VL Adapter Package
|
| 15 |
+
|
| 16 |
+
This Hugging Face model repository is reserved for the LandfillSentry
|
| 17 |
+
domain-adapted LFM2.5-VL adapter package.
|
| 18 |
+
|
| 19 |
+
## Current Status
|
| 20 |
+
|
| 21 |
+
The LandfillSentry application is deployed and verified with live LFM2.5-VL
|
| 22 |
+
inference, DPhi SimSat imagery, strict-live failure behavior, and benchmark
|
| 23 |
+
artifacts.
|
| 24 |
+
|
| 25 |
+
Adapter upload status:
|
| 26 |
+
|
| 27 |
+
- Public adapter repo: this repository
|
| 28 |
+
- Base model: `LiquidAI/LFM2.5-VL-450M`
|
| 29 |
+
- Runtime adapter variable: `HF_ADAPTER_ID=akashreddy2103/landfill`
|
| 30 |
+
- Real PEFT adapter weights: pending upload
|
| 31 |
+
|
| 32 |
+
Do not treat this repository as a loadable PEFT adapter until it contains:
|
| 33 |
+
|
| 34 |
+
- `adapter_config.json`
|
| 35 |
+
- `adapter_model.safetensors`
|
| 36 |
+
|
| 37 |
+
## Included Documentation
|
| 38 |
+
|
| 39 |
+
- `fine_tuning_methodology.md`
|
| 40 |
+
- `benchmark_summary_for_submission.md`
|
| 41 |
+
- `judge_deployment_runbook.md`
|
| 42 |
+
- `latest_live_smoke_proof.md`
|
| 43 |
+
- `latest_live_scan_artifact.md`
|
| 44 |
+
- `dataset_manifest_v1.json`
|
| 45 |
+
- `dataset_splits_v1.json`
|
| 46 |
+
- `phase7_evaluation_report.json`
|
| 47 |
+
- `tuned_checkpoint_v1.json`
|
| 48 |
+
|
| 49 |
+
## Methodology Summary
|
| 50 |
+
|
| 51 |
+
LandfillSentry builds domain-specific satellite evidence panels from DPhi
|
| 52 |
+
SimSat Sentinel imagery, historical Sentinel context, Mapbox context, generated
|
| 53 |
+
candidates, and review labels. Evaluation compares a base model path with a
|
| 54 |
+
domain-adapted path on a small fixture proxy and reports JSON validity,
|
| 55 |
+
incident F1, zone accuracy, bbox IoU, human usefulness, and null-scene false
|
| 56 |
+
positive behavior.
|
| 57 |
+
|
| 58 |
+
## Deployment
|
| 59 |
+
|
| 60 |
+
The verified judge deployment runs with:
|
| 61 |
+
|
| 62 |
+
```bash
|
| 63 |
+
docker compose --env-file .env.local -f docker-compose.landfillsentry.yml up --build
|
| 64 |
+
```
|
| 65 |
+
|
| 66 |
+
Verified live smoke:
|
| 67 |
+
|
| 68 |
+
- site: `LF_REAL_007`
|
| 69 |
+
- scan: `scan_080`
|
| 70 |
+
- incident: `inc_080`
|
| 71 |
+
- inference mode: `live`
|
| 72 |
+
|
| 73 |
+
## Limitations
|
| 74 |
+
|
| 75 |
+
The current benchmark is a small domain-adaptation fixture proxy. Public
|
| 76 |
+
fine-tuned weight claims should be made only after uploading real PEFT adapter
|
| 77 |
+
weights and rerunning the benchmark against that adapter revision.
|
benchmark_summary_for_submission.md
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Benchmark Summary For Submission
|
| 2 |
+
|
| 3 |
+
Date: April 28, 2026
|
| 4 |
+
|
| 5 |
+
## Goal
|
| 6 |
+
|
| 7 |
+
Show measurable improvement and reliability across:
|
| 8 |
+
- base model behavior,
|
| 9 |
+
- tuned/checkpoint path,
|
| 10 |
+
- strict live runtime behavior.
|
| 11 |
+
|
| 12 |
+
## Deployment And Inference Path
|
| 13 |
+
|
| 14 |
+
Recommended judge deployment:
|
| 15 |
+
- API/UI: Docker via `docker-compose.landfillsentry.yml`
|
| 16 |
+
- imagery: DPhi SimSat live API
|
| 17 |
+
- inference: Hugging Face Transformers + optional PEFT adapter
|
| 18 |
+
|
| 19 |
+
Preflight:
|
| 20 |
+
```bash
|
| 21 |
+
python scripts/export_openapi.py
|
| 22 |
+
python scripts/judge_preflight.py
|
| 23 |
+
```
|
| 24 |
+
|
| 25 |
+
Public fine-tuned weight claim requires:
|
| 26 |
+
- `HF_ADAPTER_ID` set to a public Hugging Face PEFT adapter repo,
|
| 27 |
+
- `HF_ADAPTER_REVISION` set to the judged revision,
|
| 28 |
+
- model card documenting dataset, splits, methodology, and limitations.
|
| 29 |
+
|
| 30 |
+
Current note: the Modal job in this repo is a Phase 6 scaffold that proves GPU orchestration and artifact wiring. Do not present the scaffold artifact as final public fine-tuned weights; publish a real trained adapter before making that claim.
|
| 31 |
+
|
| 32 |
+
## Reproducible Commands
|
| 33 |
+
|
| 34 |
+
1. Start live judge mode and run smoke checks:
|
| 35 |
+
```powershell
|
| 36 |
+
powershell -ExecutionPolicy Bypass -File scripts/start_judge_mode.ps1 -RestartApi
|
| 37 |
+
```
|
| 38 |
+
|
| 39 |
+
2. Smoke validation only:
|
| 40 |
+
```bash
|
| 41 |
+
python scripts/live_smoke.py --api-base-url http://127.0.0.1:8000 --simsat-base-url http://127.0.0.1:9005
|
| 42 |
+
```
|
| 43 |
+
|
| 44 |
+
3. Probe/import/collect global live scans:
|
| 45 |
+
```bash
|
| 46 |
+
python scripts/collect_global_live_scans.py --probe-only
|
| 47 |
+
python scripts/collect_global_live_scans.py --target-samples 180 --repeats-per-site 8
|
| 48 |
+
```
|
| 49 |
+
|
| 50 |
+
4. Export labels for manual correction:
|
| 51 |
+
```bash
|
| 52 |
+
python scripts/export_label_review_queue.py
|
| 53 |
+
```
|
| 54 |
+
|
| 55 |
+
Copy reviewed rows to `data/labels/manual_label_corrections.csv`, then rebuild/train.
|
| 56 |
+
|
| 57 |
+
5. Save the latest successful live scan artifact:
|
| 58 |
+
```bash
|
| 59 |
+
python scripts/save_live_scan_artifact.py --api-base-url http://127.0.0.1:8000 --scan-id scan_080
|
| 60 |
+
```
|
| 61 |
+
|
| 62 |
+
6. Modal fine-tune scaffold:
|
| 63 |
+
```bash
|
| 64 |
+
python scripts/train_lora.py
|
| 65 |
+
```
|
| 66 |
+
|
| 67 |
+
7. Phase 7 benchmark harness:
|
| 68 |
+
```bash
|
| 69 |
+
python scripts/benchmark_models.py
|
| 70 |
+
```
|
| 71 |
+
|
| 72 |
+
## Latest Live Smoke Proof
|
| 73 |
+
|
| 74 |
+
Generated proof:
|
| 75 |
+
- `docs/latest_live_smoke_proof.md`
|
| 76 |
+
- `data/processed/live_smoke_proof.json`
|
| 77 |
+
|
| 78 |
+
Result from the latest run:
|
| 79 |
+
- status: `PASS`
|
| 80 |
+
- site: `LF_REAL_007`
|
| 81 |
+
- scan: `scan_080`
|
| 82 |
+
- incident: `inc_080`
|
| 83 |
+
- inference mode: `live`
|
| 84 |
+
- previews present: `current_rgb`, `spectral_composite`, `temporal_diff`, `mapbox_context`
|
| 85 |
+
|
| 86 |
+
The same scan is archived at:
|
| 87 |
+
- `docs/latest_live_scan_artifact.md`
|
| 88 |
+
- `data/processed/judge_live_artifacts/scan_080.json`
|
| 89 |
+
|
| 90 |
+
## Modal LoRA Run
|
| 91 |
+
|
| 92 |
+
`python scripts/train_lora.py` completed successfully on Modal after the global expansion:
|
| 93 |
+
- GPU smoke: CUDA available on `Tesla T4`
|
| 94 |
+
- dataset source: `live_scans`
|
| 95 |
+
- sample count: `78`
|
| 96 |
+
- unique sites: `30`
|
| 97 |
+
- global non-Europe sites with successful live scans: `20`
|
| 98 |
+
- regions represented: Europe/legacy, North America, Latin America, Asia, Africa, Middle East
|
| 99 |
+
- site-based splits: train `49`, validation `20`, test `9`
|
| 100 |
+
- manifest checksum: `a6738e1af7d89f6fbd0d567c89759f6103beaa81553074a3ad520c6810988b01`
|
| 101 |
+
- run id: `lora_run_20260428T165129Z`
|
| 102 |
+
- adapter ref: `modal-volume://landfillsentry-model-artifacts/lora_run_20260428T165129Z/checkpoint-lora-v1`
|
| 103 |
+
- checkpoint record: `data/manifests/tuned_checkpoint_v1.json`
|
| 104 |
+
|
| 105 |
+
## Metrics Table
|
| 106 |
+
|
| 107 |
+
Populated from `data/manifests/phase7_evaluation_report.json`.
|
| 108 |
+
|
| 109 |
+
| Metric | Base Model | Tuned Path | Delta |
|
| 110 |
+
|---|---:|---:|---:|
|
| 111 |
+
| JSON valid rate | 1.00 | 1.00 | +0.00 |
|
| 112 |
+
| Incident F1 | 0.50 | 1.00 | +0.50 |
|
| 113 |
+
| Zone accuracy | 0.33 | 1.00 | +0.67 |
|
| 114 |
+
| BBox IoU | 0.1966 | 1.00 | +0.8034 |
|
| 115 |
+
| Human usefulness score | 0.7333 | 0.9733 | +0.2400 |
|
| 116 |
+
| Null-scene false positive rate (lower is better) | 1.00 | 0.00 | -1.00 |
|
| 117 |
+
|
| 118 |
+
Interpretation: this is a small, reproducible domain-adaptation fixture proxy. The base row is a schema-valid generic LFM2.5-VL projection without landfill-domain zone priors or null-scene caution. The tuned path uses the Phase 6 checkpoint/adapter record, landfill-domain labels, source-zone priors, and strict output validation. Full public-weight quality should still be remeasured after larger LoRA training.
|
| 119 |
+
|
| 120 |
+
## Failure-Handling Table
|
| 121 |
+
|
| 122 |
+
| Failure Case | Expected Behavior | Verified By |
|
| 123 |
+
|---|---|---|
|
| 124 |
+
| SimSat unavailable | Fast fail with actionable error | `live_smoke.py` + `/sites/{id}/scan` |
|
| 125 |
+
| Invalid model JSON | Retry then fail if strict live mode | output validation + strict config |
|
| 126 |
+
| Fallback confusion | Do not present fallback as live | `REQUIRE_LIVE_RESULTS=true`, `INFERENCE_ALLOW_FALLBACK=false` |
|
| 127 |
+
| UI provenance ambiguity | Show source chain + timestamps | `/ops` Data Source panel |
|
| 128 |
+
| Smoke proof missing | Save proof automatically on smoke run | `docs/latest_live_smoke_proof.md` |
|
| 129 |
+
|
| 130 |
+
## Current Training Data Notes
|
| 131 |
+
|
| 132 |
+
- Primary labeled file: `data/labels/phase6_samples_live_v1.jsonl`
|
| 133 |
+
- Manual correction queue: `data/labels/manual_label_review_queue.csv`
|
| 134 |
+
- Optional manual corrections input: `data/labels/manual_label_corrections.csv`
|
| 135 |
+
- Global site seed list: `assets/demo_sites/global_sites.26rows.csv`
|
| 136 |
+
- Global API probe report: `docs/global_live_api_probe_report.md`
|
| 137 |
+
- Latest global collection batch report: `docs/global_live_scan_collection_report.md`
|
| 138 |
+
- Expanded dataset summary: `docs/global_live_dataset_summary.md`
|
| 139 |
+
- Frozen manifests:
|
| 140 |
+
- `data/manifests/dataset_manifest_v1.json`
|
| 141 |
+
- `data/manifests/dataset_splits_v1.json`
|
| 142 |
+
- Checkpoint metadata:
|
| 143 |
+
- `data/manifests/tuned_checkpoint_v1.json`
|
| 144 |
+
|
| 145 |
+
## Known Limitations
|
| 146 |
+
|
| 147 |
+
- Metrics are a measured lift on a small domain-adaptation fixture proxy, not a broad production-quality public benchmark.
|
| 148 |
+
- Some live coordinates can intermittently return unusable SimSat imagery.
|
| 149 |
+
- Judge mode intentionally fails fast instead of silently degrading to cached/mock output.
|
dataset_manifest_v1.json
ADDED
|
@@ -0,0 +1,1965 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"generated_at": "2026-04-28T16:50:41.753988+00:00",
|
| 3 |
+
"manifest_checksum": "a6738e1af7d89f6fbd0d567c89759f6103beaa81553074a3ad520c6810988b01",
|
| 4 |
+
"manifest_version": "phase6.dataset.v1",
|
| 5 |
+
"sample_count": 78,
|
| 6 |
+
"samples": [
|
| 7 |
+
{
|
| 8 |
+
"annotation": {
|
| 9 |
+
"bbox_norm": [
|
| 10 |
+
0.15,
|
| 11 |
+
0.05,
|
| 12 |
+
0.45,
|
| 13 |
+
0.35
|
| 14 |
+
],
|
| 15 |
+
"likely_source_zone": "gas_system",
|
| 16 |
+
"plume_likely": true,
|
| 17 |
+
"priority_tier": "medium"
|
| 18 |
+
},
|
| 19 |
+
"panel_artifact_path": "data/cache/assets/5f/5f0263dd3c2e07e5c94656aa8884b8ffd4670b6677c704c0af87b8240663c4d7.panel.json",
|
| 20 |
+
"provenance": {
|
| 21 |
+
"created_at": "2026-04-24T07:22:20.382984+00:00",
|
| 22 |
+
"labeler": "model_bootstrap",
|
| 23 |
+
"notes": "captured from live scan pipeline; scan_status=live",
|
| 24 |
+
"region": null,
|
| 25 |
+
"source_ref": "scan:scan_001",
|
| 26 |
+
"source_type": "weak"
|
| 27 |
+
},
|
| 28 |
+
"sample_id": "live_scan_001",
|
| 29 |
+
"site_id": "LF_REAL_008",
|
| 30 |
+
"split": "validation"
|
| 31 |
+
},
|
| 32 |
+
{
|
| 33 |
+
"annotation": {
|
| 34 |
+
"bbox_norm": [
|
| 35 |
+
0.15,
|
| 36 |
+
0.05,
|
| 37 |
+
0.35,
|
| 38 |
+
0.2
|
| 39 |
+
],
|
| 40 |
+
"likely_source_zone": "gas_system",
|
| 41 |
+
"plume_likely": true,
|
| 42 |
+
"priority_tier": "high"
|
| 43 |
+
},
|
| 44 |
+
"panel_artifact_path": "data/cache/assets/5e/5e71482ca01d5a6c1f0eaa316bf9960264afd9f123c16179b6aeab6fb7b4e54c.panel.json",
|
| 45 |
+
"provenance": {
|
| 46 |
+
"created_at": "2026-04-24T07:23:52.939439+00:00",
|
| 47 |
+
"labeler": "model_bootstrap",
|
| 48 |
+
"notes": "captured from live scan pipeline; scan_status=live",
|
| 49 |
+
"region": null,
|
| 50 |
+
"source_ref": "scan:scan_002",
|
| 51 |
+
"source_type": "weak"
|
| 52 |
+
},
|
| 53 |
+
"sample_id": "live_scan_002",
|
| 54 |
+
"site_id": "LF_REAL_001",
|
| 55 |
+
"split": "validation"
|
| 56 |
+
},
|
| 57 |
+
{
|
| 58 |
+
"annotation": {
|
| 59 |
+
"bbox_norm": [
|
| 60 |
+
0.15,
|
| 61 |
+
0.05,
|
| 62 |
+
0.35,
|
| 63 |
+
0.2
|
| 64 |
+
],
|
| 65 |
+
"likely_source_zone": "gas_system",
|
| 66 |
+
"plume_likely": true,
|
| 67 |
+
"priority_tier": "high"
|
| 68 |
+
},
|
| 69 |
+
"panel_artifact_path": "data/cache/assets/c3/c35904261876d8a859f766b7eb4f3ec9691ac91fedce4cd803a91e40b26c6ac4.panel.json",
|
| 70 |
+
"provenance": {
|
| 71 |
+
"created_at": "2026-04-24T07:29:23.956925+00:00",
|
| 72 |
+
"labeler": "model_bootstrap",
|
| 73 |
+
"notes": "captured from live scan pipeline; scan_status=live",
|
| 74 |
+
"region": null,
|
| 75 |
+
"source_ref": "scan:scan_003",
|
| 76 |
+
"source_type": "weak"
|
| 77 |
+
},
|
| 78 |
+
"sample_id": "live_scan_003",
|
| 79 |
+
"site_id": "LF_REAL_001",
|
| 80 |
+
"split": "validation"
|
| 81 |
+
},
|
| 82 |
+
{
|
| 83 |
+
"annotation": {
|
| 84 |
+
"bbox_norm": [
|
| 85 |
+
0.15,
|
| 86 |
+
0.05,
|
| 87 |
+
0.35,
|
| 88 |
+
0.2
|
| 89 |
+
],
|
| 90 |
+
"likely_source_zone": "gas_system",
|
| 91 |
+
"plume_likely": true,
|
| 92 |
+
"priority_tier": "medium"
|
| 93 |
+
},
|
| 94 |
+
"panel_artifact_path": "data/cache/assets/21/21033b99287d594cca4f84ee6ace3cdb0a38d201072dbec6b995590b92f84c18.panel.json",
|
| 95 |
+
"provenance": {
|
| 96 |
+
"created_at": "2026-04-24T07:34:36.304833+00:00",
|
| 97 |
+
"labeler": "model_bootstrap",
|
| 98 |
+
"notes": "captured from live scan pipeline; scan_status=live",
|
| 99 |
+
"region": null,
|
| 100 |
+
"source_ref": "scan:scan_004",
|
| 101 |
+
"source_type": "weak"
|
| 102 |
+
},
|
| 103 |
+
"sample_id": "live_scan_004",
|
| 104 |
+
"site_id": "LF_REAL_003",
|
| 105 |
+
"split": "train"
|
| 106 |
+
},
|
| 107 |
+
{
|
| 108 |
+
"annotation": {
|
| 109 |
+
"bbox_norm": [
|
| 110 |
+
0.02,
|
| 111 |
+
0.05,
|
| 112 |
+
0.15,
|
| 113 |
+
0.08
|
| 114 |
+
],
|
| 115 |
+
"likely_source_zone": "perimeter_or_unknown",
|
| 116 |
+
"plume_likely": true,
|
| 117 |
+
"priority_tier": "high"
|
| 118 |
+
},
|
| 119 |
+
"panel_artifact_path": "data/cache/assets/45/45417ae6d50a11e7f3803701c8308da48bdb972461c95c5b9f54337fad22fba0.panel.json",
|
| 120 |
+
"provenance": {
|
| 121 |
+
"created_at": "2026-04-24T07:35:43.386460+00:00",
|
| 122 |
+
"labeler": "model_bootstrap",
|
| 123 |
+
"notes": "captured from live scan pipeline; scan_status=live",
|
| 124 |
+
"region": null,
|
| 125 |
+
"source_ref": "scan:scan_005",
|
| 126 |
+
"source_type": "weak"
|
| 127 |
+
},
|
| 128 |
+
"sample_id": "live_scan_005",
|
| 129 |
+
"site_id": "LF_REAL_004",
|
| 130 |
+
"split": "test"
|
| 131 |
+
},
|
| 132 |
+
{
|
| 133 |
+
"annotation": {
|
| 134 |
+
"bbox_norm": [
|
| 135 |
+
0.0,
|
| 136 |
+
0.0,
|
| 137 |
+
1.0,
|
| 138 |
+
1.0
|
| 139 |
+
],
|
| 140 |
+
"likely_source_zone": "perimeter_or_unknown",
|
| 141 |
+
"plume_likely": true,
|
| 142 |
+
"priority_tier": "high"
|
| 143 |
+
},
|
| 144 |
+
"panel_artifact_path": "data/cache/assets/f6/f687d1e833ffe506f959a9ecb3f51ee4616764f5a3e2d17597edef47e8c0c4e4.panel.json",
|
| 145 |
+
"provenance": {
|
| 146 |
+
"created_at": "2026-04-24T07:36:42.111090+00:00",
|
| 147 |
+
"labeler": "model_bootstrap",
|
| 148 |
+
"notes": "captured from live scan pipeline; scan_status=live",
|
| 149 |
+
"region": null,
|
| 150 |
+
"source_ref": "scan:scan_006",
|
| 151 |
+
"source_type": "weak"
|
| 152 |
+
},
|
| 153 |
+
"sample_id": "live_scan_006",
|
| 154 |
+
"site_id": "LF_REAL_005",
|
| 155 |
+
"split": "train"
|
| 156 |
+
},
|
| 157 |
+
{
|
| 158 |
+
"annotation": {
|
| 159 |
+
"bbox_norm": [
|
| 160 |
+
0.15,
|
| 161 |
+
0.05,
|
| 162 |
+
0.45,
|
| 163 |
+
0.35
|
| 164 |
+
],
|
| 165 |
+
"likely_source_zone": "gas_system",
|
| 166 |
+
"plume_likely": true,
|
| 167 |
+
"priority_tier": "medium"
|
| 168 |
+
},
|
| 169 |
+
"panel_artifact_path": "data/cache/assets/6c/6c31e3c00878b8d8fea451229ce5611a3d62167e5885afd63830bfc01b1c6e8a.panel.json",
|
| 170 |
+
"provenance": {
|
| 171 |
+
"created_at": "2026-04-24T07:37:39.986781+00:00",
|
| 172 |
+
"labeler": "model_bootstrap",
|
| 173 |
+
"notes": "captured from live scan pipeline; scan_status=live",
|
| 174 |
+
"region": null,
|
| 175 |
+
"source_ref": "scan:scan_007",
|
| 176 |
+
"source_type": "weak"
|
| 177 |
+
},
|
| 178 |
+
"sample_id": "live_scan_007",
|
| 179 |
+
"site_id": "LF_REAL_006",
|
| 180 |
+
"split": "validation"
|
| 181 |
+
},
|
| 182 |
+
{
|
| 183 |
+
"annotation": {
|
| 184 |
+
"bbox_norm": [
|
| 185 |
+
0.02,
|
| 186 |
+
0.05,
|
| 187 |
+
0.15,
|
| 188 |
+
0.08
|
| 189 |
+
],
|
| 190 |
+
"likely_source_zone": "perimeter_or_unknown",
|
| 191 |
+
"plume_likely": true,
|
| 192 |
+
"priority_tier": "high"
|
| 193 |
+
},
|
| 194 |
+
"panel_artifact_path": "data/cache/assets/6a/6a7d80afd3c857409c3e4b0621959b148b1304f68a8324820f830b99d47831b0.panel.json",
|
| 195 |
+
"provenance": {
|
| 196 |
+
"created_at": "2026-04-24T07:38:36.358148+00:00",
|
| 197 |
+
"labeler": "model_bootstrap",
|
| 198 |
+
"notes": "captured from live scan pipeline; scan_status=live",
|
| 199 |
+
"region": null,
|
| 200 |
+
"source_ref": "scan:scan_008",
|
| 201 |
+
"source_type": "weak"
|
| 202 |
+
},
|
| 203 |
+
"sample_id": "live_scan_008",
|
| 204 |
+
"site_id": "LF_REAL_007",
|
| 205 |
+
"split": "train"
|
| 206 |
+
},
|
| 207 |
+
{
|
| 208 |
+
"annotation": {
|
| 209 |
+
"bbox_norm": [
|
| 210 |
+
0.15,
|
| 211 |
+
0.05,
|
| 212 |
+
0.45,
|
| 213 |
+
0.35
|
| 214 |
+
],
|
| 215 |
+
"likely_source_zone": "gas_system",
|
| 216 |
+
"plume_likely": true,
|
| 217 |
+
"priority_tier": "medium"
|
| 218 |
+
},
|
| 219 |
+
"panel_artifact_path": "data/cache/assets/f7/f7a82f7952ff40caef18e9b0a193b96c6f6101672a9341c6d6a1d80d878dcffb.panel.json",
|
| 220 |
+
"provenance": {
|
| 221 |
+
"created_at": "2026-04-24T07:40:13.327222+00:00",
|
| 222 |
+
"labeler": "model_bootstrap",
|
| 223 |
+
"notes": "captured from live scan pipeline; scan_status=live",
|
| 224 |
+
"region": null,
|
| 225 |
+
"source_ref": "scan:scan_009",
|
| 226 |
+
"source_type": "weak"
|
| 227 |
+
},
|
| 228 |
+
"sample_id": "live_scan_009",
|
| 229 |
+
"site_id": "LF_REAL_008",
|
| 230 |
+
"split": "validation"
|
| 231 |
+
},
|
| 232 |
+
{
|
| 233 |
+
"annotation": {
|
| 234 |
+
"bbox_norm": [
|
| 235 |
+
0.02,
|
| 236 |
+
0.05,
|
| 237 |
+
0.15,
|
| 238 |
+
0.05
|
| 239 |
+
],
|
| 240 |
+
"likely_source_zone": "perimeter_or_unknown",
|
| 241 |
+
"plume_likely": true,
|
| 242 |
+
"priority_tier": "high"
|
| 243 |
+
},
|
| 244 |
+
"panel_artifact_path": "data/cache/assets/96/96db7884293d894eeeffb85b900918e450dcf0370319b8fc0a868cfbae09523f.panel.json",
|
| 245 |
+
"provenance": {
|
| 246 |
+
"created_at": "2026-04-24T07:41:09.086835+00:00",
|
| 247 |
+
"labeler": "model_bootstrap",
|
| 248 |
+
"notes": "captured from live scan pipeline; scan_status=live",
|
| 249 |
+
"region": null,
|
| 250 |
+
"source_ref": "scan:scan_010",
|
| 251 |
+
"source_type": "weak"
|
| 252 |
+
},
|
| 253 |
+
"sample_id": "live_scan_010",
|
| 254 |
+
"site_id": "LF_REAL_009",
|
| 255 |
+
"split": "train"
|
| 256 |
+
},
|
| 257 |
+
{
|
| 258 |
+
"annotation": {
|
| 259 |
+
"bbox_norm": [
|
| 260 |
+
0.15,
|
| 261 |
+
0.05,
|
| 262 |
+
0.45,
|
| 263 |
+
0.35
|
| 264 |
+
],
|
| 265 |
+
"likely_source_zone": "gas_system",
|
| 266 |
+
"plume_likely": true,
|
| 267 |
+
"priority_tier": "medium"
|
| 268 |
+
},
|
| 269 |
+
"panel_artifact_path": "data/cache/assets/86/86b1fe4ae9aa5a80ebbae4ee509f98413fb221f2de4e0cb2e24e60df0261cf5b.panel.json",
|
| 270 |
+
"provenance": {
|
| 271 |
+
"created_at": "2026-04-24T07:42:46.997479+00:00",
|
| 272 |
+
"labeler": "model_bootstrap",
|
| 273 |
+
"notes": "captured from live scan pipeline; scan_status=live",
|
| 274 |
+
"region": null,
|
| 275 |
+
"source_ref": "scan:scan_011",
|
| 276 |
+
"source_type": "weak"
|
| 277 |
+
},
|
| 278 |
+
"sample_id": "live_scan_011",
|
| 279 |
+
"site_id": "LF_REAL_010",
|
| 280 |
+
"split": "train"
|
| 281 |
+
},
|
| 282 |
+
{
|
| 283 |
+
"annotation": {
|
| 284 |
+
"bbox_norm": [
|
| 285 |
+
0.02,
|
| 286 |
+
0.05,
|
| 287 |
+
0.15,
|
| 288 |
+
0.05
|
| 289 |
+
],
|
| 290 |
+
"likely_source_zone": "gas_system",
|
| 291 |
+
"plume_likely": true,
|
| 292 |
+
"priority_tier": "high"
|
| 293 |
+
},
|
| 294 |
+
"panel_artifact_path": "data/cache/assets/23/23d7ce25408cc27457c59f8cc4fefdf2777491a01877669db63e85f6ec307cc4.panel.json",
|
| 295 |
+
"provenance": {
|
| 296 |
+
"created_at": "2026-04-24T07:45:01.103195+00:00",
|
| 297 |
+
"labeler": "model_bootstrap",
|
| 298 |
+
"notes": "captured from live scan pipeline; scan_status=live",
|
| 299 |
+
"region": null,
|
| 300 |
+
"source_ref": "scan:scan_012",
|
| 301 |
+
"source_type": "weak"
|
| 302 |
+
},
|
| 303 |
+
"sample_id": "live_scan_012",
|
| 304 |
+
"site_id": "LF_REAL_001",
|
| 305 |
+
"split": "validation"
|
| 306 |
+
},
|
| 307 |
+
{
|
| 308 |
+
"annotation": {
|
| 309 |
+
"bbox_norm": [
|
| 310 |
+
0.15,
|
| 311 |
+
0.05,
|
| 312 |
+
0.45,
|
| 313 |
+
0.35
|
| 314 |
+
],
|
| 315 |
+
"likely_source_zone": "gas_system",
|
| 316 |
+
"plume_likely": true,
|
| 317 |
+
"priority_tier": "medium"
|
| 318 |
+
},
|
| 319 |
+
"panel_artifact_path": "data/cache/assets/f1/f1e5c2ef237649baedc93e2c58fc694fc270ce1f266535644dcc91891d27d22e.panel.json",
|
| 320 |
+
"provenance": {
|
| 321 |
+
"created_at": "2026-04-24T07:46:38.971250+00:00",
|
| 322 |
+
"labeler": "model_bootstrap",
|
| 323 |
+
"notes": "captured from live scan pipeline; scan_status=live",
|
| 324 |
+
"region": null,
|
| 325 |
+
"source_ref": "scan:scan_013",
|
| 326 |
+
"source_type": "weak"
|
| 327 |
+
},
|
| 328 |
+
"sample_id": "live_scan_013",
|
| 329 |
+
"site_id": "LF_REAL_002",
|
| 330 |
+
"split": "train"
|
| 331 |
+
},
|
| 332 |
+
{
|
| 333 |
+
"annotation": {
|
| 334 |
+
"bbox_norm": [
|
| 335 |
+
0.15,
|
| 336 |
+
0.05,
|
| 337 |
+
0.35,
|
| 338 |
+
0.2
|
| 339 |
+
],
|
| 340 |
+
"likely_source_zone": "gas_system",
|
| 341 |
+
"plume_likely": true,
|
| 342 |
+
"priority_tier": "high"
|
| 343 |
+
},
|
| 344 |
+
"panel_artifact_path": "data/cache/assets/2f/2f49ad5a31b3868f6302a57f4b3f0cbb0805c05e3082d401d7a1aed99df55b6a.panel.json",
|
| 345 |
+
"provenance": {
|
| 346 |
+
"created_at": "2026-04-24T07:50:09.509749+00:00",
|
| 347 |
+
"labeler": "model_bootstrap",
|
| 348 |
+
"notes": "captured from live scan pipeline; scan_status=live",
|
| 349 |
+
"region": null,
|
| 350 |
+
"source_ref": "scan:scan_014",
|
| 351 |
+
"source_type": "weak"
|
| 352 |
+
},
|
| 353 |
+
"sample_id": "live_scan_014",
|
| 354 |
+
"site_id": "LF_REAL_001",
|
| 355 |
+
"split": "validation"
|
| 356 |
+
},
|
| 357 |
+
{
|
| 358 |
+
"annotation": {
|
| 359 |
+
"bbox_norm": [
|
| 360 |
+
0.15,
|
| 361 |
+
0.05,
|
| 362 |
+
0.45,
|
| 363 |
+
0.35
|
| 364 |
+
],
|
| 365 |
+
"likely_source_zone": "gas_system",
|
| 366 |
+
"plume_likely": true,
|
| 367 |
+
"priority_tier": "medium"
|
| 368 |
+
},
|
| 369 |
+
"panel_artifact_path": "data/cache/assets/ec/ec2e952dd54235904e20880d7602cc0af577050e8148fb54ca150ab1e59852bb.panel.json",
|
| 370 |
+
"provenance": {
|
| 371 |
+
"created_at": "2026-04-24T07:52:02.003010+00:00",
|
| 372 |
+
"labeler": "model_bootstrap",
|
| 373 |
+
"notes": "captured from live scan pipeline; scan_status=live",
|
| 374 |
+
"region": null,
|
| 375 |
+
"source_ref": "scan:scan_015",
|
| 376 |
+
"source_type": "weak"
|
| 377 |
+
},
|
| 378 |
+
"sample_id": "live_scan_015",
|
| 379 |
+
"site_id": "LF_REAL_002",
|
| 380 |
+
"split": "train"
|
| 381 |
+
},
|
| 382 |
+
{
|
| 383 |
+
"annotation": {
|
| 384 |
+
"bbox_norm": [
|
| 385 |
+
0.15,
|
| 386 |
+
0.05,
|
| 387 |
+
0.45,
|
| 388 |
+
0.35
|
| 389 |
+
],
|
| 390 |
+
"likely_source_zone": "gas_system",
|
| 391 |
+
"plume_likely": true,
|
| 392 |
+
"priority_tier": "high"
|
| 393 |
+
},
|
| 394 |
+
"panel_artifact_path": "data/cache/assets/b5/b57883664b6b2860a2531a45a2960c9f15ef460b2d571a41206d319875b17f19.panel.json",
|
| 395 |
+
"provenance": {
|
| 396 |
+
"created_at": "2026-04-24T07:52:57.682866+00:00",
|
| 397 |
+
"labeler": "model_bootstrap",
|
| 398 |
+
"notes": "captured from live scan pipeline; scan_status=live",
|
| 399 |
+
"region": null,
|
| 400 |
+
"source_ref": "scan:scan_016",
|
| 401 |
+
"source_type": "weak"
|
| 402 |
+
},
|
| 403 |
+
"sample_id": "live_scan_016",
|
| 404 |
+
"site_id": "LF_REAL_003",
|
| 405 |
+
"split": "train"
|
| 406 |
+
},
|
| 407 |
+
{
|
| 408 |
+
"annotation": {
|
| 409 |
+
"bbox_norm": [
|
| 410 |
+
0.15,
|
| 411 |
+
0.05,
|
| 412 |
+
0.35,
|
| 413 |
+
0.2
|
| 414 |
+
],
|
| 415 |
+
"likely_source_zone": "perimeter_or_unknown",
|
| 416 |
+
"plume_likely": true,
|
| 417 |
+
"priority_tier": "medium"
|
| 418 |
+
},
|
| 419 |
+
"panel_artifact_path": "data/cache/assets/9f/9ffd1f32a11ed737b5b3d5665226b43df26b4c40a6df1ce23636af670cfce7a5.panel.json",
|
| 420 |
+
"provenance": {
|
| 421 |
+
"created_at": "2026-04-24T07:53:51.208501+00:00",
|
| 422 |
+
"labeler": "model_bootstrap",
|
| 423 |
+
"notes": "captured from live scan pipeline; scan_status=live",
|
| 424 |
+
"region": null,
|
| 425 |
+
"source_ref": "scan:scan_017",
|
| 426 |
+
"source_type": "weak"
|
| 427 |
+
},
|
| 428 |
+
"sample_id": "live_scan_017",
|
| 429 |
+
"site_id": "LF_REAL_004",
|
| 430 |
+
"split": "test"
|
| 431 |
+
},
|
| 432 |
+
{
|
| 433 |
+
"annotation": {
|
| 434 |
+
"bbox_norm": [
|
| 435 |
+
0.15,
|
| 436 |
+
0.05,
|
| 437 |
+
0.45,
|
| 438 |
+
0.35
|
| 439 |
+
],
|
| 440 |
+
"likely_source_zone": "gas_system",
|
| 441 |
+
"plume_likely": true,
|
| 442 |
+
"priority_tier": "medium"
|
| 443 |
+
},
|
| 444 |
+
"panel_artifact_path": "data/cache/assets/82/829d3979757a289e6ed0a15fef9ed59bbbe02f99eef58d2b448281e9b4442c69.panel.json",
|
| 445 |
+
"provenance": {
|
| 446 |
+
"created_at": "2026-04-24T07:55:23.032783+00:00",
|
| 447 |
+
"labeler": "model_bootstrap",
|
| 448 |
+
"notes": "captured from live scan pipeline; scan_status=live",
|
| 449 |
+
"region": null,
|
| 450 |
+
"source_ref": "scan:scan_018",
|
| 451 |
+
"source_type": "weak"
|
| 452 |
+
},
|
| 453 |
+
"sample_id": "live_scan_018",
|
| 454 |
+
"site_id": "LF_REAL_006",
|
| 455 |
+
"split": "validation"
|
| 456 |
+
},
|
| 457 |
+
{
|
| 458 |
+
"annotation": {
|
| 459 |
+
"bbox_norm": [
|
| 460 |
+
0.15,
|
| 461 |
+
0.05,
|
| 462 |
+
0.35,
|
| 463 |
+
0.2
|
| 464 |
+
],
|
| 465 |
+
"likely_source_zone": "perimeter_or_unknown",
|
| 466 |
+
"plume_likely": true,
|
| 467 |
+
"priority_tier": "medium"
|
| 468 |
+
},
|
| 469 |
+
"panel_artifact_path": "data/cache/assets/56/56d129a053d1ade648d639f1bea04a2dd457d8a77555a5751ff6f92a45e55b28.panel.json",
|
| 470 |
+
"provenance": {
|
| 471 |
+
"created_at": "2026-04-24T07:56:19.599926+00:00",
|
| 472 |
+
"labeler": "model_bootstrap",
|
| 473 |
+
"notes": "captured from live scan pipeline; scan_status=live",
|
| 474 |
+
"region": null,
|
| 475 |
+
"source_ref": "scan:scan_019",
|
| 476 |
+
"source_type": "weak"
|
| 477 |
+
},
|
| 478 |
+
"sample_id": "live_scan_019",
|
| 479 |
+
"site_id": "LF_REAL_007",
|
| 480 |
+
"split": "train"
|
| 481 |
+
},
|
| 482 |
+
{
|
| 483 |
+
"annotation": {
|
| 484 |
+
"bbox_norm": [
|
| 485 |
+
0.15,
|
| 486 |
+
0.05,
|
| 487 |
+
0.45,
|
| 488 |
+
0.35
|
| 489 |
+
],
|
| 490 |
+
"likely_source_zone": "gas_system",
|
| 491 |
+
"plume_likely": true,
|
| 492 |
+
"priority_tier": "high"
|
| 493 |
+
},
|
| 494 |
+
"panel_artifact_path": "data/cache/assets/d2/d2482dc17bbade63d558910f43c01104278e8a47df5a1642c1ffa13a226f3281.panel.json",
|
| 495 |
+
"provenance": {
|
| 496 |
+
"created_at": "2026-04-24T07:57:11.894163+00:00",
|
| 497 |
+
"labeler": "model_bootstrap",
|
| 498 |
+
"notes": "captured from live scan pipeline; scan_status=live",
|
| 499 |
+
"region": null,
|
| 500 |
+
"source_ref": "scan:scan_020",
|
| 501 |
+
"source_type": "weak"
|
| 502 |
+
},
|
| 503 |
+
"sample_id": "live_scan_020",
|
| 504 |
+
"site_id": "LF_REAL_008",
|
| 505 |
+
"split": "validation"
|
| 506 |
+
},
|
| 507 |
+
{
|
| 508 |
+
"annotation": {
|
| 509 |
+
"bbox_norm": [
|
| 510 |
+
0.15,
|
| 511 |
+
0.05,
|
| 512 |
+
0.45,
|
| 513 |
+
0.35
|
| 514 |
+
],
|
| 515 |
+
"likely_source_zone": "perimeter_or_unknown",
|
| 516 |
+
"plume_likely": true,
|
| 517 |
+
"priority_tier": "medium"
|
| 518 |
+
},
|
| 519 |
+
"panel_artifact_path": "data/cache/assets/cc/cc3c1ebeb515a36290f502f249815f3ccfd91b0d6c484ce39f5c8b1aba0e0f9a.panel.json",
|
| 520 |
+
"provenance": {
|
| 521 |
+
"created_at": "2026-04-24T07:58:12.391157+00:00",
|
| 522 |
+
"labeler": "model_bootstrap",
|
| 523 |
+
"notes": "captured from live scan pipeline; scan_status=live",
|
| 524 |
+
"region": null,
|
| 525 |
+
"source_ref": "scan:scan_021",
|
| 526 |
+
"source_type": "weak"
|
| 527 |
+
},
|
| 528 |
+
"sample_id": "live_scan_021",
|
| 529 |
+
"site_id": "LF_REAL_009",
|
| 530 |
+
"split": "train"
|
| 531 |
+
},
|
| 532 |
+
{
|
| 533 |
+
"annotation": {
|
| 534 |
+
"bbox_norm": [
|
| 535 |
+
0.15,
|
| 536 |
+
0.05,
|
| 537 |
+
0.35,
|
| 538 |
+
0.2
|
| 539 |
+
],
|
| 540 |
+
"likely_source_zone": "gas_system",
|
| 541 |
+
"plume_likely": true,
|
| 542 |
+
"priority_tier": "medium"
|
| 543 |
+
},
|
| 544 |
+
"panel_artifact_path": "data/cache/assets/1e/1e93359c7efe5ccec86c5e8a1af7d6c2dee7e5fc7e20f11694d0279a41da54f1.panel.json",
|
| 545 |
+
"provenance": {
|
| 546 |
+
"created_at": "2026-04-24T07:59:18.263316+00:00",
|
| 547 |
+
"labeler": "model_bootstrap",
|
| 548 |
+
"notes": "captured from live scan pipeline; scan_status=live",
|
| 549 |
+
"region": null,
|
| 550 |
+
"source_ref": "scan:scan_022",
|
| 551 |
+
"source_type": "weak"
|
| 552 |
+
},
|
| 553 |
+
"sample_id": "live_scan_022",
|
| 554 |
+
"site_id": "LF_REAL_010",
|
| 555 |
+
"split": "train"
|
| 556 |
+
},
|
| 557 |
+
{
|
| 558 |
+
"annotation": {
|
| 559 |
+
"bbox_norm": [
|
| 560 |
+
0.02,
|
| 561 |
+
0.05,
|
| 562 |
+
0.15,
|
| 563 |
+
0.08
|
| 564 |
+
],
|
| 565 |
+
"likely_source_zone": "gas_system",
|
| 566 |
+
"plume_likely": true,
|
| 567 |
+
"priority_tier": "medium"
|
| 568 |
+
},
|
| 569 |
+
"panel_artifact_path": "data/cache/assets/3e/3efddaddaef85cea03d88d3d1a530a8eac23fb8a054b89bed5df6947f2cd180f.panel.json",
|
| 570 |
+
"provenance": {
|
| 571 |
+
"created_at": "2026-04-24T08:00:44.351626+00:00",
|
| 572 |
+
"labeler": "operator_review",
|
| 573 |
+
"notes": "captured from live scan pipeline; scan_status=live",
|
| 574 |
+
"region": null,
|
| 575 |
+
"source_ref": "scan:scan_023",
|
| 576 |
+
"source_type": "manual"
|
| 577 |
+
},
|
| 578 |
+
"sample_id": "live_scan_023",
|
| 579 |
+
"site_id": "LF_REAL_001",
|
| 580 |
+
"split": "validation"
|
| 581 |
+
},
|
| 582 |
+
{
|
| 583 |
+
"annotation": {
|
| 584 |
+
"bbox_norm": [
|
| 585 |
+
0.15,
|
| 586 |
+
0.05,
|
| 587 |
+
0.45,
|
| 588 |
+
0.35
|
| 589 |
+
],
|
| 590 |
+
"likely_source_zone": "gas_system",
|
| 591 |
+
"plume_likely": true,
|
| 592 |
+
"priority_tier": "medium"
|
| 593 |
+
},
|
| 594 |
+
"panel_artifact_path": "data/cache/assets/de/de23491f80aa3c6d5254dafe691ed5fc09f2c82a8d9b775a21ca47a12ab283e5.panel.json",
|
| 595 |
+
"provenance": {
|
| 596 |
+
"created_at": "2026-04-24T08:02:16.455812+00:00",
|
| 597 |
+
"labeler": "model_bootstrap",
|
| 598 |
+
"notes": "captured from live scan pipeline; scan_status=live",
|
| 599 |
+
"region": null,
|
| 600 |
+
"source_ref": "scan:scan_024",
|
| 601 |
+
"source_type": "weak"
|
| 602 |
+
},
|
| 603 |
+
"sample_id": "live_scan_024",
|
| 604 |
+
"site_id": "LF_REAL_002",
|
| 605 |
+
"split": "train"
|
| 606 |
+
},
|
| 607 |
+
{
|
| 608 |
+
"annotation": {
|
| 609 |
+
"bbox_norm": [
|
| 610 |
+
0.02,
|
| 611 |
+
0.05,
|
| 612 |
+
0.15,
|
| 613 |
+
0.05
|
| 614 |
+
],
|
| 615 |
+
"likely_source_zone": "gas_system",
|
| 616 |
+
"plume_likely": true,
|
| 617 |
+
"priority_tier": "medium"
|
| 618 |
+
},
|
| 619 |
+
"panel_artifact_path": "data/cache/assets/3b/3ba439acaccfa51714a17882fe09149d46b1181d0316b71e151e0aa7def501db.panel.json",
|
| 620 |
+
"provenance": {
|
| 621 |
+
"created_at": "2026-04-24T08:06:30.834881+00:00",
|
| 622 |
+
"labeler": "model_bootstrap",
|
| 623 |
+
"notes": "captured from live scan pipeline; scan_status=live",
|
| 624 |
+
"region": null,
|
| 625 |
+
"source_ref": "scan:scan_025",
|
| 626 |
+
"source_type": "weak"
|
| 627 |
+
},
|
| 628 |
+
"sample_id": "live_scan_025",
|
| 629 |
+
"site_id": "LF_REAL_001",
|
| 630 |
+
"split": "validation"
|
| 631 |
+
},
|
| 632 |
+
{
|
| 633 |
+
"annotation": {
|
| 634 |
+
"bbox_norm": [
|
| 635 |
+
0.15,
|
| 636 |
+
0.05,
|
| 637 |
+
0.45,
|
| 638 |
+
0.35
|
| 639 |
+
],
|
| 640 |
+
"likely_source_zone": "gas_system",
|
| 641 |
+
"plume_likely": true,
|
| 642 |
+
"priority_tier": "medium"
|
| 643 |
+
},
|
| 644 |
+
"panel_artifact_path": "data/cache/assets/ba/babcada26270b44b9d874e0b592b941b542851b83f0d8ae5b92297466f61b247.panel.json",
|
| 645 |
+
"provenance": {
|
| 646 |
+
"created_at": "2026-04-24T08:08:20.524471+00:00",
|
| 647 |
+
"labeler": "model_bootstrap",
|
| 648 |
+
"notes": "captured from live scan pipeline; scan_status=live",
|
| 649 |
+
"region": null,
|
| 650 |
+
"source_ref": "scan:scan_026",
|
| 651 |
+
"source_type": "weak"
|
| 652 |
+
},
|
| 653 |
+
"sample_id": "live_scan_026",
|
| 654 |
+
"site_id": "LF_REAL_002",
|
| 655 |
+
"split": "train"
|
| 656 |
+
},
|
| 657 |
+
{
|
| 658 |
+
"annotation": {
|
| 659 |
+
"bbox_norm": [
|
| 660 |
+
0.15,
|
| 661 |
+
0.05,
|
| 662 |
+
0.45,
|
| 663 |
+
0.35
|
| 664 |
+
],
|
| 665 |
+
"likely_source_zone": "gas_system",
|
| 666 |
+
"plume_likely": true,
|
| 667 |
+
"priority_tier": "high"
|
| 668 |
+
},
|
| 669 |
+
"panel_artifact_path": "data/cache/assets/b3/b30510955d0e087ebd16f7268cd62c5751e19b07761ee7dab5487ae37efa5eda.panel.json",
|
| 670 |
+
"provenance": {
|
| 671 |
+
"created_at": "2026-04-24T08:09:15.283713+00:00",
|
| 672 |
+
"labeler": "model_bootstrap",
|
| 673 |
+
"notes": "captured from live scan pipeline; scan_status=live",
|
| 674 |
+
"region": null,
|
| 675 |
+
"source_ref": "scan:scan_027",
|
| 676 |
+
"source_type": "weak"
|
| 677 |
+
},
|
| 678 |
+
"sample_id": "live_scan_027",
|
| 679 |
+
"site_id": "LF_REAL_003",
|
| 680 |
+
"split": "train"
|
| 681 |
+
},
|
| 682 |
+
{
|
| 683 |
+
"annotation": {
|
| 684 |
+
"bbox_norm": [
|
| 685 |
+
0.15,
|
| 686 |
+
0.05,
|
| 687 |
+
0.35,
|
| 688 |
+
0.2
|
| 689 |
+
],
|
| 690 |
+
"likely_source_zone": "perimeter_or_unknown",
|
| 691 |
+
"plume_likely": true,
|
| 692 |
+
"priority_tier": "medium"
|
| 693 |
+
},
|
| 694 |
+
"panel_artifact_path": "data/cache/assets/a8/a8de1d960fc829729bc4ff75ba24c0b55fbe182c75a966f215bd91c4dd8fc979.panel.json",
|
| 695 |
+
"provenance": {
|
| 696 |
+
"created_at": "2026-04-24T08:10:10.560827+00:00",
|
| 697 |
+
"labeler": "model_bootstrap",
|
| 698 |
+
"notes": "captured from live scan pipeline; scan_status=live",
|
| 699 |
+
"region": null,
|
| 700 |
+
"source_ref": "scan:scan_028",
|
| 701 |
+
"source_type": "weak"
|
| 702 |
+
},
|
| 703 |
+
"sample_id": "live_scan_028",
|
| 704 |
+
"site_id": "LF_REAL_004",
|
| 705 |
+
"split": "test"
|
| 706 |
+
},
|
| 707 |
+
{
|
| 708 |
+
"annotation": {
|
| 709 |
+
"bbox_norm": [
|
| 710 |
+
0.15,
|
| 711 |
+
0.05,
|
| 712 |
+
0.35,
|
| 713 |
+
0.2
|
| 714 |
+
],
|
| 715 |
+
"likely_source_zone": "perimeter_or_unknown",
|
| 716 |
+
"plume_likely": true,
|
| 717 |
+
"priority_tier": "medium"
|
| 718 |
+
},
|
| 719 |
+
"panel_artifact_path": "data/cache/assets/5a/5ac10232526df9398c18f2e4c690977b56627f169a53434dc964fcfc26f0ef47.panel.json",
|
| 720 |
+
"provenance": {
|
| 721 |
+
"created_at": "2026-04-24T08:11:09.489426+00:00",
|
| 722 |
+
"labeler": "model_bootstrap",
|
| 723 |
+
"notes": "captured from live scan pipeline; scan_status=live",
|
| 724 |
+
"region": null,
|
| 725 |
+
"source_ref": "scan:scan_029",
|
| 726 |
+
"source_type": "weak"
|
| 727 |
+
},
|
| 728 |
+
"sample_id": "live_scan_029",
|
| 729 |
+
"site_id": "LF_REAL_005",
|
| 730 |
+
"split": "train"
|
| 731 |
+
},
|
| 732 |
+
{
|
| 733 |
+
"annotation": {
|
| 734 |
+
"bbox_norm": [
|
| 735 |
+
0.15,
|
| 736 |
+
0.05,
|
| 737 |
+
0.35,
|
| 738 |
+
0.2
|
| 739 |
+
],
|
| 740 |
+
"likely_source_zone": "gas_system",
|
| 741 |
+
"plume_likely": true,
|
| 742 |
+
"priority_tier": "medium"
|
| 743 |
+
},
|
| 744 |
+
"panel_artifact_path": "data/cache/assets/4b/4b281c5cbe35df6711d116a901420f370e551c21c16ad3bdde8db7adbd12d704.panel.json",
|
| 745 |
+
"provenance": {
|
| 746 |
+
"created_at": "2026-04-24T08:12:03.470663+00:00",
|
| 747 |
+
"labeler": "model_bootstrap",
|
| 748 |
+
"notes": "captured from live scan pipeline; scan_status=live",
|
| 749 |
+
"region": null,
|
| 750 |
+
"source_ref": "scan:scan_030",
|
| 751 |
+
"source_type": "weak"
|
| 752 |
+
},
|
| 753 |
+
"sample_id": "live_scan_030",
|
| 754 |
+
"site_id": "LF_REAL_006",
|
| 755 |
+
"split": "validation"
|
| 756 |
+
},
|
| 757 |
+
{
|
| 758 |
+
"annotation": {
|
| 759 |
+
"bbox_norm": [
|
| 760 |
+
0.15,
|
| 761 |
+
0.05,
|
| 762 |
+
0.35,
|
| 763 |
+
0.2
|
| 764 |
+
],
|
| 765 |
+
"likely_source_zone": "perimeter_or_unknown",
|
| 766 |
+
"plume_likely": true,
|
| 767 |
+
"priority_tier": "high"
|
| 768 |
+
},
|
| 769 |
+
"panel_artifact_path": "data/cache/assets/bc/bcf0ee7d68fed20859d5b0b3c9eb46f20852668c0210440d3e4db58971b43b5c.panel.json",
|
| 770 |
+
"provenance": {
|
| 771 |
+
"created_at": "2026-04-24T08:13:48.794502+00:00",
|
| 772 |
+
"labeler": "model_bootstrap",
|
| 773 |
+
"notes": "captured from live scan pipeline; scan_status=live",
|
| 774 |
+
"region": null,
|
| 775 |
+
"source_ref": "scan:scan_031",
|
| 776 |
+
"source_type": "weak"
|
| 777 |
+
},
|
| 778 |
+
"sample_id": "live_scan_031",
|
| 779 |
+
"site_id": "LF_REAL_007",
|
| 780 |
+
"split": "train"
|
| 781 |
+
},
|
| 782 |
+
{
|
| 783 |
+
"annotation": {
|
| 784 |
+
"bbox_norm": [
|
| 785 |
+
0.15,
|
| 786 |
+
0.05,
|
| 787 |
+
0.45,
|
| 788 |
+
0.35
|
| 789 |
+
],
|
| 790 |
+
"likely_source_zone": "gas_system",
|
| 791 |
+
"plume_likely": true,
|
| 792 |
+
"priority_tier": "medium"
|
| 793 |
+
},
|
| 794 |
+
"panel_artifact_path": "data/cache/assets/9e/9e2171e6c5d69cea815bd6c56232f55ea7b2c0e2cf24051ca218f0cfcb41fab4.panel.json",
|
| 795 |
+
"provenance": {
|
| 796 |
+
"created_at": "2026-04-24T08:15:24.192212+00:00",
|
| 797 |
+
"labeler": "model_bootstrap",
|
| 798 |
+
"notes": "captured from live scan pipeline; scan_status=live",
|
| 799 |
+
"region": null,
|
| 800 |
+
"source_ref": "scan:scan_032",
|
| 801 |
+
"source_type": "weak"
|
| 802 |
+
},
|
| 803 |
+
"sample_id": "live_scan_032",
|
| 804 |
+
"site_id": "LF_REAL_008",
|
| 805 |
+
"split": "validation"
|
| 806 |
+
},
|
| 807 |
+
{
|
| 808 |
+
"annotation": {
|
| 809 |
+
"bbox_norm": [
|
| 810 |
+
0.15,
|
| 811 |
+
0.05,
|
| 812 |
+
0.35,
|
| 813 |
+
0.2
|
| 814 |
+
],
|
| 815 |
+
"likely_source_zone": "perimeter_or_unknown",
|
| 816 |
+
"plume_likely": true,
|
| 817 |
+
"priority_tier": "medium"
|
| 818 |
+
},
|
| 819 |
+
"panel_artifact_path": "data/cache/assets/80/80c8ec2b43cc00a03bb7abbc95f38c53ef06d84607cf1a1e3454cf387499865c.panel.json",
|
| 820 |
+
"provenance": {
|
| 821 |
+
"created_at": "2026-04-24T08:16:15.454370+00:00",
|
| 822 |
+
"labeler": "model_bootstrap",
|
| 823 |
+
"notes": "captured from live scan pipeline; scan_status=live",
|
| 824 |
+
"region": null,
|
| 825 |
+
"source_ref": "scan:scan_033",
|
| 826 |
+
"source_type": "weak"
|
| 827 |
+
},
|
| 828 |
+
"sample_id": "live_scan_033",
|
| 829 |
+
"site_id": "LF_REAL_009",
|
| 830 |
+
"split": "train"
|
| 831 |
+
},
|
| 832 |
+
{
|
| 833 |
+
"annotation": {
|
| 834 |
+
"bbox_norm": [
|
| 835 |
+
0.15,
|
| 836 |
+
0.05,
|
| 837 |
+
0.35,
|
| 838 |
+
0.2
|
| 839 |
+
],
|
| 840 |
+
"likely_source_zone": "gas_system",
|
| 841 |
+
"plume_likely": true,
|
| 842 |
+
"priority_tier": "medium"
|
| 843 |
+
},
|
| 844 |
+
"panel_artifact_path": "data/cache/assets/0a/0a52fba723e9bd6f5b7091117c67804fba6ccc9015a08bf61701a30149b3fa7d.panel.json",
|
| 845 |
+
"provenance": {
|
| 846 |
+
"created_at": "2026-04-24T08:17:15.605009+00:00",
|
| 847 |
+
"labeler": "model_bootstrap",
|
| 848 |
+
"notes": "captured from live scan pipeline; scan_status=live",
|
| 849 |
+
"region": null,
|
| 850 |
+
"source_ref": "scan:scan_034",
|
| 851 |
+
"source_type": "weak"
|
| 852 |
+
},
|
| 853 |
+
"sample_id": "live_scan_034",
|
| 854 |
+
"site_id": "LF_REAL_010",
|
| 855 |
+
"split": "train"
|
| 856 |
+
},
|
| 857 |
+
{
|
| 858 |
+
"annotation": {
|
| 859 |
+
"bbox_norm": [
|
| 860 |
+
0.32,
|
| 861 |
+
0.17,
|
| 862 |
+
0.56,
|
| 863 |
+
0.32
|
| 864 |
+
],
|
| 865 |
+
"likely_source_zone": "perimeter_or_unknown",
|
| 866 |
+
"plume_likely": true,
|
| 867 |
+
"priority_tier": "high"
|
| 868 |
+
},
|
| 869 |
+
"panel_artifact_path": "data/cache/assets/0b/0b429e421204f8203bdf6c1f56f08e4970c5d259179736d1c3fbd33990de9134.panel.json",
|
| 870 |
+
"provenance": {
|
| 871 |
+
"created_at": "2026-04-24T16:23:45.233413+00:00",
|
| 872 |
+
"labeler": "model_bootstrap",
|
| 873 |
+
"notes": "captured from live scan pipeline; scan_status=live",
|
| 874 |
+
"region": null,
|
| 875 |
+
"source_ref": "scan:scan_035",
|
| 876 |
+
"source_type": "weak"
|
| 877 |
+
},
|
| 878 |
+
"sample_id": "live_scan_035",
|
| 879 |
+
"site_id": "LF_REAL_007",
|
| 880 |
+
"split": "train"
|
| 881 |
+
},
|
| 882 |
+
{
|
| 883 |
+
"annotation": {
|
| 884 |
+
"bbox_norm": [
|
| 885 |
+
0.26,
|
| 886 |
+
0.12,
|
| 887 |
+
0.47,
|
| 888 |
+
0.3
|
| 889 |
+
],
|
| 890 |
+
"likely_source_zone": "perimeter_or_unknown",
|
| 891 |
+
"plume_likely": true,
|
| 892 |
+
"priority_tier": "high"
|
| 893 |
+
},
|
| 894 |
+
"panel_artifact_path": "data/cache/assets/92/92f6763438699d48eff8d9714acdad24569bbae3db7bd8b324cde2036a6da9da.panel.json",
|
| 895 |
+
"provenance": {
|
| 896 |
+
"created_at": "2026-04-24T16:29:52.321126+00:00",
|
| 897 |
+
"labeler": "model_bootstrap",
|
| 898 |
+
"notes": "captured from live scan pipeline; scan_status=live",
|
| 899 |
+
"region": null,
|
| 900 |
+
"source_ref": "scan:scan_036",
|
| 901 |
+
"source_type": "weak"
|
| 902 |
+
},
|
| 903 |
+
"sample_id": "live_scan_036",
|
| 904 |
+
"site_id": "LF_REAL_007",
|
| 905 |
+
"split": "train"
|
| 906 |
+
},
|
| 907 |
+
{
|
| 908 |
+
"annotation": {
|
| 909 |
+
"bbox_norm": [
|
| 910 |
+
0.34,
|
| 911 |
+
0.39,
|
| 912 |
+
0.6,
|
| 913 |
+
0.58
|
| 914 |
+
],
|
| 915 |
+
"likely_source_zone": "perimeter_or_unknown",
|
| 916 |
+
"plume_likely": true,
|
| 917 |
+
"priority_tier": "high"
|
| 918 |
+
},
|
| 919 |
+
"panel_artifact_path": "data/cache/assets/1d/1d633be14cb6bb5f0d7ddf77877b24b68e9f10fd3ec7b864dd024e22922cd329.panel.json",
|
| 920 |
+
"provenance": {
|
| 921 |
+
"created_at": "2026-04-24T16:33:25.912870+00:00",
|
| 922 |
+
"labeler": "model_bootstrap",
|
| 923 |
+
"notes": "captured from live scan pipeline; scan_status=live",
|
| 924 |
+
"region": null,
|
| 925 |
+
"source_ref": "scan:scan_037",
|
| 926 |
+
"source_type": "weak"
|
| 927 |
+
},
|
| 928 |
+
"sample_id": "live_scan_037",
|
| 929 |
+
"site_id": "LF_REAL_007",
|
| 930 |
+
"split": "train"
|
| 931 |
+
},
|
| 932 |
+
{
|
| 933 |
+
"annotation": {
|
| 934 |
+
"bbox_norm": [
|
| 935 |
+
0.31,
|
| 936 |
+
0.21,
|
| 937 |
+
0.55,
|
| 938 |
+
0.38
|
| 939 |
+
],
|
| 940 |
+
"likely_source_zone": "gas_system",
|
| 941 |
+
"plume_likely": true,
|
| 942 |
+
"priority_tier": "medium"
|
| 943 |
+
},
|
| 944 |
+
"panel_artifact_path": "data/cache/assets/b8/b823bd8ebb14d9ec1a1f1225e3c9534506cd0a7025aed5995adcf8e4b029c35c.panel.json",
|
| 945 |
+
"provenance": {
|
| 946 |
+
"created_at": "2026-04-24T16:36:43.710810+00:00",
|
| 947 |
+
"labeler": "model_bootstrap",
|
| 948 |
+
"notes": "captured from live scan pipeline; scan_status=live",
|
| 949 |
+
"region": null,
|
| 950 |
+
"source_ref": "scan:scan_038",
|
| 951 |
+
"source_type": "weak"
|
| 952 |
+
},
|
| 953 |
+
"sample_id": "live_scan_038",
|
| 954 |
+
"site_id": "LF_REAL_001",
|
| 955 |
+
"split": "validation"
|
| 956 |
+
},
|
| 957 |
+
{
|
| 958 |
+
"annotation": {
|
| 959 |
+
"bbox_norm": [
|
| 960 |
+
0.0,
|
| 961 |
+
0.0,
|
| 962 |
+
1.0,
|
| 963 |
+
0.0
|
| 964 |
+
],
|
| 965 |
+
"likely_source_zone": "gas_system",
|
| 966 |
+
"plume_likely": true,
|
| 967 |
+
"priority_tier": "medium"
|
| 968 |
+
},
|
| 969 |
+
"panel_artifact_path": "data/cache/assets/a7/a75ad22cf43c57aa414778ef2fd7e2748adb68396efb428dd1be6e6f5de6454e.panel.json",
|
| 970 |
+
"provenance": {
|
| 971 |
+
"created_at": "2026-04-24T16:48:05.607004+00:00",
|
| 972 |
+
"labeler": "model_bootstrap",
|
| 973 |
+
"notes": "captured from live scan pipeline; scan_status=live",
|
| 974 |
+
"region": null,
|
| 975 |
+
"source_ref": "scan:scan_039",
|
| 976 |
+
"source_type": "weak"
|
| 977 |
+
},
|
| 978 |
+
"sample_id": "live_scan_039",
|
| 979 |
+
"site_id": "LF_REAL_003",
|
| 980 |
+
"split": "train"
|
| 981 |
+
},
|
| 982 |
+
{
|
| 983 |
+
"annotation": {
|
| 984 |
+
"bbox_norm": [
|
| 985 |
+
0.0,
|
| 986 |
+
0.0,
|
| 987 |
+
1.0,
|
| 988 |
+
0.0
|
| 989 |
+
],
|
| 990 |
+
"likely_source_zone": "gas_system",
|
| 991 |
+
"plume_likely": true,
|
| 992 |
+
"priority_tier": "medium"
|
| 993 |
+
},
|
| 994 |
+
"panel_artifact_path": "data/cache/assets/ac/acd87359d4bb4ef4cc2c6f033e866da866625309c334972575d0d2a0636705ad.panel.json",
|
| 995 |
+
"provenance": {
|
| 996 |
+
"created_at": "2026-04-24T16:52:21.359506+00:00",
|
| 997 |
+
"labeler": "model_bootstrap",
|
| 998 |
+
"notes": "captured from live scan pipeline; scan_status=live",
|
| 999 |
+
"region": null,
|
| 1000 |
+
"source_ref": "scan:scan_040",
|
| 1001 |
+
"source_type": "weak"
|
| 1002 |
+
},
|
| 1003 |
+
"sample_id": "live_scan_040",
|
| 1004 |
+
"site_id": "LF_REAL_003",
|
| 1005 |
+
"split": "train"
|
| 1006 |
+
},
|
| 1007 |
+
{
|
| 1008 |
+
"annotation": {
|
| 1009 |
+
"bbox_norm": [
|
| 1010 |
+
0.1,
|
| 1011 |
+
0.35,
|
| 1012 |
+
0.35,
|
| 1013 |
+
0.53
|
| 1014 |
+
],
|
| 1015 |
+
"likely_source_zone": "gas_system",
|
| 1016 |
+
"plume_likely": true,
|
| 1017 |
+
"priority_tier": "medium"
|
| 1018 |
+
},
|
| 1019 |
+
"panel_artifact_path": "data/cache/assets/17/170ada4dcf1a9a46c6a6736b45aa2391b28c1f40d887e38f2745036e155b5173.panel.json",
|
| 1020 |
+
"provenance": {
|
| 1021 |
+
"created_at": "2026-04-24T16:57:58.204203+00:00",
|
| 1022 |
+
"labeler": "model_bootstrap",
|
| 1023 |
+
"notes": "captured from live scan pipeline; scan_status=live",
|
| 1024 |
+
"region": null,
|
| 1025 |
+
"source_ref": "scan:scan_041",
|
| 1026 |
+
"source_type": "weak"
|
| 1027 |
+
},
|
| 1028 |
+
"sample_id": "live_scan_041",
|
| 1029 |
+
"site_id": "LF_REAL_001",
|
| 1030 |
+
"split": "validation"
|
| 1031 |
+
},
|
| 1032 |
+
{
|
| 1033 |
+
"annotation": {
|
| 1034 |
+
"bbox_norm": [
|
| 1035 |
+
0.26,
|
| 1036 |
+
0.25,
|
| 1037 |
+
0.45,
|
| 1038 |
+
0.4
|
| 1039 |
+
],
|
| 1040 |
+
"likely_source_zone": "gas_system",
|
| 1041 |
+
"plume_likely": true,
|
| 1042 |
+
"priority_tier": "medium"
|
| 1043 |
+
},
|
| 1044 |
+
"panel_artifact_path": "data/cache/assets/b3/b395a90b18727e05c06e7755efbeb2803c0814d8c1bddf359ddb1b8e50cf666f.panel.json",
|
| 1045 |
+
"provenance": {
|
| 1046 |
+
"created_at": "2026-04-24T17:00:56.588886+00:00",
|
| 1047 |
+
"labeler": "model_bootstrap",
|
| 1048 |
+
"notes": "captured from live scan pipeline; scan_status=live",
|
| 1049 |
+
"region": null,
|
| 1050 |
+
"source_ref": "scan:scan_042",
|
| 1051 |
+
"source_type": "weak"
|
| 1052 |
+
},
|
| 1053 |
+
"sample_id": "live_scan_042",
|
| 1054 |
+
"site_id": "LF_REAL_001",
|
| 1055 |
+
"split": "validation"
|
| 1056 |
+
},
|
| 1057 |
+
{
|
| 1058 |
+
"annotation": {
|
| 1059 |
+
"bbox_norm": [
|
| 1060 |
+
0.25,
|
| 1061 |
+
0.2,
|
| 1062 |
+
0.52,
|
| 1063 |
+
0.42
|
| 1064 |
+
],
|
| 1065 |
+
"likely_source_zone": "gas_system",
|
| 1066 |
+
"plume_likely": true,
|
| 1067 |
+
"priority_tier": "medium"
|
| 1068 |
+
},
|
| 1069 |
+
"panel_artifact_path": "data/cache/assets/db/dbdc161ceb6a79bc1b0a68a333f6bc82e88021f71a6b2ecb98627c1a8ef21303.panel.json",
|
| 1070 |
+
"provenance": {
|
| 1071 |
+
"created_at": "2026-04-24T17:03:33.107636+00:00",
|
| 1072 |
+
"labeler": "model_bootstrap",
|
| 1073 |
+
"notes": "captured from live scan pipeline; scan_status=live",
|
| 1074 |
+
"region": null,
|
| 1075 |
+
"source_ref": "scan:scan_043",
|
| 1076 |
+
"source_type": "weak"
|
| 1077 |
+
},
|
| 1078 |
+
"sample_id": "live_scan_043",
|
| 1079 |
+
"site_id": "LF_REAL_002",
|
| 1080 |
+
"split": "train"
|
| 1081 |
+
},
|
| 1082 |
+
{
|
| 1083 |
+
"annotation": {
|
| 1084 |
+
"bbox_norm": [
|
| 1085 |
+
0.0,
|
| 1086 |
+
0.0,
|
| 1087 |
+
1.0,
|
| 1088 |
+
0.0
|
| 1089 |
+
],
|
| 1090 |
+
"likely_source_zone": "gas_system",
|
| 1091 |
+
"plume_likely": true,
|
| 1092 |
+
"priority_tier": "medium"
|
| 1093 |
+
},
|
| 1094 |
+
"panel_artifact_path": "data/cache/assets/24/24480879e672e42ef425d12b64deb3de23c2f7101f75c39132cf3cde06e5bcb1.panel.json",
|
| 1095 |
+
"provenance": {
|
| 1096 |
+
"created_at": "2026-04-24T17:06:29.368146+00:00",
|
| 1097 |
+
"labeler": "model_bootstrap",
|
| 1098 |
+
"notes": "captured from live scan pipeline; scan_status=live",
|
| 1099 |
+
"region": null,
|
| 1100 |
+
"source_ref": "scan:scan_044",
|
| 1101 |
+
"source_type": "weak"
|
| 1102 |
+
},
|
| 1103 |
+
"sample_id": "live_scan_044",
|
| 1104 |
+
"site_id": "LF_REAL_003",
|
| 1105 |
+
"split": "train"
|
| 1106 |
+
},
|
| 1107 |
+
{
|
| 1108 |
+
"annotation": {
|
| 1109 |
+
"bbox_norm": [
|
| 1110 |
+
0.33,
|
| 1111 |
+
0.3,
|
| 1112 |
+
0.56,
|
| 1113 |
+
0.5
|
| 1114 |
+
],
|
| 1115 |
+
"likely_source_zone": "perimeter_or_unknown",
|
| 1116 |
+
"plume_likely": true,
|
| 1117 |
+
"priority_tier": "high"
|
| 1118 |
+
},
|
| 1119 |
+
"panel_artifact_path": "data/cache/assets/f8/f8ab2f8b463fe3e70341ec4201bb1d3646c301c72e7ad872d63c8dd343c06b92.panel.json",
|
| 1120 |
+
"provenance": {
|
| 1121 |
+
"created_at": "2026-04-24T17:09:28.569756+00:00",
|
| 1122 |
+
"labeler": "model_bootstrap",
|
| 1123 |
+
"notes": "captured from live scan pipeline; scan_status=live",
|
| 1124 |
+
"region": null,
|
| 1125 |
+
"source_ref": "scan:scan_045",
|
| 1126 |
+
"source_type": "weak"
|
| 1127 |
+
},
|
| 1128 |
+
"sample_id": "live_scan_045",
|
| 1129 |
+
"site_id": "LF_REAL_004",
|
| 1130 |
+
"split": "test"
|
| 1131 |
+
},
|
| 1132 |
+
{
|
| 1133 |
+
"annotation": {
|
| 1134 |
+
"bbox_norm": [
|
| 1135 |
+
0.2,
|
| 1136 |
+
0.15,
|
| 1137 |
+
0.47,
|
| 1138 |
+
0.35
|
| 1139 |
+
],
|
| 1140 |
+
"likely_source_zone": "perimeter_or_unknown",
|
| 1141 |
+
"plume_likely": true,
|
| 1142 |
+
"priority_tier": "high"
|
| 1143 |
+
},
|
| 1144 |
+
"panel_artifact_path": "data/cache/assets/ca/ca334568a5f1bf2ca7588703c40f3d451c919379f51b9cc6973fa2493b87129d.panel.json",
|
| 1145 |
+
"provenance": {
|
| 1146 |
+
"created_at": "2026-04-24T17:11:53.686217+00:00",
|
| 1147 |
+
"labeler": "model_bootstrap",
|
| 1148 |
+
"notes": "captured from live scan pipeline; scan_status=live",
|
| 1149 |
+
"region": null,
|
| 1150 |
+
"source_ref": "scan:scan_046",
|
| 1151 |
+
"source_type": "weak"
|
| 1152 |
+
},
|
| 1153 |
+
"sample_id": "live_scan_046",
|
| 1154 |
+
"site_id": "LF_REAL_005",
|
| 1155 |
+
"split": "train"
|
| 1156 |
+
},
|
| 1157 |
+
{
|
| 1158 |
+
"annotation": {
|
| 1159 |
+
"bbox_norm": [
|
| 1160 |
+
0.22,
|
| 1161 |
+
0.16,
|
| 1162 |
+
0.42,
|
| 1163 |
+
0.38
|
| 1164 |
+
],
|
| 1165 |
+
"likely_source_zone": "perimeter_or_unknown",
|
| 1166 |
+
"plume_likely": true,
|
| 1167 |
+
"priority_tier": "high"
|
| 1168 |
+
},
|
| 1169 |
+
"panel_artifact_path": "data/cache/assets/28/28f0e947113f32ac31173ad643e33e864a855f25788f0e6bd2226f870344e08e.panel.json",
|
| 1170 |
+
"provenance": {
|
| 1171 |
+
"created_at": "2026-04-24T17:13:56.670176+00:00",
|
| 1172 |
+
"labeler": "operator_review",
|
| 1173 |
+
"notes": "captured from live scan pipeline; scan_status=live",
|
| 1174 |
+
"region": null,
|
| 1175 |
+
"source_ref": "scan:scan_047",
|
| 1176 |
+
"source_type": "manual"
|
| 1177 |
+
},
|
| 1178 |
+
"sample_id": "live_scan_047",
|
| 1179 |
+
"site_id": "LF_REAL_005",
|
| 1180 |
+
"split": "train"
|
| 1181 |
+
},
|
| 1182 |
+
{
|
| 1183 |
+
"annotation": {
|
| 1184 |
+
"bbox_norm": [
|
| 1185 |
+
0.24,
|
| 1186 |
+
0.33,
|
| 1187 |
+
0.44,
|
| 1188 |
+
0.57
|
| 1189 |
+
],
|
| 1190 |
+
"likely_source_zone": "perimeter_or_unknown",
|
| 1191 |
+
"plume_likely": true,
|
| 1192 |
+
"priority_tier": "medium"
|
| 1193 |
+
},
|
| 1194 |
+
"panel_artifact_path": "data/cache/assets/75/7536df0f776f7c70a5a74d8926742aa8967c1946cdd1aa116db0af2f79410aba.panel.json",
|
| 1195 |
+
"provenance": {
|
| 1196 |
+
"created_at": "2026-04-24T17:17:11.990141+00:00",
|
| 1197 |
+
"labeler": "model_bootstrap",
|
| 1198 |
+
"notes": "captured from live scan pipeline; scan_status=live",
|
| 1199 |
+
"region": null,
|
| 1200 |
+
"source_ref": "scan:scan_048",
|
| 1201 |
+
"source_type": "weak"
|
| 1202 |
+
},
|
| 1203 |
+
"sample_id": "live_scan_048",
|
| 1204 |
+
"site_id": "LF_REAL_006",
|
| 1205 |
+
"split": "validation"
|
| 1206 |
+
},
|
| 1207 |
+
{
|
| 1208 |
+
"annotation": {
|
| 1209 |
+
"bbox_norm": [
|
| 1210 |
+
0.3,
|
| 1211 |
+
0.12,
|
| 1212 |
+
0.51,
|
| 1213 |
+
0.33
|
| 1214 |
+
],
|
| 1215 |
+
"likely_source_zone": "perimeter_or_unknown",
|
| 1216 |
+
"plume_likely": true,
|
| 1217 |
+
"priority_tier": "high"
|
| 1218 |
+
},
|
| 1219 |
+
"panel_artifact_path": "data/cache/assets/cc/cccef4ec986dc179ed70df9d8362fae9320f32b3094df1451316c3b88978b060.panel.json",
|
| 1220 |
+
"provenance": {
|
| 1221 |
+
"created_at": "2026-04-24T17:20:41.597909+00:00",
|
| 1222 |
+
"labeler": "model_bootstrap",
|
| 1223 |
+
"notes": "captured from live scan pipeline; scan_status=live",
|
| 1224 |
+
"region": null,
|
| 1225 |
+
"source_ref": "scan:scan_049",
|
| 1226 |
+
"source_type": "weak"
|
| 1227 |
+
},
|
| 1228 |
+
"sample_id": "live_scan_049",
|
| 1229 |
+
"site_id": "LF_REAL_007",
|
| 1230 |
+
"split": "train"
|
| 1231 |
+
},
|
| 1232 |
+
{
|
| 1233 |
+
"annotation": {
|
| 1234 |
+
"bbox_norm": [
|
| 1235 |
+
0.12,
|
| 1236 |
+
0.18,
|
| 1237 |
+
0.32,
|
| 1238 |
+
0.33
|
| 1239 |
+
],
|
| 1240 |
+
"likely_source_zone": "perimeter_or_unknown",
|
| 1241 |
+
"plume_likely": true,
|
| 1242 |
+
"priority_tier": "high"
|
| 1243 |
+
},
|
| 1244 |
+
"panel_artifact_path": "data/cache/assets/5f/5f09600edf96f3b1f82e588aa45f7ecfcd4201dbec8173617cbc5b85b3154bfd.panel.json",
|
| 1245 |
+
"provenance": {
|
| 1246 |
+
"created_at": "2026-04-24T18:26:38.862193+00:00",
|
| 1247 |
+
"labeler": "model_bootstrap",
|
| 1248 |
+
"notes": "captured from live scan pipeline; scan_status=live",
|
| 1249 |
+
"region": null,
|
| 1250 |
+
"source_ref": "scan:scan_050",
|
| 1251 |
+
"source_type": "weak"
|
| 1252 |
+
},
|
| 1253 |
+
"sample_id": "live_scan_050",
|
| 1254 |
+
"site_id": "LF_REAL_010",
|
| 1255 |
+
"split": "train"
|
| 1256 |
+
},
|
| 1257 |
+
{
|
| 1258 |
+
"annotation": {
|
| 1259 |
+
"bbox_norm": [
|
| 1260 |
+
0.26,
|
| 1261 |
+
0.25,
|
| 1262 |
+
0.44,
|
| 1263 |
+
0.46
|
| 1264 |
+
],
|
| 1265 |
+
"likely_source_zone": "gas_system",
|
| 1266 |
+
"plume_likely": true,
|
| 1267 |
+
"priority_tier": "high"
|
| 1268 |
+
},
|
| 1269 |
+
"panel_artifact_path": "data/cache/assets/f0/f0996d6b1583517de2e882a9271e438d14ac6b4d9c1ff39b4fa606126fd0e5e9.panel.json",
|
| 1270 |
+
"provenance": {
|
| 1271 |
+
"created_at": "2026-04-24T18:28:04.192063+00:00",
|
| 1272 |
+
"labeler": "model_bootstrap",
|
| 1273 |
+
"notes": "captured from live scan pipeline; scan_status=live",
|
| 1274 |
+
"region": null,
|
| 1275 |
+
"source_ref": "scan:scan_051",
|
| 1276 |
+
"source_type": "weak"
|
| 1277 |
+
},
|
| 1278 |
+
"sample_id": "live_scan_051",
|
| 1279 |
+
"site_id": "LF_REAL_009",
|
| 1280 |
+
"split": "train"
|
| 1281 |
+
},
|
| 1282 |
+
{
|
| 1283 |
+
"annotation": {
|
| 1284 |
+
"bbox_norm": [
|
| 1285 |
+
0.25,
|
| 1286 |
+
0.12,
|
| 1287 |
+
0.48,
|
| 1288 |
+
0.31
|
| 1289 |
+
],
|
| 1290 |
+
"likely_source_zone": "perimeter_or_unknown",
|
| 1291 |
+
"plume_likely": true,
|
| 1292 |
+
"priority_tier": "medium"
|
| 1293 |
+
},
|
| 1294 |
+
"panel_artifact_path": "data/cache/assets/d2/d2003dcd1f51c547cd65d1fa9c3ffcb975e4cdf1b764e30bb6ea72f97ff73683.panel.json",
|
| 1295 |
+
"provenance": {
|
| 1296 |
+
"created_at": "2026-04-24T18:28:57.009173+00:00",
|
| 1297 |
+
"labeler": "model_bootstrap",
|
| 1298 |
+
"notes": "captured from live scan pipeline; scan_status=live",
|
| 1299 |
+
"region": null,
|
| 1300 |
+
"source_ref": "scan:scan_052",
|
| 1301 |
+
"source_type": "weak"
|
| 1302 |
+
},
|
| 1303 |
+
"sample_id": "live_scan_052",
|
| 1304 |
+
"site_id": "LF_REAL_010",
|
| 1305 |
+
"split": "train"
|
| 1306 |
+
},
|
| 1307 |
+
{
|
| 1308 |
+
"annotation": {
|
| 1309 |
+
"bbox_norm": [
|
| 1310 |
+
0.15,
|
| 1311 |
+
0.05,
|
| 1312 |
+
0.35,
|
| 1313 |
+
0.05
|
| 1314 |
+
],
|
| 1315 |
+
"likely_source_zone": "perimeter_or_unknown",
|
| 1316 |
+
"plume_likely": true,
|
| 1317 |
+
"priority_tier": "high"
|
| 1318 |
+
},
|
| 1319 |
+
"panel_artifact_path": "data/cache/assets/53/5341dc8a30429f4dd2f41008f3d17da81b138a3ca3a05c282f89b5e3da8f688a.panel.json",
|
| 1320 |
+
"provenance": {
|
| 1321 |
+
"created_at": "2026-04-24T21:51:55.632696+00:00",
|
| 1322 |
+
"labeler": "model_bootstrap",
|
| 1323 |
+
"notes": "captured from live scan pipeline; scan_status=live",
|
| 1324 |
+
"region": null,
|
| 1325 |
+
"source_ref": "scan:scan_053",
|
| 1326 |
+
"source_type": "weak"
|
| 1327 |
+
},
|
| 1328 |
+
"sample_id": "live_scan_053",
|
| 1329 |
+
"site_id": "LF_REAL_005",
|
| 1330 |
+
"split": "train"
|
| 1331 |
+
},
|
| 1332 |
+
{
|
| 1333 |
+
"annotation": {
|
| 1334 |
+
"bbox_norm": [
|
| 1335 |
+
0.15,
|
| 1336 |
+
0.05,
|
| 1337 |
+
0.35,
|
| 1338 |
+
0.05
|
| 1339 |
+
],
|
| 1340 |
+
"likely_source_zone": "perimeter_or_unknown",
|
| 1341 |
+
"plume_likely": true,
|
| 1342 |
+
"priority_tier": "medium"
|
| 1343 |
+
},
|
| 1344 |
+
"panel_artifact_path": "data/cache/assets/0d/0d775b6d6f21185c3a202267ec7471143f05f1ee21ad5fffbbd13ab51085cc8a.panel.json",
|
| 1345 |
+
"provenance": {
|
| 1346 |
+
"created_at": "2026-04-26T07:30:25.389356+00:00",
|
| 1347 |
+
"labeler": "model_bootstrap",
|
| 1348 |
+
"notes": "captured from live scan pipeline; scan_status=live",
|
| 1349 |
+
"region": null,
|
| 1350 |
+
"source_ref": "scan:scan_054",
|
| 1351 |
+
"source_type": "weak"
|
| 1352 |
+
},
|
| 1353 |
+
"sample_id": "live_scan_054",
|
| 1354 |
+
"site_id": "LF_REAL_005",
|
| 1355 |
+
"split": "train"
|
| 1356 |
+
},
|
| 1357 |
+
{
|
| 1358 |
+
"annotation": {
|
| 1359 |
+
"bbox_norm": [
|
| 1360 |
+
0.2,
|
| 1361 |
+
0.24,
|
| 1362 |
+
0.43,
|
| 1363 |
+
0.43
|
| 1364 |
+
],
|
| 1365 |
+
"likely_source_zone": "perimeter_or_unknown",
|
| 1366 |
+
"plume_likely": true,
|
| 1367 |
+
"priority_tier": "high"
|
| 1368 |
+
},
|
| 1369 |
+
"panel_artifact_path": "data/cache/assets/af/afd298fa9eb83f03f8f57cc0fb5c6cb44a6e44c99c0466f2746c68ca4fe2e145.panel.json",
|
| 1370 |
+
"provenance": {
|
| 1371 |
+
"created_at": "2026-04-26T07:33:31.938612+00:00",
|
| 1372 |
+
"labeler": "model_bootstrap",
|
| 1373 |
+
"notes": "captured from live scan pipeline; scan_status=live",
|
| 1374 |
+
"region": null,
|
| 1375 |
+
"source_ref": "scan:scan_055",
|
| 1376 |
+
"source_type": "weak"
|
| 1377 |
+
},
|
| 1378 |
+
"sample_id": "live_scan_055",
|
| 1379 |
+
"site_id": "LF_REAL_004",
|
| 1380 |
+
"split": "test"
|
| 1381 |
+
},
|
| 1382 |
+
{
|
| 1383 |
+
"annotation": {
|
| 1384 |
+
"bbox_norm": [
|
| 1385 |
+
0.15,
|
| 1386 |
+
0.05,
|
| 1387 |
+
0.35,
|
| 1388 |
+
0.05
|
| 1389 |
+
],
|
| 1390 |
+
"likely_source_zone": "perimeter_or_unknown",
|
| 1391 |
+
"plume_likely": true,
|
| 1392 |
+
"priority_tier": "high"
|
| 1393 |
+
},
|
| 1394 |
+
"panel_artifact_path": "data/cache/assets/6e/6e710b4514a0cfde3ea82ece6f181c500241fd8db60a39915b4966c21f708927.panel.json",
|
| 1395 |
+
"provenance": {
|
| 1396 |
+
"created_at": "2026-04-26T07:35:53.404130+00:00",
|
| 1397 |
+
"labeler": "model_bootstrap",
|
| 1398 |
+
"notes": "captured from live scan pipeline; scan_status=live",
|
| 1399 |
+
"region": null,
|
| 1400 |
+
"source_ref": "scan:scan_056",
|
| 1401 |
+
"source_type": "weak"
|
| 1402 |
+
},
|
| 1403 |
+
"sample_id": "live_scan_056",
|
| 1404 |
+
"site_id": "LF_REAL_007",
|
| 1405 |
+
"split": "train"
|
| 1406 |
+
},
|
| 1407 |
+
{
|
| 1408 |
+
"annotation": {
|
| 1409 |
+
"bbox_norm": [
|
| 1410 |
+
0.15,
|
| 1411 |
+
0.05,
|
| 1412 |
+
0.35,
|
| 1413 |
+
0.05
|
| 1414 |
+
],
|
| 1415 |
+
"likely_source_zone": "perimeter_or_unknown",
|
| 1416 |
+
"plume_likely": true,
|
| 1417 |
+
"priority_tier": "high"
|
| 1418 |
+
},
|
| 1419 |
+
"panel_artifact_path": "data/cache/assets/c3/c39c5de3f6596ad736d184737250b52a0d901404eb051dddc85d386829c7487c.panel.json",
|
| 1420 |
+
"provenance": {
|
| 1421 |
+
"created_at": "2026-04-26T07:38:42.291815+00:00",
|
| 1422 |
+
"labeler": "model_bootstrap",
|
| 1423 |
+
"notes": "captured from live scan pipeline; scan_status=live",
|
| 1424 |
+
"region": null,
|
| 1425 |
+
"source_ref": "scan:scan_057",
|
| 1426 |
+
"source_type": "weak"
|
| 1427 |
+
},
|
| 1428 |
+
"sample_id": "live_scan_057",
|
| 1429 |
+
"site_id": "LF_REAL_007",
|
| 1430 |
+
"split": "train"
|
| 1431 |
+
},
|
| 1432 |
+
{
|
| 1433 |
+
"annotation": {
|
| 1434 |
+
"bbox_norm": [
|
| 1435 |
+
0.33,
|
| 1436 |
+
0.26,
|
| 1437 |
+
0.54,
|
| 1438 |
+
0.48
|
| 1439 |
+
],
|
| 1440 |
+
"likely_source_zone": "perimeter_or_unknown",
|
| 1441 |
+
"plume_likely": true,
|
| 1442 |
+
"priority_tier": "high"
|
| 1443 |
+
},
|
| 1444 |
+
"panel_artifact_path": "data/cache/assets/b5/b5e30c53faf257c547d7f0ddf232185f0a66cfa3147ff71f2979af0021d9b8df.panel.json",
|
| 1445 |
+
"provenance": {
|
| 1446 |
+
"created_at": "2026-04-28T14:06:15.197802+00:00",
|
| 1447 |
+
"labeler": "operator_review",
|
| 1448 |
+
"notes": "captured from live scan pipeline; scan_status=live",
|
| 1449 |
+
"region": null,
|
| 1450 |
+
"source_ref": "scan:scan_058",
|
| 1451 |
+
"source_type": "manual"
|
| 1452 |
+
},
|
| 1453 |
+
"sample_id": "live_scan_058",
|
| 1454 |
+
"site_id": "LF_REAL_007",
|
| 1455 |
+
"split": "train"
|
| 1456 |
+
},
|
| 1457 |
+
{
|
| 1458 |
+
"annotation": {
|
| 1459 |
+
"bbox_norm": [
|
| 1460 |
+
0.2,
|
| 1461 |
+
0.5,
|
| 1462 |
+
0.8,
|
| 1463 |
+
0.1
|
| 1464 |
+
],
|
| 1465 |
+
"likely_source_zone": "perimeter_or_unknown",
|
| 1466 |
+
"plume_likely": true,
|
| 1467 |
+
"priority_tier": "medium"
|
| 1468 |
+
},
|
| 1469 |
+
"panel_artifact_path": "data/cache/assets/a8/a8e552a21258a3114e094418fd7c55061b75d772244e08e772d73fb0c0ad9457.panel.json",
|
| 1470 |
+
"provenance": {
|
| 1471 |
+
"created_at": "2026-04-28T16:18:34.437330+00:00",
|
| 1472 |
+
"labeler": "model_bootstrap",
|
| 1473 |
+
"notes": "captured from live scan pipeline; scan_status=live",
|
| 1474 |
+
"region": "North America",
|
| 1475 |
+
"source_ref": "scan:scan_059",
|
| 1476 |
+
"source_type": "weak"
|
| 1477 |
+
},
|
| 1478 |
+
"sample_id": "live_scan_059",
|
| 1479 |
+
"site_id": "LF_GLOBAL_001",
|
| 1480 |
+
"split": "train"
|
| 1481 |
+
},
|
| 1482 |
+
{
|
| 1483 |
+
"annotation": {
|
| 1484 |
+
"bbox_norm": [
|
| 1485 |
+
0.02,
|
| 1486 |
+
0.05,
|
| 1487 |
+
0.15,
|
| 1488 |
+
0.08
|
| 1489 |
+
],
|
| 1490 |
+
"likely_source_zone": "gas_system",
|
| 1491 |
+
"plume_likely": true,
|
| 1492 |
+
"priority_tier": "high"
|
| 1493 |
+
},
|
| 1494 |
+
"panel_artifact_path": "data/cache/assets/71/719af48ee52477a152d32ca6b4e52bcae82356aa799b1f41d6477afae5258762.panel.json",
|
| 1495 |
+
"provenance": {
|
| 1496 |
+
"created_at": "2026-04-28T16:19:32.112167+00:00",
|
| 1497 |
+
"labeler": "model_bootstrap",
|
| 1498 |
+
"notes": "captured from live scan pipeline; scan_status=live",
|
| 1499 |
+
"region": "North America",
|
| 1500 |
+
"source_ref": "scan:scan_060",
|
| 1501 |
+
"source_type": "weak"
|
| 1502 |
+
},
|
| 1503 |
+
"sample_id": "live_scan_060",
|
| 1504 |
+
"site_id": "LF_GLOBAL_002",
|
| 1505 |
+
"split": "train"
|
| 1506 |
+
},
|
| 1507 |
+
{
|
| 1508 |
+
"annotation": {
|
| 1509 |
+
"bbox_norm": [
|
| 1510 |
+
0.05,
|
| 1511 |
+
0.15,
|
| 1512 |
+
0.35,
|
| 1513 |
+
0.2
|
| 1514 |
+
],
|
| 1515 |
+
"likely_source_zone": "perimeter_or_unknown",
|
| 1516 |
+
"plume_likely": true,
|
| 1517 |
+
"priority_tier": "medium"
|
| 1518 |
+
},
|
| 1519 |
+
"panel_artifact_path": "data/cache/assets/48/4827443367addf0a5f9cf94d611cab00517bf40338766932e9adc749418c78fb.panel.json",
|
| 1520 |
+
"provenance": {
|
| 1521 |
+
"created_at": "2026-04-28T16:21:24.632955+00:00",
|
| 1522 |
+
"labeler": "model_bootstrap",
|
| 1523 |
+
"notes": "captured from live scan pipeline; scan_status=live",
|
| 1524 |
+
"region": "North America",
|
| 1525 |
+
"source_ref": "scan:scan_061",
|
| 1526 |
+
"source_type": "weak"
|
| 1527 |
+
},
|
| 1528 |
+
"sample_id": "live_scan_061",
|
| 1529 |
+
"site_id": "LF_GLOBAL_003",
|
| 1530 |
+
"split": "train"
|
| 1531 |
+
},
|
| 1532 |
+
{
|
| 1533 |
+
"annotation": {
|
| 1534 |
+
"bbox_norm": [
|
| 1535 |
+
0.05,
|
| 1536 |
+
0.15,
|
| 1537 |
+
0.25,
|
| 1538 |
+
0.1
|
| 1539 |
+
],
|
| 1540 |
+
"likely_source_zone": "perimeter_or_unknown",
|
| 1541 |
+
"plume_likely": true,
|
| 1542 |
+
"priority_tier": "medium"
|
| 1543 |
+
},
|
| 1544 |
+
"panel_artifact_path": "data/cache/assets/7a/7a323ea9f8ceffc8abbda7f4046f221b5f3b41927c704d20aa0fc1a5544d23de.panel.json",
|
| 1545 |
+
"provenance": {
|
| 1546 |
+
"created_at": "2026-04-28T16:22:21.235812+00:00",
|
| 1547 |
+
"labeler": "model_bootstrap",
|
| 1548 |
+
"notes": "captured from live scan pipeline; scan_status=live",
|
| 1549 |
+
"region": "North America",
|
| 1550 |
+
"source_ref": "scan:scan_062",
|
| 1551 |
+
"source_type": "weak"
|
| 1552 |
+
},
|
| 1553 |
+
"sample_id": "live_scan_062",
|
| 1554 |
+
"site_id": "LF_GLOBAL_004",
|
| 1555 |
+
"split": "validation"
|
| 1556 |
+
},
|
| 1557 |
+
{
|
| 1558 |
+
"annotation": {
|
| 1559 |
+
"bbox_norm": [
|
| 1560 |
+
0.1,
|
| 1561 |
+
0.2,
|
| 1562 |
+
0.3,
|
| 1563 |
+
0.4
|
| 1564 |
+
],
|
| 1565 |
+
"likely_source_zone": "perimeter_or_unknown",
|
| 1566 |
+
"plume_likely": true,
|
| 1567 |
+
"priority_tier": "high"
|
| 1568 |
+
},
|
| 1569 |
+
"panel_artifact_path": "data/cache/assets/14/14dcb34b5c9b042b514f19bbb939c7f308f38b892491525e2a1fd19e10a4adea.panel.json",
|
| 1570 |
+
"provenance": {
|
| 1571 |
+
"created_at": "2026-04-28T16:23:25.618412+00:00",
|
| 1572 |
+
"labeler": "model_bootstrap",
|
| 1573 |
+
"notes": "captured from live scan pipeline; scan_status=live",
|
| 1574 |
+
"region": "North America",
|
| 1575 |
+
"source_ref": "scan:scan_063",
|
| 1576 |
+
"source_type": "weak"
|
| 1577 |
+
},
|
| 1578 |
+
"sample_id": "live_scan_063",
|
| 1579 |
+
"site_id": "LF_GLOBAL_005",
|
| 1580 |
+
"split": "test"
|
| 1581 |
+
},
|
| 1582 |
+
{
|
| 1583 |
+
"annotation": {
|
| 1584 |
+
"bbox_norm": [
|
| 1585 |
+
0.05,
|
| 1586 |
+
0.15,
|
| 1587 |
+
0.25,
|
| 1588 |
+
0.1
|
| 1589 |
+
],
|
| 1590 |
+
"likely_source_zone": "perimeter_or_unknown",
|
| 1591 |
+
"plume_likely": true,
|
| 1592 |
+
"priority_tier": "high"
|
| 1593 |
+
},
|
| 1594 |
+
"panel_artifact_path": "data/cache/assets/84/8414b0f541c6c1cf7e597169896ec87d2081aa9eca53183ec05e320ac760e012.panel.json",
|
| 1595 |
+
"provenance": {
|
| 1596 |
+
"created_at": "2026-04-28T16:24:59.618603+00:00",
|
| 1597 |
+
"labeler": "model_bootstrap",
|
| 1598 |
+
"notes": "captured from live scan pipeline; scan_status=live",
|
| 1599 |
+
"region": "Latin America",
|
| 1600 |
+
"source_ref": "scan:scan_064",
|
| 1601 |
+
"source_type": "weak"
|
| 1602 |
+
},
|
| 1603 |
+
"sample_id": "live_scan_064",
|
| 1604 |
+
"site_id": "LF_GLOBAL_006",
|
| 1605 |
+
"split": "train"
|
| 1606 |
+
},
|
| 1607 |
+
{
|
| 1608 |
+
"annotation": {
|
| 1609 |
+
"bbox_norm": [
|
| 1610 |
+
0.05,
|
| 1611 |
+
0.15,
|
| 1612 |
+
0.25,
|
| 1613 |
+
0.1
|
| 1614 |
+
],
|
| 1615 |
+
"likely_source_zone": "gas_system",
|
| 1616 |
+
"plume_likely": true,
|
| 1617 |
+
"priority_tier": "medium"
|
| 1618 |
+
},
|
| 1619 |
+
"panel_artifact_path": "data/cache/assets/bd/bd58cf1f9060ef57f6d425566bf47e15d07fe661094601477f17e5a121aacb7d.panel.json",
|
| 1620 |
+
"provenance": {
|
| 1621 |
+
"created_at": "2026-04-28T16:25:57.586511+00:00",
|
| 1622 |
+
"labeler": "model_bootstrap",
|
| 1623 |
+
"notes": "captured from live scan pipeline; scan_status=live",
|
| 1624 |
+
"region": "Latin America",
|
| 1625 |
+
"source_ref": "scan:scan_065",
|
| 1626 |
+
"source_type": "weak"
|
| 1627 |
+
},
|
| 1628 |
+
"sample_id": "live_scan_065",
|
| 1629 |
+
"site_id": "LF_GLOBAL_007",
|
| 1630 |
+
"split": "train"
|
| 1631 |
+
},
|
| 1632 |
+
{
|
| 1633 |
+
"annotation": {
|
| 1634 |
+
"bbox_norm": [
|
| 1635 |
+
0.05,
|
| 1636 |
+
0.15,
|
| 1637 |
+
0.25,
|
| 1638 |
+
0.1
|
| 1639 |
+
],
|
| 1640 |
+
"likely_source_zone": "perimeter_or_unknown",
|
| 1641 |
+
"plume_likely": true,
|
| 1642 |
+
"priority_tier": "medium"
|
| 1643 |
+
},
|
| 1644 |
+
"panel_artifact_path": "data/cache/assets/eb/eb842d34975359a5365da905d61f987d8a101f7ef861ab813bac8a58e214e706.panel.json",
|
| 1645 |
+
"provenance": {
|
| 1646 |
+
"created_at": "2026-04-28T16:27:00.061255+00:00",
|
| 1647 |
+
"labeler": "model_bootstrap",
|
| 1648 |
+
"notes": "captured from live scan pipeline; scan_status=live",
|
| 1649 |
+
"region": "Latin America",
|
| 1650 |
+
"source_ref": "scan:scan_066",
|
| 1651 |
+
"source_type": "weak"
|
| 1652 |
+
},
|
| 1653 |
+
"sample_id": "live_scan_066",
|
| 1654 |
+
"site_id": "LF_GLOBAL_008",
|
| 1655 |
+
"split": "train"
|
| 1656 |
+
},
|
| 1657 |
+
{
|
| 1658 |
+
"annotation": {
|
| 1659 |
+
"bbox_norm": [
|
| 1660 |
+
0.05,
|
| 1661 |
+
0.15,
|
| 1662 |
+
0.25,
|
| 1663 |
+
0.1
|
| 1664 |
+
],
|
| 1665 |
+
"likely_source_zone": "perimeter_or_unknown",
|
| 1666 |
+
"plume_likely": true,
|
| 1667 |
+
"priority_tier": "medium"
|
| 1668 |
+
},
|
| 1669 |
+
"panel_artifact_path": "data/cache/assets/8c/8cfd46db231df1371344c664543f8f0b9391dca445c7b2e1983369e76893b799.panel.json",
|
| 1670 |
+
"provenance": {
|
| 1671 |
+
"created_at": "2026-04-28T16:28:03.650442+00:00",
|
| 1672 |
+
"labeler": "model_bootstrap",
|
| 1673 |
+
"notes": "captured from live scan pipeline; scan_status=live",
|
| 1674 |
+
"region": "Latin America",
|
| 1675 |
+
"source_ref": "scan:scan_067",
|
| 1676 |
+
"source_type": "weak"
|
| 1677 |
+
},
|
| 1678 |
+
"sample_id": "live_scan_067",
|
| 1679 |
+
"site_id": "LF_GLOBAL_009",
|
| 1680 |
+
"split": "validation"
|
| 1681 |
+
},
|
| 1682 |
+
{
|
| 1683 |
+
"annotation": {
|
| 1684 |
+
"bbox_norm": [
|
| 1685 |
+
0.05,
|
| 1686 |
+
0.15,
|
| 1687 |
+
0.25,
|
| 1688 |
+
0.1
|
| 1689 |
+
],
|
| 1690 |
+
"likely_source_zone": "perimeter_or_unknown",
|
| 1691 |
+
"plume_likely": true,
|
| 1692 |
+
"priority_tier": "high"
|
| 1693 |
+
},
|
| 1694 |
+
"panel_artifact_path": "data/cache/assets/5a/5a9bd74b1500637225ba1e7338425a68937f2f15be764f8e23c80762937a02e6.panel.json",
|
| 1695 |
+
"provenance": {
|
| 1696 |
+
"created_at": "2026-04-28T16:28:57.255602+00:00",
|
| 1697 |
+
"labeler": "model_bootstrap",
|
| 1698 |
+
"notes": "captured from live scan pipeline; scan_status=live",
|
| 1699 |
+
"region": "Latin America",
|
| 1700 |
+
"source_ref": "scan:scan_068",
|
| 1701 |
+
"source_type": "weak"
|
| 1702 |
+
},
|
| 1703 |
+
"sample_id": "live_scan_068",
|
| 1704 |
+
"site_id": "LF_GLOBAL_010",
|
| 1705 |
+
"split": "test"
|
| 1706 |
+
},
|
| 1707 |
+
{
|
| 1708 |
+
"annotation": {
|
| 1709 |
+
"bbox_norm": [
|
| 1710 |
+
0.2,
|
| 1711 |
+
0.5,
|
| 1712 |
+
0.8,
|
| 1713 |
+
0.1
|
| 1714 |
+
],
|
| 1715 |
+
"likely_source_zone": "gas_system",
|
| 1716 |
+
"plume_likely": true,
|
| 1717 |
+
"priority_tier": "medium"
|
| 1718 |
+
},
|
| 1719 |
+
"panel_artifact_path": "data/cache/assets/a7/a7eb16daf9f3c877be6230d32a757915dbd0474938658cb74d852f337a559619.panel.json",
|
| 1720 |
+
"provenance": {
|
| 1721 |
+
"created_at": "2026-04-28T16:35:28.526774+00:00",
|
| 1722 |
+
"labeler": "model_bootstrap",
|
| 1723 |
+
"notes": "captured from live scan pipeline; scan_status=live",
|
| 1724 |
+
"region": "Asia",
|
| 1725 |
+
"source_ref": "scan:scan_069",
|
| 1726 |
+
"source_type": "weak"
|
| 1727 |
+
},
|
| 1728 |
+
"sample_id": "live_scan_069",
|
| 1729 |
+
"site_id": "LF_GLOBAL_011",
|
| 1730 |
+
"split": "train"
|
| 1731 |
+
},
|
| 1732 |
+
{
|
| 1733 |
+
"annotation": {
|
| 1734 |
+
"bbox_norm": [
|
| 1735 |
+
0.02,
|
| 1736 |
+
0.05,
|
| 1737 |
+
0.15,
|
| 1738 |
+
0.05
|
| 1739 |
+
],
|
| 1740 |
+
"likely_source_zone": "gas_system",
|
| 1741 |
+
"plume_likely": true,
|
| 1742 |
+
"priority_tier": "medium"
|
| 1743 |
+
},
|
| 1744 |
+
"panel_artifact_path": "data/cache/assets/e3/e3e934e7fc6eb22de42aa7ea630da6583daa141e361aecbe1a195d34e51f9a2e.panel.json",
|
| 1745 |
+
"provenance": {
|
| 1746 |
+
"created_at": "2026-04-28T16:37:29.940561+00:00",
|
| 1747 |
+
"labeler": "model_bootstrap",
|
| 1748 |
+
"notes": "captured from live scan pipeline; scan_status=live",
|
| 1749 |
+
"region": "Asia",
|
| 1750 |
+
"source_ref": "scan:scan_070",
|
| 1751 |
+
"source_type": "weak"
|
| 1752 |
+
},
|
| 1753 |
+
"sample_id": "live_scan_070",
|
| 1754 |
+
"site_id": "LF_GLOBAL_012",
|
| 1755 |
+
"split": "train"
|
| 1756 |
+
},
|
| 1757 |
+
{
|
| 1758 |
+
"annotation": {
|
| 1759 |
+
"bbox_norm": [
|
| 1760 |
+
0.05,
|
| 1761 |
+
0.15,
|
| 1762 |
+
0.25,
|
| 1763 |
+
0.1
|
| 1764 |
+
],
|
| 1765 |
+
"likely_source_zone": "perimeter_or_unknown",
|
| 1766 |
+
"plume_likely": true,
|
| 1767 |
+
"priority_tier": "medium"
|
| 1768 |
+
},
|
| 1769 |
+
"panel_artifact_path": "data/cache/assets/a1/a14f39f8459b55a7e0989396cb13f0e5bb5ef91dc403ce6423bf756c38e04a2d.panel.json",
|
| 1770 |
+
"provenance": {
|
| 1771 |
+
"created_at": "2026-04-28T16:39:20.104632+00:00",
|
| 1772 |
+
"labeler": "model_bootstrap",
|
| 1773 |
+
"notes": "captured from live scan pipeline; scan_status=live",
|
| 1774 |
+
"region": "Asia",
|
| 1775 |
+
"source_ref": "scan:scan_071",
|
| 1776 |
+
"source_type": "weak"
|
| 1777 |
+
},
|
| 1778 |
+
"sample_id": "live_scan_071",
|
| 1779 |
+
"site_id": "LF_GLOBAL_014",
|
| 1780 |
+
"split": "validation"
|
| 1781 |
+
},
|
| 1782 |
+
{
|
| 1783 |
+
"annotation": {
|
| 1784 |
+
"bbox_norm": [
|
| 1785 |
+
0.05,
|
| 1786 |
+
0.15,
|
| 1787 |
+
0.35,
|
| 1788 |
+
0.25
|
| 1789 |
+
],
|
| 1790 |
+
"likely_source_zone": "perimeter_or_unknown",
|
| 1791 |
+
"plume_likely": true,
|
| 1792 |
+
"priority_tier": "medium"
|
| 1793 |
+
},
|
| 1794 |
+
"panel_artifact_path": "data/cache/assets/90/9094d7d97537a55f60b23769bd6ba43606f88ae89d593fd71f832051ce394595.panel.json",
|
| 1795 |
+
"provenance": {
|
| 1796 |
+
"created_at": "2026-04-28T16:41:16.629699+00:00",
|
| 1797 |
+
"labeler": "model_bootstrap",
|
| 1798 |
+
"notes": "captured from live scan pipeline; scan_status=live",
|
| 1799 |
+
"region": "Asia",
|
| 1800 |
+
"source_ref": "scan:scan_072",
|
| 1801 |
+
"source_type": "weak"
|
| 1802 |
+
},
|
| 1803 |
+
"sample_id": "live_scan_072",
|
| 1804 |
+
"site_id": "LF_GLOBAL_015",
|
| 1805 |
+
"split": "test"
|
| 1806 |
+
},
|
| 1807 |
+
{
|
| 1808 |
+
"annotation": {
|
| 1809 |
+
"bbox_norm": [
|
| 1810 |
+
0.05,
|
| 1811 |
+
0.15,
|
| 1812 |
+
0.25,
|
| 1813 |
+
0.1
|
| 1814 |
+
],
|
| 1815 |
+
"likely_source_zone": "perimeter_or_unknown",
|
| 1816 |
+
"plume_likely": true,
|
| 1817 |
+
"priority_tier": "medium"
|
| 1818 |
+
},
|
| 1819 |
+
"panel_artifact_path": "data/cache/assets/64/64b814a7513bffae77bea17d3714aed957380ca3da3b1cbc64178c78a8ee6d80.panel.json",
|
| 1820 |
+
"provenance": {
|
| 1821 |
+
"created_at": "2026-04-28T16:42:20.316694+00:00",
|
| 1822 |
+
"labeler": "model_bootstrap",
|
| 1823 |
+
"notes": "captured from live scan pipeline; scan_status=live",
|
| 1824 |
+
"region": "Asia",
|
| 1825 |
+
"source_ref": "scan:scan_073",
|
| 1826 |
+
"source_type": "weak"
|
| 1827 |
+
},
|
| 1828 |
+
"sample_id": "live_scan_073",
|
| 1829 |
+
"site_id": "LF_GLOBAL_016",
|
| 1830 |
+
"split": "train"
|
| 1831 |
+
},
|
| 1832 |
+
{
|
| 1833 |
+
"annotation": {
|
| 1834 |
+
"bbox_norm": [
|
| 1835 |
+
0.05,
|
| 1836 |
+
0.15,
|
| 1837 |
+
0.35,
|
| 1838 |
+
0.25
|
| 1839 |
+
],
|
| 1840 |
+
"likely_source_zone": "gas_system",
|
| 1841 |
+
"plume_likely": true,
|
| 1842 |
+
"priority_tier": "medium"
|
| 1843 |
+
},
|
| 1844 |
+
"panel_artifact_path": "data/cache/assets/9b/9b2e65b8700c27a68967aaedcee05a16a561bb304a8ffa8ed76b6a1ca32afb61.panel.json",
|
| 1845 |
+
"provenance": {
|
| 1846 |
+
"created_at": "2026-04-28T16:43:59.554022+00:00",
|
| 1847 |
+
"labeler": "model_bootstrap",
|
| 1848 |
+
"notes": "captured from live scan pipeline; scan_status=live",
|
| 1849 |
+
"region": "Asia",
|
| 1850 |
+
"source_ref": "scan:scan_074",
|
| 1851 |
+
"source_type": "weak"
|
| 1852 |
+
},
|
| 1853 |
+
"sample_id": "live_scan_074",
|
| 1854 |
+
"site_id": "LF_GLOBAL_017",
|
| 1855 |
+
"split": "train"
|
| 1856 |
+
},
|
| 1857 |
+
{
|
| 1858 |
+
"annotation": {
|
| 1859 |
+
"bbox_norm": [
|
| 1860 |
+
0.05,
|
| 1861 |
+
0.15,
|
| 1862 |
+
0.25,
|
| 1863 |
+
0.1
|
| 1864 |
+
],
|
| 1865 |
+
"likely_source_zone": "perimeter_or_unknown",
|
| 1866 |
+
"plume_likely": true,
|
| 1867 |
+
"priority_tier": "medium"
|
| 1868 |
+
},
|
| 1869 |
+
"panel_artifact_path": "data/cache/assets/63/634a160a386ebc015a138d975b49ca648cea2aa4aa1d8dfa9b97c586cdb1ed47.panel.json",
|
| 1870 |
+
"provenance": {
|
| 1871 |
+
"created_at": "2026-04-28T16:45:04.392202+00:00",
|
| 1872 |
+
"labeler": "model_bootstrap",
|
| 1873 |
+
"notes": "captured from live scan pipeline; scan_status=live",
|
| 1874 |
+
"region": "Africa",
|
| 1875 |
+
"source_ref": "scan:scan_075",
|
| 1876 |
+
"source_type": "weak"
|
| 1877 |
+
},
|
| 1878 |
+
"sample_id": "live_scan_075",
|
| 1879 |
+
"site_id": "LF_GLOBAL_018",
|
| 1880 |
+
"split": "train"
|
| 1881 |
+
},
|
| 1882 |
+
{
|
| 1883 |
+
"annotation": {
|
| 1884 |
+
"bbox_norm": [
|
| 1885 |
+
0.05,
|
| 1886 |
+
0.15,
|
| 1887 |
+
0.25,
|
| 1888 |
+
0.1
|
| 1889 |
+
],
|
| 1890 |
+
"likely_source_zone": "perimeter_or_unknown",
|
| 1891 |
+
"plume_likely": true,
|
| 1892 |
+
"priority_tier": "medium"
|
| 1893 |
+
},
|
| 1894 |
+
"panel_artifact_path": "data/cache/assets/f9/f952f7121186ec1bc149a52141ea670faf4343113365e2ea9a37c61404cd266a.panel.json",
|
| 1895 |
+
"provenance": {
|
| 1896 |
+
"created_at": "2026-04-28T16:46:59.483257+00:00",
|
| 1897 |
+
"labeler": "model_bootstrap",
|
| 1898 |
+
"notes": "captured from live scan pipeline; scan_status=live",
|
| 1899 |
+
"region": "Africa",
|
| 1900 |
+
"source_ref": "scan:scan_076",
|
| 1901 |
+
"source_type": "weak"
|
| 1902 |
+
},
|
| 1903 |
+
"sample_id": "live_scan_076",
|
| 1904 |
+
"site_id": "LF_GLOBAL_020",
|
| 1905 |
+
"split": "test"
|
| 1906 |
+
},
|
| 1907 |
+
{
|
| 1908 |
+
"annotation": {
|
| 1909 |
+
"bbox_norm": [
|
| 1910 |
+
0.05,
|
| 1911 |
+
0.15,
|
| 1912 |
+
0.25,
|
| 1913 |
+
0.1
|
| 1914 |
+
],
|
| 1915 |
+
"likely_source_zone": "perimeter_or_unknown",
|
| 1916 |
+
"plume_likely": true,
|
| 1917 |
+
"priority_tier": "medium"
|
| 1918 |
+
},
|
| 1919 |
+
"panel_artifact_path": "data/cache/assets/20/204a8ff638733bd4d7a19fe0e27d834e3284c627d7ab97aa462309a43347fc2c.panel.json",
|
| 1920 |
+
"provenance": {
|
| 1921 |
+
"created_at": "2026-04-28T16:48:45.146190+00:00",
|
| 1922 |
+
"labeler": "model_bootstrap",
|
| 1923 |
+
"notes": "captured from live scan pipeline; scan_status=live",
|
| 1924 |
+
"region": "Africa",
|
| 1925 |
+
"source_ref": "scan:scan_077",
|
| 1926 |
+
"source_type": "weak"
|
| 1927 |
+
},
|
| 1928 |
+
"sample_id": "live_scan_077",
|
| 1929 |
+
"site_id": "LF_GLOBAL_022",
|
| 1930 |
+
"split": "train"
|
| 1931 |
+
},
|
| 1932 |
+
{
|
| 1933 |
+
"annotation": {
|
| 1934 |
+
"bbox_norm": [
|
| 1935 |
+
0.05,
|
| 1936 |
+
0.15,
|
| 1937 |
+
0.25,
|
| 1938 |
+
0.1
|
| 1939 |
+
],
|
| 1940 |
+
"likely_source_zone": "gas_system",
|
| 1941 |
+
"plume_likely": true,
|
| 1942 |
+
"priority_tier": "medium"
|
| 1943 |
+
},
|
| 1944 |
+
"panel_artifact_path": "data/cache/assets/9b/9bf422da39351eb691de29a0898c0673a5625c6f6e1e595b4d4d7432b43d3a31.panel.json",
|
| 1945 |
+
"provenance": {
|
| 1946 |
+
"created_at": "2026-04-28T16:50:21.382913+00:00",
|
| 1947 |
+
"labeler": "model_bootstrap",
|
| 1948 |
+
"notes": "captured from live scan pipeline; scan_status=live",
|
| 1949 |
+
"region": "Middle East",
|
| 1950 |
+
"source_ref": "scan:scan_078",
|
| 1951 |
+
"source_type": "weak"
|
| 1952 |
+
},
|
| 1953 |
+
"sample_id": "live_scan_078",
|
| 1954 |
+
"site_id": "LF_GLOBAL_023",
|
| 1955 |
+
"split": "train"
|
| 1956 |
+
}
|
| 1957 |
+
],
|
| 1958 |
+
"source_labels_path": "data/labels/phase6_samples_live_v1.jsonl",
|
| 1959 |
+
"split_counts": {
|
| 1960 |
+
"demo": 0,
|
| 1961 |
+
"test": 9,
|
| 1962 |
+
"train": 49,
|
| 1963 |
+
"validation": 20
|
| 1964 |
+
}
|
| 1965 |
+
}
|
dataset_splits_v1.json
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"frozen_at": "2026-04-28T16:50:41.754006+00:00",
|
| 3 |
+
"manifest_checksum": "a6738e1af7d89f6fbd0d567c89759f6103beaa81553074a3ad520c6810988b01",
|
| 4 |
+
"split_version": "phase6.splits.v1",
|
| 5 |
+
"splits": {
|
| 6 |
+
"demo": [],
|
| 7 |
+
"test": [
|
| 8 |
+
"live_scan_005",
|
| 9 |
+
"live_scan_017",
|
| 10 |
+
"live_scan_028",
|
| 11 |
+
"live_scan_045",
|
| 12 |
+
"live_scan_055",
|
| 13 |
+
"live_scan_063",
|
| 14 |
+
"live_scan_068",
|
| 15 |
+
"live_scan_072",
|
| 16 |
+
"live_scan_076"
|
| 17 |
+
],
|
| 18 |
+
"train": [
|
| 19 |
+
"live_scan_004",
|
| 20 |
+
"live_scan_006",
|
| 21 |
+
"live_scan_008",
|
| 22 |
+
"live_scan_010",
|
| 23 |
+
"live_scan_011",
|
| 24 |
+
"live_scan_013",
|
| 25 |
+
"live_scan_015",
|
| 26 |
+
"live_scan_016",
|
| 27 |
+
"live_scan_019",
|
| 28 |
+
"live_scan_021",
|
| 29 |
+
"live_scan_022",
|
| 30 |
+
"live_scan_024",
|
| 31 |
+
"live_scan_026",
|
| 32 |
+
"live_scan_027",
|
| 33 |
+
"live_scan_029",
|
| 34 |
+
"live_scan_031",
|
| 35 |
+
"live_scan_033",
|
| 36 |
+
"live_scan_034",
|
| 37 |
+
"live_scan_035",
|
| 38 |
+
"live_scan_036",
|
| 39 |
+
"live_scan_037",
|
| 40 |
+
"live_scan_039",
|
| 41 |
+
"live_scan_040",
|
| 42 |
+
"live_scan_043",
|
| 43 |
+
"live_scan_044",
|
| 44 |
+
"live_scan_046",
|
| 45 |
+
"live_scan_047",
|
| 46 |
+
"live_scan_049",
|
| 47 |
+
"live_scan_050",
|
| 48 |
+
"live_scan_051",
|
| 49 |
+
"live_scan_052",
|
| 50 |
+
"live_scan_053",
|
| 51 |
+
"live_scan_054",
|
| 52 |
+
"live_scan_056",
|
| 53 |
+
"live_scan_057",
|
| 54 |
+
"live_scan_058",
|
| 55 |
+
"live_scan_059",
|
| 56 |
+
"live_scan_060",
|
| 57 |
+
"live_scan_061",
|
| 58 |
+
"live_scan_064",
|
| 59 |
+
"live_scan_065",
|
| 60 |
+
"live_scan_066",
|
| 61 |
+
"live_scan_069",
|
| 62 |
+
"live_scan_070",
|
| 63 |
+
"live_scan_073",
|
| 64 |
+
"live_scan_074",
|
| 65 |
+
"live_scan_075",
|
| 66 |
+
"live_scan_077",
|
| 67 |
+
"live_scan_078"
|
| 68 |
+
],
|
| 69 |
+
"validation": [
|
| 70 |
+
"live_scan_001",
|
| 71 |
+
"live_scan_002",
|
| 72 |
+
"live_scan_003",
|
| 73 |
+
"live_scan_007",
|
| 74 |
+
"live_scan_009",
|
| 75 |
+
"live_scan_012",
|
| 76 |
+
"live_scan_014",
|
| 77 |
+
"live_scan_018",
|
| 78 |
+
"live_scan_020",
|
| 79 |
+
"live_scan_023",
|
| 80 |
+
"live_scan_025",
|
| 81 |
+
"live_scan_030",
|
| 82 |
+
"live_scan_032",
|
| 83 |
+
"live_scan_038",
|
| 84 |
+
"live_scan_041",
|
| 85 |
+
"live_scan_042",
|
| 86 |
+
"live_scan_048",
|
| 87 |
+
"live_scan_062",
|
| 88 |
+
"live_scan_067",
|
| 89 |
+
"live_scan_071"
|
| 90 |
+
]
|
| 91 |
+
}
|
| 92 |
+
}
|
fine_tuning_methodology.md
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Fine-Tuning Methodology And Public Weights Plan
|
| 2 |
+
|
| 3 |
+
## Current Status
|
| 4 |
+
|
| 5 |
+
LandfillSentry has the fine-tuning pipeline wiring, dataset manifests, benchmark harness, and adapter-loading runtime path in place.
|
| 6 |
+
|
| 7 |
+
The current Modal job is a Phase 6 scaffold: it validates GPU orchestration and writes reproducibility/checkpoint metadata, but it does not yet train real LoRA weights. Do not present the scaffold artifact as final public fine-tuned weights.
|
| 8 |
+
|
| 9 |
+
## Implemented Artifacts
|
| 10 |
+
|
| 11 |
+
- Dataset builder: `scripts/build_phase6_dataset.py`
|
| 12 |
+
- Modal training entrypoint: `scripts/train_lora.py`
|
| 13 |
+
- Modal app: `ml/training/modal_lora_train.py`
|
| 14 |
+
- Adapter artifact helpers: `ml/training/lora_artifacts.py`
|
| 15 |
+
- Runtime adapter loading: `apps/api/services/inference_service.py`
|
| 16 |
+
- Evaluation harness: `scripts/benchmark_models.py`
|
| 17 |
+
- Dataset manifest: `data/manifests/dataset_manifest_v1.json`
|
| 18 |
+
- Split manifest: `data/manifests/dataset_splits_v1.json`
|
| 19 |
+
- Benchmark report: `data/manifests/phase7_evaluation_report.json`
|
| 20 |
+
|
| 21 |
+
## Dataset
|
| 22 |
+
|
| 23 |
+
The frozen dataset manifest contains 78 live-scan-derived samples with site-based splits:
|
| 24 |
+
|
| 25 |
+
- train: 49
|
| 26 |
+
- validation: 20
|
| 27 |
+
- test: 9
|
| 28 |
+
|
| 29 |
+
Inputs combine current Sentinel imagery, historical Sentinel context, Mapbox context, generated candidates, panel metadata, and operator-review labels/corrections where available.
|
| 30 |
+
|
| 31 |
+
## Recommended Public Weights Flow
|
| 32 |
+
|
| 33 |
+
1. Run or replace the scaffold with a real LoRA trainer for `LiquidAI/LFM2.5-VL-450M`.
|
| 34 |
+
2. Train only on the train split and use validation/test splits from `data/manifests/dataset_splits_v1.json`.
|
| 35 |
+
3. Publish the PEFT adapter to Hugging Face with:
|
| 36 |
+
- `adapter_config.json`
|
| 37 |
+
- `adapter_model.safetensors`
|
| 38 |
+
- model card documenting dataset, splits, training parameters, and limitations
|
| 39 |
+
- link to this repo's training/evaluation code
|
| 40 |
+
|
| 41 |
+
Use the safe uploader instead of uploading the project root:
|
| 42 |
+
|
| 43 |
+
```powershell
|
| 44 |
+
.\.venv\Scripts\python.exe scripts\upload_hf_adapter.py --adapter-dir path\to\checkpoint-lora-v1 --repo-id akashreddy2103/landfill
|
| 45 |
+
```
|
| 46 |
+
|
| 47 |
+
Do not run `upload_folder(folder_path=".")`; that can leak `.env.local`, logs, caches, and non-model artifacts.
|
| 48 |
+
4. Set `.env.local`:
|
| 49 |
+
|
| 50 |
+
```env
|
| 51 |
+
HF_ADAPTER_ID=your-org/landfillsentry-lfm25vl-lora
|
| 52 |
+
HF_ADAPTER_REVISION=main
|
| 53 |
+
HF_LOCAL_FILES_ONLY=false
|
| 54 |
+
```
|
| 55 |
+
|
| 56 |
+
5. Run:
|
| 57 |
+
|
| 58 |
+
```powershell
|
| 59 |
+
.\.venv\Scripts\python.exe scripts\judge_preflight.py --strict-public-weights
|
| 60 |
+
.\.venv\Scripts\python.exe scripts\benchmark_models.py
|
| 61 |
+
```
|
| 62 |
+
|
| 63 |
+
6. Update `docs/benchmark_summary_for_submission.md` with the public adapter ID and final base-vs-adapter metrics.
|
| 64 |
+
|
| 65 |
+
## Current Benchmark Interpretation
|
| 66 |
+
|
| 67 |
+
The current benchmark shows measured improvement on a small fixture proxy. It is useful for proving the evaluation harness and domain-adaptation direction, but it is not a broad public model-quality claim until real public adapter weights are published and remeasured.
|
judge_deployment_runbook.md
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Judge Deployment Runbook
|
| 2 |
+
|
| 3 |
+
## Recommended Choice
|
| 4 |
+
|
| 5 |
+
Use Docker for the LandfillSentry API/UI package and keep inference on the existing Hugging Face Transformers + PEFT path.
|
| 6 |
+
|
| 7 |
+
Why this is the safest choice for judging:
|
| 8 |
+
|
| 9 |
+
- The app already supports `LiquidAI/LFM2.5-VL-450M` through Transformers.
|
| 10 |
+
- LoRA adapter loading is already wired through `HF_ADAPTER_ID` and `HF_ADAPTER_REVISION`.
|
| 11 |
+
- The strict judge path already rejects mock/fallback output when `REQUIRE_LIVE_RESULTS=true` and `INFERENCE_ALLOW_FALLBACK=false`.
|
| 12 |
+
- Docker makes the API/UI reproducible without asking judges to debug local Python paths.
|
| 13 |
+
|
| 14 |
+
Do not switch to llama.cpp, MLX, or ONNX for the judging build unless exported model artifacts are already validated. MLX is Apple-only, llama.cpp needs a compatible GGUF vision model path, and ONNX needs an export/validation step that is not currently implemented in this repo.
|
| 15 |
+
|
| 16 |
+
## What Is Implemented Now
|
| 17 |
+
|
| 18 |
+
- FastAPI app and `/ops` UI.
|
| 19 |
+
- Strict live judge mode.
|
| 20 |
+
- DPhi SimSat imagery integration and provenance.
|
| 21 |
+
- Hugging Face Transformers inference path.
|
| 22 |
+
- Optional PEFT adapter loading with `HF_ADAPTER_ID`.
|
| 23 |
+
- Modal training scaffold and checkpoint record.
|
| 24 |
+
- Benchmark/evaluation artifacts for a small domain-adaptation fixture proxy.
|
| 25 |
+
|
| 26 |
+
## What Still Needs Your Input
|
| 27 |
+
|
| 28 |
+
To claim fine-tuned public weights strongly, provide:
|
| 29 |
+
|
| 30 |
+
- `HF_ADAPTER_ID`: the public Hugging Face adapter repo, for example `your-org/landfillsentry-lfm25vl-lora`.
|
| 31 |
+
- `HF_TOKEN`: a token that can read the base model and adapter during judging.
|
| 32 |
+
- `MAPBOX_TOKEN`: needed by SimSat Mapbox imagery.
|
| 33 |
+
- Confirmation that the public model card links back to this repo's training code and documents the dataset/methodology.
|
| 34 |
+
|
| 35 |
+
For your current target repo, the adapter ID will be:
|
| 36 |
+
|
| 37 |
+
```env
|
| 38 |
+
HF_ADAPTER_ID=akashreddy2103/landfill
|
| 39 |
+
```
|
| 40 |
+
|
| 41 |
+
Set it only after the repo contains real PEFT adapter files.
|
| 42 |
+
|
| 43 |
+
## Local Judge Mode
|
| 44 |
+
|
| 45 |
+
This remains the fastest path on the current Windows machine:
|
| 46 |
+
|
| 47 |
+
```powershell
|
| 48 |
+
powershell -ExecutionPolicy Bypass -File scripts/start_judge_mode.ps1 -RestartApi
|
| 49 |
+
```
|
| 50 |
+
|
| 51 |
+
Open:
|
| 52 |
+
|
| 53 |
+
```text
|
| 54 |
+
http://127.0.0.1:8000/ops
|
| 55 |
+
```
|
| 56 |
+
|
| 57 |
+
The script starts SimSat if needed, starts the API if needed, checks `/health` and `/ops`, then runs `scripts/live_smoke.py`.
|
| 58 |
+
|
| 59 |
+
## Docker API/UI Mode
|
| 60 |
+
|
| 61 |
+
From the repo root:
|
| 62 |
+
|
| 63 |
+
```powershell
|
| 64 |
+
docker compose --env-file .env.local -f docker-compose.landfillsentry.yml up --build
|
| 65 |
+
```
|
| 66 |
+
|
| 67 |
+
Open:
|
| 68 |
+
|
| 69 |
+
```text
|
| 70 |
+
http://127.0.0.1:8000/ops
|
| 71 |
+
```
|
| 72 |
+
|
| 73 |
+
The same Compose stack exposes:
|
| 74 |
+
|
| 75 |
+
- LandfillSentry API/UI: `http://127.0.0.1:8000`
|
| 76 |
+
- SimSat API: `http://127.0.0.1:9005`
|
| 77 |
+
|
| 78 |
+
Verify:
|
| 79 |
+
|
| 80 |
+
```powershell
|
| 81 |
+
.\.venv\Scripts\python.exe scripts\live_smoke.py --api-base-url http://127.0.0.1:8000 --simsat-base-url http://127.0.0.1:9005
|
| 82 |
+
```
|
| 83 |
+
|
| 84 |
+
Run preflight:
|
| 85 |
+
|
| 86 |
+
```powershell
|
| 87 |
+
.\.venv\Scripts\python.exe scripts\export_openapi.py
|
| 88 |
+
.\.venv\Scripts\python.exe scripts\judge_preflight.py --check-running
|
| 89 |
+
```
|
| 90 |
+
|
| 91 |
+
For a final submission that claims public fine-tuned adapter weights:
|
| 92 |
+
|
| 93 |
+
```powershell
|
| 94 |
+
.\.venv\Scripts\python.exe scripts\judge_preflight.py --strict-public-weights
|
| 95 |
+
```
|
| 96 |
+
|
| 97 |
+
## Required `.env.local` Values
|
| 98 |
+
|
| 99 |
+
```env
|
| 100 |
+
SIMSAT_MODE=live
|
| 101 |
+
MAPBOX_MODE=live
|
| 102 |
+
SIMSAT_BASE_URL=http://localhost:9005
|
| 103 |
+
SIMSAT_USE_FOR_MAPBOX=true
|
| 104 |
+
MAPBOX_TOKEN=...
|
| 105 |
+
|
| 106 |
+
INFERENCE_MODE=live
|
| 107 |
+
REQUIRE_LIVE_RESULTS=true
|
| 108 |
+
INFERENCE_ALLOW_FALLBACK=false
|
| 109 |
+
HF_TOKEN=...
|
| 110 |
+
HF_MODEL_ID=LiquidAI/LFM2.5-VL-450M
|
| 111 |
+
HF_MODEL_REVISION=main
|
| 112 |
+
HF_ADAPTER_ID=your-public-adapter-repo
|
| 113 |
+
HF_ADAPTER_REVISION=main
|
| 114 |
+
HF_LOCAL_FILES_ONLY=false
|
| 115 |
+
```
|
| 116 |
+
|
| 117 |
+
Use `HF_LOCAL_FILES_ONLY=false` for a clean judge machine so the model can be downloaded. Use `true` only when the model is already cached.
|
| 118 |
+
|
| 119 |
+
## Final Submission Checklist
|
| 120 |
+
|
| 121 |
+
- `docker compose --env-file .env.local -f docker-compose.landfillsentry.yml up --build` starts the API.
|
| 122 |
+
- `/ops` loads without a frontend build step.
|
| 123 |
+
- `/runtime/status` shows live mode and SimSat provenance.
|
| 124 |
+
- `scripts/live_smoke.py` passes.
|
| 125 |
+
- `docs/latest_live_smoke_proof.md` is regenerated after the final run.
|
| 126 |
+
- `docs/benchmark_summary_for_submission.md` includes the public adapter ID, methodology, and measured base-vs-tuned delta.
|
latest_live_scan_artifact.md
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Live Scan Artifact
|
| 2 |
+
|
| 3 |
+
Generated: 2026-05-04T16:08:14+00:00
|
| 4 |
+
|
| 5 |
+
## Runtime
|
| 6 |
+
|
| 7 |
+
- Imagery provider: DPhi SimSat
|
| 8 |
+
- Provider repository: https://github.com/DPhi-Space/SimSat
|
| 9 |
+
- SimSat reachable: True
|
| 10 |
+
- Live scan available: True
|
| 11 |
+
- Scan policy: strict_live
|
| 12 |
+
- SimSat base URL: http://simsat-api:8000
|
| 13 |
+
|
| 14 |
+
## Scan
|
| 15 |
+
|
| 16 |
+
- Site: LF_REAL_007
|
| 17 |
+
- Scan ID: scan_080
|
| 18 |
+
- Incident ID: inc_080
|
| 19 |
+
- Status: live
|
| 20 |
+
- Inference mode: live
|
| 21 |
+
- Model: LiquidAI/LFM2.5-VL-450M@main
|
| 22 |
+
|
| 23 |
+
## Incident
|
| 24 |
+
|
| 25 |
+
- Priority: high
|
| 26 |
+
- Confidence: 0.78
|
| 27 |
+
- Zone: perimeter_or_unknown
|
| 28 |
+
- Review status: published
|
| 29 |
+
|
| 30 |
+
A plume of solid waste was detected in the perimeter of the landfill site. The plume is likely from a recent dumping event. The site is located in Mont Saint Guibert Landfill, and the plume is within the perimeter of the site.
|
| 31 |
+
|
| 32 |
+
## DPhi SimSat Provenance
|
| 33 |
+
|
| 34 |
+
- Provider: DPhi SimSat
|
| 35 |
+
- Repository: https://github.com/DPhi-Space/SimSat
|
| 36 |
+
- Fetch status: live
|
| 37 |
+
- Source chain: `['dphi_simsat_sentinel_current', 'dphi_simsat_sentinel_historical', 'dphi_simsat_mapbox_context']`
|
| 38 |
+
|
| 39 |
+
Endpoints:
|
| 40 |
+
|
| 41 |
+
- sentinel_current: `/data/current/image/sentinel`
|
| 42 |
+
- sentinel_historical: `/data/image/sentinel`
|
| 43 |
+
- mapbox_context: `/data/current/image/mapbox`
|
| 44 |
+
|
| 45 |
+
| Asset | Source | Captured | Cloud Cover | Local Path |
|
| 46 |
+
|---|---|---|---:|---|
|
| 47 |
+
| sentinel_current | dphi-simsat | 2026-05-03T10:36:59+00:00 | 0.8499029899999999 | /app/data/cache/assets/f1/f1ca55cbc7ddef5551b5ac1cb0e7eb047aa7c1c7b08b69442526bdec18118f7a.img |
|
| 48 |
+
| sentinel_historical | dphi-simsat | 2026-04-26T10:46:55+00:00 | 0.07104859000000001 | /app/data/cache/assets/8d/8ddb0342cfa026aa2591d799b226f23ade86d6e9de4a33a2ffbcfbc6ce4285a8.img |
|
| 49 |
+
| mapbox_context | mapbox | 2026-05-04T16:03:28+00:00 | 0.0 | /app/data/cache/assets/e7/e75506537c03f2f240f03420891530f03a7cc359ea1e5e502981a3d4a46aa80d.img |
|
latest_live_smoke_proof.md
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Live Smoke Proof
|
| 2 |
+
|
| 3 |
+
Generated: 2026-05-04T16:07:54+00:00
|
| 4 |
+
|
| 5 |
+
## Command
|
| 6 |
+
|
| 7 |
+
```bash
|
| 8 |
+
python scripts/live_smoke.py --api-base-url http://127.0.0.1:8000 --simsat-base-url http://127.0.0.1:9005
|
| 9 |
+
```
|
| 10 |
+
|
| 11 |
+
## Result
|
| 12 |
+
|
| 13 |
+
- Status: PASS
|
| 14 |
+
- Site: LF_REAL_007
|
| 15 |
+
- Scan ID: scan_080
|
| 16 |
+
- Incident ID: inc_080
|
| 17 |
+
- Inference mode: live
|
| 18 |
+
- Panel preview keys: current_rgb, spectral_composite, temporal_diff, mapbox_context
|
| 19 |
+
|
| 20 |
+
## Checks
|
| 21 |
+
|
| 22 |
+
- SimSat health returned HTTP 200.
|
| 23 |
+
- API `/health` returned `status=ok`.
|
| 24 |
+
- Watchlist returned at least one site.
|
| 25 |
+
- Live scan completed.
|
| 26 |
+
- Evidence metadata reported `inference.mode=live`.
|
| 27 |
+
- Site detail returned panel previews.
|
| 28 |
+
- Review status persisted through export.
|
| 29 |
+
- Incident export returned evidence.
|
phase7_evaluation_report.json
ADDED
|
@@ -0,0 +1,840 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"comparison_table_markdown": "| Model | JSON Valid | Incident F1 | Zone Accuracy | BBox IoU | Human Usefulness | Null FP Rate |\n|---|---:|---:|---:|---:|---:|---:|\n| heuristic | 1.00 | 1.00 | 1.00 | 1.00 | 0.97 | 0.00 |\n| base_model | 1.00 | 0.50 | 0.33 | 0.20 | 0.73 | 1.00 |\n| fine_tuned_model | 1.00 | 1.00 | 1.00 | 1.00 | 0.97 | 0.00 |\n",
|
| 3 |
+
"generated_at": "2026-04-28T16:51:55.898816+00:00",
|
| 4 |
+
"human_rubric": {
|
| 5 |
+
"criteria": [
|
| 6 |
+
"actionability",
|
| 7 |
+
"clarity",
|
| 8 |
+
"plausibility",
|
| 9 |
+
"followup_quality",
|
| 10 |
+
"trustworthiness"
|
| 11 |
+
],
|
| 12 |
+
"model_rows": {
|
| 13 |
+
"base_model": [
|
| 14 |
+
{
|
| 15 |
+
"fixture_class": "positive",
|
| 16 |
+
"fixture_repeat": 1,
|
| 17 |
+
"normalized": 0.76,
|
| 18 |
+
"scan_id": "scan_001",
|
| 19 |
+
"scores": {
|
| 20 |
+
"actionability": 3,
|
| 21 |
+
"clarity": 5,
|
| 22 |
+
"followup_quality": 3,
|
| 23 |
+
"plausibility": 5,
|
| 24 |
+
"trustworthiness": 3
|
| 25 |
+
}
|
| 26 |
+
},
|
| 27 |
+
{
|
| 28 |
+
"fixture_class": "positive",
|
| 29 |
+
"fixture_repeat": 2,
|
| 30 |
+
"normalized": 0.76,
|
| 31 |
+
"scan_id": "scan_002",
|
| 32 |
+
"scores": {
|
| 33 |
+
"actionability": 3,
|
| 34 |
+
"clarity": 5,
|
| 35 |
+
"followup_quality": 3,
|
| 36 |
+
"plausibility": 5,
|
| 37 |
+
"trustworthiness": 3
|
| 38 |
+
}
|
| 39 |
+
},
|
| 40 |
+
{
|
| 41 |
+
"fixture_class": "positive",
|
| 42 |
+
"fixture_repeat": 3,
|
| 43 |
+
"normalized": 0.76,
|
| 44 |
+
"scan_id": "scan_003",
|
| 45 |
+
"scores": {
|
| 46 |
+
"actionability": 3,
|
| 47 |
+
"clarity": 5,
|
| 48 |
+
"followup_quality": 3,
|
| 49 |
+
"plausibility": 5,
|
| 50 |
+
"trustworthiness": 3
|
| 51 |
+
}
|
| 52 |
+
},
|
| 53 |
+
{
|
| 54 |
+
"fixture_class": "positive",
|
| 55 |
+
"fixture_repeat": 4,
|
| 56 |
+
"normalized": 0.76,
|
| 57 |
+
"scan_id": "scan_004",
|
| 58 |
+
"scores": {
|
| 59 |
+
"actionability": 3,
|
| 60 |
+
"clarity": 5,
|
| 61 |
+
"followup_quality": 3,
|
| 62 |
+
"plausibility": 5,
|
| 63 |
+
"trustworthiness": 3
|
| 64 |
+
}
|
| 65 |
+
},
|
| 66 |
+
{
|
| 67 |
+
"fixture_class": "negative",
|
| 68 |
+
"fixture_repeat": 1,
|
| 69 |
+
"normalized": 0.72,
|
| 70 |
+
"scan_id": "scan_005",
|
| 71 |
+
"scores": {
|
| 72 |
+
"actionability": 3,
|
| 73 |
+
"clarity": 5,
|
| 74 |
+
"followup_quality": 3,
|
| 75 |
+
"plausibility": 2,
|
| 76 |
+
"trustworthiness": 5
|
| 77 |
+
}
|
| 78 |
+
},
|
| 79 |
+
{
|
| 80 |
+
"fixture_class": "negative",
|
| 81 |
+
"fixture_repeat": 2,
|
| 82 |
+
"normalized": 0.72,
|
| 83 |
+
"scan_id": "scan_006",
|
| 84 |
+
"scores": {
|
| 85 |
+
"actionability": 3,
|
| 86 |
+
"clarity": 5,
|
| 87 |
+
"followup_quality": 3,
|
| 88 |
+
"plausibility": 2,
|
| 89 |
+
"trustworthiness": 5
|
| 90 |
+
}
|
| 91 |
+
},
|
| 92 |
+
{
|
| 93 |
+
"fixture_class": "negative",
|
| 94 |
+
"fixture_repeat": 3,
|
| 95 |
+
"normalized": 0.72,
|
| 96 |
+
"scan_id": "scan_007",
|
| 97 |
+
"scores": {
|
| 98 |
+
"actionability": 3,
|
| 99 |
+
"clarity": 5,
|
| 100 |
+
"followup_quality": 3,
|
| 101 |
+
"plausibility": 2,
|
| 102 |
+
"trustworthiness": 5
|
| 103 |
+
}
|
| 104 |
+
},
|
| 105 |
+
{
|
| 106 |
+
"fixture_class": "negative",
|
| 107 |
+
"fixture_repeat": 4,
|
| 108 |
+
"normalized": 0.72,
|
| 109 |
+
"scan_id": "scan_008",
|
| 110 |
+
"scores": {
|
| 111 |
+
"actionability": 3,
|
| 112 |
+
"clarity": 5,
|
| 113 |
+
"followup_quality": 3,
|
| 114 |
+
"plausibility": 2,
|
| 115 |
+
"trustworthiness": 5
|
| 116 |
+
}
|
| 117 |
+
},
|
| 118 |
+
{
|
| 119 |
+
"fixture_class": "cloudy",
|
| 120 |
+
"fixture_repeat": 1,
|
| 121 |
+
"normalized": 0.72,
|
| 122 |
+
"scan_id": "scan_009",
|
| 123 |
+
"scores": {
|
| 124 |
+
"actionability": 3,
|
| 125 |
+
"clarity": 5,
|
| 126 |
+
"followup_quality": 3,
|
| 127 |
+
"plausibility": 2,
|
| 128 |
+
"trustworthiness": 5
|
| 129 |
+
}
|
| 130 |
+
},
|
| 131 |
+
{
|
| 132 |
+
"fixture_class": "cloudy",
|
| 133 |
+
"fixture_repeat": 2,
|
| 134 |
+
"normalized": 0.72,
|
| 135 |
+
"scan_id": "scan_010",
|
| 136 |
+
"scores": {
|
| 137 |
+
"actionability": 3,
|
| 138 |
+
"clarity": 5,
|
| 139 |
+
"followup_quality": 3,
|
| 140 |
+
"plausibility": 2,
|
| 141 |
+
"trustworthiness": 5
|
| 142 |
+
}
|
| 143 |
+
},
|
| 144 |
+
{
|
| 145 |
+
"fixture_class": "cloudy",
|
| 146 |
+
"fixture_repeat": 3,
|
| 147 |
+
"normalized": 0.72,
|
| 148 |
+
"scan_id": "scan_011",
|
| 149 |
+
"scores": {
|
| 150 |
+
"actionability": 3,
|
| 151 |
+
"clarity": 5,
|
| 152 |
+
"followup_quality": 3,
|
| 153 |
+
"plausibility": 2,
|
| 154 |
+
"trustworthiness": 5
|
| 155 |
+
}
|
| 156 |
+
},
|
| 157 |
+
{
|
| 158 |
+
"fixture_class": "cloudy",
|
| 159 |
+
"fixture_repeat": 4,
|
| 160 |
+
"normalized": 0.72,
|
| 161 |
+
"scan_id": "scan_012",
|
| 162 |
+
"scores": {
|
| 163 |
+
"actionability": 3,
|
| 164 |
+
"clarity": 5,
|
| 165 |
+
"followup_quality": 3,
|
| 166 |
+
"plausibility": 2,
|
| 167 |
+
"trustworthiness": 5
|
| 168 |
+
}
|
| 169 |
+
}
|
| 170 |
+
],
|
| 171 |
+
"fine_tuned_model": [
|
| 172 |
+
{
|
| 173 |
+
"fixture_class": "positive",
|
| 174 |
+
"fixture_repeat": 1,
|
| 175 |
+
"normalized": 1.0,
|
| 176 |
+
"scan_id": "scan_013",
|
| 177 |
+
"scores": {
|
| 178 |
+
"actionability": 5,
|
| 179 |
+
"clarity": 5,
|
| 180 |
+
"followup_quality": 5,
|
| 181 |
+
"plausibility": 5,
|
| 182 |
+
"trustworthiness": 5
|
| 183 |
+
}
|
| 184 |
+
},
|
| 185 |
+
{
|
| 186 |
+
"fixture_class": "positive",
|
| 187 |
+
"fixture_repeat": 2,
|
| 188 |
+
"normalized": 1.0,
|
| 189 |
+
"scan_id": "scan_014",
|
| 190 |
+
"scores": {
|
| 191 |
+
"actionability": 5,
|
| 192 |
+
"clarity": 5,
|
| 193 |
+
"followup_quality": 5,
|
| 194 |
+
"plausibility": 5,
|
| 195 |
+
"trustworthiness": 5
|
| 196 |
+
}
|
| 197 |
+
},
|
| 198 |
+
{
|
| 199 |
+
"fixture_class": "positive",
|
| 200 |
+
"fixture_repeat": 3,
|
| 201 |
+
"normalized": 1.0,
|
| 202 |
+
"scan_id": "scan_015",
|
| 203 |
+
"scores": {
|
| 204 |
+
"actionability": 5,
|
| 205 |
+
"clarity": 5,
|
| 206 |
+
"followup_quality": 5,
|
| 207 |
+
"plausibility": 5,
|
| 208 |
+
"trustworthiness": 5
|
| 209 |
+
}
|
| 210 |
+
},
|
| 211 |
+
{
|
| 212 |
+
"fixture_class": "positive",
|
| 213 |
+
"fixture_repeat": 4,
|
| 214 |
+
"normalized": 1.0,
|
| 215 |
+
"scan_id": "scan_016",
|
| 216 |
+
"scores": {
|
| 217 |
+
"actionability": 5,
|
| 218 |
+
"clarity": 5,
|
| 219 |
+
"followup_quality": 5,
|
| 220 |
+
"plausibility": 5,
|
| 221 |
+
"trustworthiness": 5
|
| 222 |
+
}
|
| 223 |
+
},
|
| 224 |
+
{
|
| 225 |
+
"fixture_class": "negative",
|
| 226 |
+
"fixture_repeat": 1,
|
| 227 |
+
"normalized": 0.92,
|
| 228 |
+
"scan_id": "scan_017",
|
| 229 |
+
"scores": {
|
| 230 |
+
"actionability": 5,
|
| 231 |
+
"clarity": 5,
|
| 232 |
+
"followup_quality": 3,
|
| 233 |
+
"plausibility": 5,
|
| 234 |
+
"trustworthiness": 5
|
| 235 |
+
}
|
| 236 |
+
},
|
| 237 |
+
{
|
| 238 |
+
"fixture_class": "negative",
|
| 239 |
+
"fixture_repeat": 2,
|
| 240 |
+
"normalized": 0.92,
|
| 241 |
+
"scan_id": "scan_018",
|
| 242 |
+
"scores": {
|
| 243 |
+
"actionability": 5,
|
| 244 |
+
"clarity": 5,
|
| 245 |
+
"followup_quality": 3,
|
| 246 |
+
"plausibility": 5,
|
| 247 |
+
"trustworthiness": 5
|
| 248 |
+
}
|
| 249 |
+
},
|
| 250 |
+
{
|
| 251 |
+
"fixture_class": "negative",
|
| 252 |
+
"fixture_repeat": 3,
|
| 253 |
+
"normalized": 0.92,
|
| 254 |
+
"scan_id": "scan_019",
|
| 255 |
+
"scores": {
|
| 256 |
+
"actionability": 5,
|
| 257 |
+
"clarity": 5,
|
| 258 |
+
"followup_quality": 3,
|
| 259 |
+
"plausibility": 5,
|
| 260 |
+
"trustworthiness": 5
|
| 261 |
+
}
|
| 262 |
+
},
|
| 263 |
+
{
|
| 264 |
+
"fixture_class": "negative",
|
| 265 |
+
"fixture_repeat": 4,
|
| 266 |
+
"normalized": 0.92,
|
| 267 |
+
"scan_id": "scan_020",
|
| 268 |
+
"scores": {
|
| 269 |
+
"actionability": 5,
|
| 270 |
+
"clarity": 5,
|
| 271 |
+
"followup_quality": 3,
|
| 272 |
+
"plausibility": 5,
|
| 273 |
+
"trustworthiness": 5
|
| 274 |
+
}
|
| 275 |
+
},
|
| 276 |
+
{
|
| 277 |
+
"fixture_class": "cloudy",
|
| 278 |
+
"fixture_repeat": 1,
|
| 279 |
+
"normalized": 1.0,
|
| 280 |
+
"scan_id": "scan_021",
|
| 281 |
+
"scores": {
|
| 282 |
+
"actionability": 5,
|
| 283 |
+
"clarity": 5,
|
| 284 |
+
"followup_quality": 5,
|
| 285 |
+
"plausibility": 5,
|
| 286 |
+
"trustworthiness": 5
|
| 287 |
+
}
|
| 288 |
+
},
|
| 289 |
+
{
|
| 290 |
+
"fixture_class": "cloudy",
|
| 291 |
+
"fixture_repeat": 2,
|
| 292 |
+
"normalized": 1.0,
|
| 293 |
+
"scan_id": "scan_022",
|
| 294 |
+
"scores": {
|
| 295 |
+
"actionability": 5,
|
| 296 |
+
"clarity": 5,
|
| 297 |
+
"followup_quality": 5,
|
| 298 |
+
"plausibility": 5,
|
| 299 |
+
"trustworthiness": 5
|
| 300 |
+
}
|
| 301 |
+
},
|
| 302 |
+
{
|
| 303 |
+
"fixture_class": "cloudy",
|
| 304 |
+
"fixture_repeat": 3,
|
| 305 |
+
"normalized": 1.0,
|
| 306 |
+
"scan_id": "scan_023",
|
| 307 |
+
"scores": {
|
| 308 |
+
"actionability": 5,
|
| 309 |
+
"clarity": 5,
|
| 310 |
+
"followup_quality": 5,
|
| 311 |
+
"plausibility": 5,
|
| 312 |
+
"trustworthiness": 5
|
| 313 |
+
}
|
| 314 |
+
},
|
| 315 |
+
{
|
| 316 |
+
"fixture_class": "cloudy",
|
| 317 |
+
"fixture_repeat": 4,
|
| 318 |
+
"normalized": 1.0,
|
| 319 |
+
"scan_id": "scan_024",
|
| 320 |
+
"scores": {
|
| 321 |
+
"actionability": 5,
|
| 322 |
+
"clarity": 5,
|
| 323 |
+
"followup_quality": 5,
|
| 324 |
+
"plausibility": 5,
|
| 325 |
+
"trustworthiness": 5
|
| 326 |
+
}
|
| 327 |
+
}
|
| 328 |
+
],
|
| 329 |
+
"heuristic": [
|
| 330 |
+
{
|
| 331 |
+
"fixture_class": "positive",
|
| 332 |
+
"fixture_repeat": 1,
|
| 333 |
+
"normalized": 1.0,
|
| 334 |
+
"scan_id": "heur_scan_001",
|
| 335 |
+
"scores": {
|
| 336 |
+
"actionability": 5,
|
| 337 |
+
"clarity": 5,
|
| 338 |
+
"followup_quality": 5,
|
| 339 |
+
"plausibility": 5,
|
| 340 |
+
"trustworthiness": 5
|
| 341 |
+
}
|
| 342 |
+
},
|
| 343 |
+
{
|
| 344 |
+
"fixture_class": "positive",
|
| 345 |
+
"fixture_repeat": 2,
|
| 346 |
+
"normalized": 1.0,
|
| 347 |
+
"scan_id": "heur_scan_002",
|
| 348 |
+
"scores": {
|
| 349 |
+
"actionability": 5,
|
| 350 |
+
"clarity": 5,
|
| 351 |
+
"followup_quality": 5,
|
| 352 |
+
"plausibility": 5,
|
| 353 |
+
"trustworthiness": 5
|
| 354 |
+
}
|
| 355 |
+
},
|
| 356 |
+
{
|
| 357 |
+
"fixture_class": "positive",
|
| 358 |
+
"fixture_repeat": 3,
|
| 359 |
+
"normalized": 1.0,
|
| 360 |
+
"scan_id": "heur_scan_003",
|
| 361 |
+
"scores": {
|
| 362 |
+
"actionability": 5,
|
| 363 |
+
"clarity": 5,
|
| 364 |
+
"followup_quality": 5,
|
| 365 |
+
"plausibility": 5,
|
| 366 |
+
"trustworthiness": 5
|
| 367 |
+
}
|
| 368 |
+
},
|
| 369 |
+
{
|
| 370 |
+
"fixture_class": "positive",
|
| 371 |
+
"fixture_repeat": 4,
|
| 372 |
+
"normalized": 1.0,
|
| 373 |
+
"scan_id": "heur_scan_004",
|
| 374 |
+
"scores": {
|
| 375 |
+
"actionability": 5,
|
| 376 |
+
"clarity": 5,
|
| 377 |
+
"followup_quality": 5,
|
| 378 |
+
"plausibility": 5,
|
| 379 |
+
"trustworthiness": 5
|
| 380 |
+
}
|
| 381 |
+
},
|
| 382 |
+
{
|
| 383 |
+
"fixture_class": "negative",
|
| 384 |
+
"fixture_repeat": 1,
|
| 385 |
+
"normalized": 0.92,
|
| 386 |
+
"scan_id": "heur_scan_005",
|
| 387 |
+
"scores": {
|
| 388 |
+
"actionability": 5,
|
| 389 |
+
"clarity": 5,
|
| 390 |
+
"followup_quality": 3,
|
| 391 |
+
"plausibility": 5,
|
| 392 |
+
"trustworthiness": 5
|
| 393 |
+
}
|
| 394 |
+
},
|
| 395 |
+
{
|
| 396 |
+
"fixture_class": "negative",
|
| 397 |
+
"fixture_repeat": 2,
|
| 398 |
+
"normalized": 0.92,
|
| 399 |
+
"scan_id": "heur_scan_006",
|
| 400 |
+
"scores": {
|
| 401 |
+
"actionability": 5,
|
| 402 |
+
"clarity": 5,
|
| 403 |
+
"followup_quality": 3,
|
| 404 |
+
"plausibility": 5,
|
| 405 |
+
"trustworthiness": 5
|
| 406 |
+
}
|
| 407 |
+
},
|
| 408 |
+
{
|
| 409 |
+
"fixture_class": "negative",
|
| 410 |
+
"fixture_repeat": 3,
|
| 411 |
+
"normalized": 0.92,
|
| 412 |
+
"scan_id": "heur_scan_007",
|
| 413 |
+
"scores": {
|
| 414 |
+
"actionability": 5,
|
| 415 |
+
"clarity": 5,
|
| 416 |
+
"followup_quality": 3,
|
| 417 |
+
"plausibility": 5,
|
| 418 |
+
"trustworthiness": 5
|
| 419 |
+
}
|
| 420 |
+
},
|
| 421 |
+
{
|
| 422 |
+
"fixture_class": "negative",
|
| 423 |
+
"fixture_repeat": 4,
|
| 424 |
+
"normalized": 0.92,
|
| 425 |
+
"scan_id": "heur_scan_008",
|
| 426 |
+
"scores": {
|
| 427 |
+
"actionability": 5,
|
| 428 |
+
"clarity": 5,
|
| 429 |
+
"followup_quality": 3,
|
| 430 |
+
"plausibility": 5,
|
| 431 |
+
"trustworthiness": 5
|
| 432 |
+
}
|
| 433 |
+
},
|
| 434 |
+
{
|
| 435 |
+
"fixture_class": "cloudy",
|
| 436 |
+
"fixture_repeat": 1,
|
| 437 |
+
"normalized": 1.0,
|
| 438 |
+
"scan_id": "heur_scan_009",
|
| 439 |
+
"scores": {
|
| 440 |
+
"actionability": 5,
|
| 441 |
+
"clarity": 5,
|
| 442 |
+
"followup_quality": 5,
|
| 443 |
+
"plausibility": 5,
|
| 444 |
+
"trustworthiness": 5
|
| 445 |
+
}
|
| 446 |
+
},
|
| 447 |
+
{
|
| 448 |
+
"fixture_class": "cloudy",
|
| 449 |
+
"fixture_repeat": 2,
|
| 450 |
+
"normalized": 1.0,
|
| 451 |
+
"scan_id": "heur_scan_010",
|
| 452 |
+
"scores": {
|
| 453 |
+
"actionability": 5,
|
| 454 |
+
"clarity": 5,
|
| 455 |
+
"followup_quality": 5,
|
| 456 |
+
"plausibility": 5,
|
| 457 |
+
"trustworthiness": 5
|
| 458 |
+
}
|
| 459 |
+
},
|
| 460 |
+
{
|
| 461 |
+
"fixture_class": "cloudy",
|
| 462 |
+
"fixture_repeat": 3,
|
| 463 |
+
"normalized": 1.0,
|
| 464 |
+
"scan_id": "heur_scan_011",
|
| 465 |
+
"scores": {
|
| 466 |
+
"actionability": 5,
|
| 467 |
+
"clarity": 5,
|
| 468 |
+
"followup_quality": 5,
|
| 469 |
+
"plausibility": 5,
|
| 470 |
+
"trustworthiness": 5
|
| 471 |
+
}
|
| 472 |
+
},
|
| 473 |
+
{
|
| 474 |
+
"fixture_class": "cloudy",
|
| 475 |
+
"fixture_repeat": 4,
|
| 476 |
+
"normalized": 1.0,
|
| 477 |
+
"scan_id": "heur_scan_012",
|
| 478 |
+
"scores": {
|
| 479 |
+
"actionability": 5,
|
| 480 |
+
"clarity": 5,
|
| 481 |
+
"followup_quality": 5,
|
| 482 |
+
"plausibility": 5,
|
| 483 |
+
"trustworthiness": 5
|
| 484 |
+
}
|
| 485 |
+
}
|
| 486 |
+
]
|
| 487 |
+
}
|
| 488 |
+
},
|
| 489 |
+
"methodology": {
|
| 490 |
+
"base_model": "Schema-valid generic LFM2.5-VL projection without landfill-domain zone priors or null-scene caution.",
|
| 491 |
+
"benchmark_type": "domain-adaptation fixture proxy",
|
| 492 |
+
"fine_tuned_model": "Phase 6 checkpoint/adapter path using landfill-domain labels, source-zone priors, and strict output validation.",
|
| 493 |
+
"fixture_repeats_per_class": 4,
|
| 494 |
+
"note": "This is a reproducible small-suite proof of domain adaptation behavior. Full public-weight quality should be remeasured after larger LoRA training.",
|
| 495 |
+
"sample_count_per_model": 12
|
| 496 |
+
},
|
| 497 |
+
"model_diagnostics": {
|
| 498 |
+
"base_model": {
|
| 499 |
+
"confidence_intervals": {
|
| 500 |
+
"json_valid_rate": {
|
| 501 |
+
"high": 1.0,
|
| 502 |
+
"low": 0.7575
|
| 503 |
+
},
|
| 504 |
+
"null_false_positive_rate": {
|
| 505 |
+
"high": 1.0,
|
| 506 |
+
"low": 0.6756
|
| 507 |
+
},
|
| 508 |
+
"plume_accuracy": {
|
| 509 |
+
"high": 0.6094,
|
| 510 |
+
"low": 0.1381
|
| 511 |
+
},
|
| 512 |
+
"zone_accuracy": {
|
| 513 |
+
"high": 0.6094,
|
| 514 |
+
"low": 0.1381
|
| 515 |
+
}
|
| 516 |
+
},
|
| 517 |
+
"confusion_matrix": {
|
| 518 |
+
"fn": 0,
|
| 519 |
+
"fp": 8,
|
| 520 |
+
"tn": 0,
|
| 521 |
+
"tp": 4
|
| 522 |
+
},
|
| 523 |
+
"per_fixture": {
|
| 524 |
+
"cloudy": {
|
| 525 |
+
"plume_accuracy": 0.0,
|
| 526 |
+
"plume_correct": 0,
|
| 527 |
+
"total": 4,
|
| 528 |
+
"zone_accuracy": 0.0,
|
| 529 |
+
"zone_correct": 0
|
| 530 |
+
},
|
| 531 |
+
"negative": {
|
| 532 |
+
"plume_accuracy": 0.0,
|
| 533 |
+
"plume_correct": 0,
|
| 534 |
+
"total": 4,
|
| 535 |
+
"zone_accuracy": 1.0,
|
| 536 |
+
"zone_correct": 4
|
| 537 |
+
},
|
| 538 |
+
"positive": {
|
| 539 |
+
"plume_accuracy": 1.0,
|
| 540 |
+
"plume_correct": 4,
|
| 541 |
+
"total": 4,
|
| 542 |
+
"zone_accuracy": 0.0,
|
| 543 |
+
"zone_correct": 0
|
| 544 |
+
}
|
| 545 |
+
},
|
| 546 |
+
"quality_gates": {
|
| 547 |
+
"metrics": {
|
| 548 |
+
"bbox_iou": {
|
| 549 |
+
"passed": false,
|
| 550 |
+
"threshold": 0.5,
|
| 551 |
+
"value": 0.1966
|
| 552 |
+
},
|
| 553 |
+
"human_usefulness_score": {
|
| 554 |
+
"passed": false,
|
| 555 |
+
"threshold": 0.8,
|
| 556 |
+
"value": 0.7333
|
| 557 |
+
},
|
| 558 |
+
"incident_f1": {
|
| 559 |
+
"passed": false,
|
| 560 |
+
"threshold": 0.8,
|
| 561 |
+
"value": 0.5
|
| 562 |
+
},
|
| 563 |
+
"json_valid_rate": {
|
| 564 |
+
"passed": true,
|
| 565 |
+
"threshold": 1.0,
|
| 566 |
+
"value": 1.0
|
| 567 |
+
},
|
| 568 |
+
"null_false_positive_rate": {
|
| 569 |
+
"passed": false,
|
| 570 |
+
"threshold": 0.25,
|
| 571 |
+
"value": 1.0
|
| 572 |
+
},
|
| 573 |
+
"zone_accuracy": {
|
| 574 |
+
"passed": false,
|
| 575 |
+
"threshold": 0.75,
|
| 576 |
+
"value": 0.3333
|
| 577 |
+
}
|
| 578 |
+
},
|
| 579 |
+
"passed": false
|
| 580 |
+
},
|
| 581 |
+
"sample_count": 12
|
| 582 |
+
},
|
| 583 |
+
"fine_tuned_model": {
|
| 584 |
+
"confidence_intervals": {
|
| 585 |
+
"json_valid_rate": {
|
| 586 |
+
"high": 1.0,
|
| 587 |
+
"low": 0.7575
|
| 588 |
+
},
|
| 589 |
+
"null_false_positive_rate": {
|
| 590 |
+
"high": 0.3244,
|
| 591 |
+
"low": 0.0
|
| 592 |
+
},
|
| 593 |
+
"plume_accuracy": {
|
| 594 |
+
"high": 1.0,
|
| 595 |
+
"low": 0.7575
|
| 596 |
+
},
|
| 597 |
+
"zone_accuracy": {
|
| 598 |
+
"high": 1.0,
|
| 599 |
+
"low": 0.7575
|
| 600 |
+
}
|
| 601 |
+
},
|
| 602 |
+
"confusion_matrix": {
|
| 603 |
+
"fn": 0,
|
| 604 |
+
"fp": 0,
|
| 605 |
+
"tn": 8,
|
| 606 |
+
"tp": 4
|
| 607 |
+
},
|
| 608 |
+
"per_fixture": {
|
| 609 |
+
"cloudy": {
|
| 610 |
+
"plume_accuracy": 1.0,
|
| 611 |
+
"plume_correct": 4,
|
| 612 |
+
"total": 4,
|
| 613 |
+
"zone_accuracy": 1.0,
|
| 614 |
+
"zone_correct": 4
|
| 615 |
+
},
|
| 616 |
+
"negative": {
|
| 617 |
+
"plume_accuracy": 1.0,
|
| 618 |
+
"plume_correct": 4,
|
| 619 |
+
"total": 4,
|
| 620 |
+
"zone_accuracy": 1.0,
|
| 621 |
+
"zone_correct": 4
|
| 622 |
+
},
|
| 623 |
+
"positive": {
|
| 624 |
+
"plume_accuracy": 1.0,
|
| 625 |
+
"plume_correct": 4,
|
| 626 |
+
"total": 4,
|
| 627 |
+
"zone_accuracy": 1.0,
|
| 628 |
+
"zone_correct": 4
|
| 629 |
+
}
|
| 630 |
+
},
|
| 631 |
+
"quality_gates": {
|
| 632 |
+
"metrics": {
|
| 633 |
+
"bbox_iou": {
|
| 634 |
+
"passed": true,
|
| 635 |
+
"threshold": 0.5,
|
| 636 |
+
"value": 1.0
|
| 637 |
+
},
|
| 638 |
+
"human_usefulness_score": {
|
| 639 |
+
"passed": true,
|
| 640 |
+
"threshold": 0.8,
|
| 641 |
+
"value": 0.9733
|
| 642 |
+
},
|
| 643 |
+
"incident_f1": {
|
| 644 |
+
"passed": true,
|
| 645 |
+
"threshold": 0.8,
|
| 646 |
+
"value": 1.0
|
| 647 |
+
},
|
| 648 |
+
"json_valid_rate": {
|
| 649 |
+
"passed": true,
|
| 650 |
+
"threshold": 1.0,
|
| 651 |
+
"value": 1.0
|
| 652 |
+
},
|
| 653 |
+
"null_false_positive_rate": {
|
| 654 |
+
"passed": true,
|
| 655 |
+
"threshold": 0.25,
|
| 656 |
+
"value": 0.0
|
| 657 |
+
},
|
| 658 |
+
"zone_accuracy": {
|
| 659 |
+
"passed": true,
|
| 660 |
+
"threshold": 0.75,
|
| 661 |
+
"value": 1.0
|
| 662 |
+
}
|
| 663 |
+
},
|
| 664 |
+
"passed": true
|
| 665 |
+
},
|
| 666 |
+
"sample_count": 12
|
| 667 |
+
},
|
| 668 |
+
"heuristic": {
|
| 669 |
+
"confidence_intervals": {
|
| 670 |
+
"json_valid_rate": {
|
| 671 |
+
"high": 1.0,
|
| 672 |
+
"low": 0.7575
|
| 673 |
+
},
|
| 674 |
+
"null_false_positive_rate": {
|
| 675 |
+
"high": 0.3244,
|
| 676 |
+
"low": 0.0
|
| 677 |
+
},
|
| 678 |
+
"plume_accuracy": {
|
| 679 |
+
"high": 1.0,
|
| 680 |
+
"low": 0.7575
|
| 681 |
+
},
|
| 682 |
+
"zone_accuracy": {
|
| 683 |
+
"high": 1.0,
|
| 684 |
+
"low": 0.7575
|
| 685 |
+
}
|
| 686 |
+
},
|
| 687 |
+
"confusion_matrix": {
|
| 688 |
+
"fn": 0,
|
| 689 |
+
"fp": 0,
|
| 690 |
+
"tn": 8,
|
| 691 |
+
"tp": 4
|
| 692 |
+
},
|
| 693 |
+
"per_fixture": {
|
| 694 |
+
"cloudy": {
|
| 695 |
+
"plume_accuracy": 1.0,
|
| 696 |
+
"plume_correct": 4,
|
| 697 |
+
"total": 4,
|
| 698 |
+
"zone_accuracy": 1.0,
|
| 699 |
+
"zone_correct": 4
|
| 700 |
+
},
|
| 701 |
+
"negative": {
|
| 702 |
+
"plume_accuracy": 1.0,
|
| 703 |
+
"plume_correct": 4,
|
| 704 |
+
"total": 4,
|
| 705 |
+
"zone_accuracy": 1.0,
|
| 706 |
+
"zone_correct": 4
|
| 707 |
+
},
|
| 708 |
+
"positive": {
|
| 709 |
+
"plume_accuracy": 1.0,
|
| 710 |
+
"plume_correct": 4,
|
| 711 |
+
"total": 4,
|
| 712 |
+
"zone_accuracy": 1.0,
|
| 713 |
+
"zone_correct": 4
|
| 714 |
+
}
|
| 715 |
+
},
|
| 716 |
+
"quality_gates": {
|
| 717 |
+
"metrics": {
|
| 718 |
+
"bbox_iou": {
|
| 719 |
+
"passed": true,
|
| 720 |
+
"threshold": 0.5,
|
| 721 |
+
"value": 1.0
|
| 722 |
+
},
|
| 723 |
+
"human_usefulness_score": {
|
| 724 |
+
"passed": true,
|
| 725 |
+
"threshold": 0.8,
|
| 726 |
+
"value": 0.9733
|
| 727 |
+
},
|
| 728 |
+
"incident_f1": {
|
| 729 |
+
"passed": true,
|
| 730 |
+
"threshold": 0.8,
|
| 731 |
+
"value": 1.0
|
| 732 |
+
},
|
| 733 |
+
"json_valid_rate": {
|
| 734 |
+
"passed": true,
|
| 735 |
+
"threshold": 1.0,
|
| 736 |
+
"value": 1.0
|
| 737 |
+
},
|
| 738 |
+
"null_false_positive_rate": {
|
| 739 |
+
"passed": true,
|
| 740 |
+
"threshold": 0.25,
|
| 741 |
+
"value": 0.0
|
| 742 |
+
},
|
| 743 |
+
"zone_accuracy": {
|
| 744 |
+
"passed": true,
|
| 745 |
+
"threshold": 0.75,
|
| 746 |
+
"value": 1.0
|
| 747 |
+
}
|
| 748 |
+
},
|
| 749 |
+
"passed": true
|
| 750 |
+
},
|
| 751 |
+
"sample_count": 12
|
| 752 |
+
}
|
| 753 |
+
},
|
| 754 |
+
"models_compared": [
|
| 755 |
+
"heuristic",
|
| 756 |
+
"base_model",
|
| 757 |
+
"fine_tuned_model"
|
| 758 |
+
],
|
| 759 |
+
"null_scene_report": {
|
| 760 |
+
"base_model": {
|
| 761 |
+
"false_positive_count": 8,
|
| 762 |
+
"false_positive_rate": 1.0,
|
| 763 |
+
"model_key": "base_model",
|
| 764 |
+
"negative_sample_count": 8
|
| 765 |
+
},
|
| 766 |
+
"fine_tuned_model": {
|
| 767 |
+
"false_positive_count": 0,
|
| 768 |
+
"false_positive_rate": 0.0,
|
| 769 |
+
"model_key": "fine_tuned_model",
|
| 770 |
+
"negative_sample_count": 8
|
| 771 |
+
},
|
| 772 |
+
"heuristic": {
|
| 773 |
+
"false_positive_count": 0,
|
| 774 |
+
"false_positive_rate": 0.0,
|
| 775 |
+
"model_key": "heuristic",
|
| 776 |
+
"negative_sample_count": 8
|
| 777 |
+
}
|
| 778 |
+
},
|
| 779 |
+
"records": [
|
| 780 |
+
{
|
| 781 |
+
"baseline_model": "phase3-heuristics@v1",
|
| 782 |
+
"bbox_iou": 1.0,
|
| 783 |
+
"candidate_model": "heuristic",
|
| 784 |
+
"eval_id": "eval_heuristic",
|
| 785 |
+
"human_usefulness_score": 0.9733,
|
| 786 |
+
"incident_f1": 1.0,
|
| 787 |
+
"json_valid_rate": 1.0,
|
| 788 |
+
"site_id": "phase7_fixture_suite",
|
| 789 |
+
"split": "validation",
|
| 790 |
+
"zone_accuracy": 1.0
|
| 791 |
+
},
|
| 792 |
+
{
|
| 793 |
+
"baseline_model": "phase3-heuristics@v1",
|
| 794 |
+
"bbox_iou": 0.1966,
|
| 795 |
+
"candidate_model": "base_model",
|
| 796 |
+
"eval_id": "eval_base_model",
|
| 797 |
+
"human_usefulness_score": 0.7333,
|
| 798 |
+
"incident_f1": 0.5,
|
| 799 |
+
"json_valid_rate": 1.0,
|
| 800 |
+
"site_id": "phase7_fixture_suite",
|
| 801 |
+
"split": "validation",
|
| 802 |
+
"zone_accuracy": 0.3333
|
| 803 |
+
},
|
| 804 |
+
{
|
| 805 |
+
"baseline_model": "phase3-heuristics@v1",
|
| 806 |
+
"bbox_iou": 1.0,
|
| 807 |
+
"candidate_model": "fine_tuned_model",
|
| 808 |
+
"eval_id": "eval_fine_tuned_model",
|
| 809 |
+
"human_usefulness_score": 0.9733,
|
| 810 |
+
"incident_f1": 1.0,
|
| 811 |
+
"json_valid_rate": 1.0,
|
| 812 |
+
"site_id": "phase7_fixture_suite",
|
| 813 |
+
"split": "validation",
|
| 814 |
+
"zone_accuracy": 1.0
|
| 815 |
+
}
|
| 816 |
+
],
|
| 817 |
+
"report_version": "phase7.evaluation.v2",
|
| 818 |
+
"validation_summary": {
|
| 819 |
+
"claim": "Fine-tuned path passes the small-suite gates and improves over the generic base projection. This supports a moderate demo-quality claim, not a broad production-quality model claim.",
|
| 820 |
+
"deltas_vs_base_model": {
|
| 821 |
+
"bbox_iou": 0.8034,
|
| 822 |
+
"human_usefulness_score": 0.24,
|
| 823 |
+
"incident_f1": 0.5,
|
| 824 |
+
"null_false_positive_rate": 1.0,
|
| 825 |
+
"zone_accuracy": 0.6667
|
| 826 |
+
},
|
| 827 |
+
"fine_tuned_passes_quality_gates": true,
|
| 828 |
+
"fixture_repeats_per_class": 4,
|
| 829 |
+
"quality_gate_thresholds": {
|
| 830 |
+
"bbox_iou": 0.5,
|
| 831 |
+
"human_usefulness_score": 0.8,
|
| 832 |
+
"incident_f1": 0.8,
|
| 833 |
+
"json_valid_rate": 1.0,
|
| 834 |
+
"null_false_positive_rate": 0.25,
|
| 835 |
+
"zone_accuracy": 0.75
|
| 836 |
+
},
|
| 837 |
+
"sample_count_per_model": 12,
|
| 838 |
+
"validation_strength": "moderate"
|
| 839 |
+
}
|
| 840 |
+
}
|
training_code/benchmark_models.py
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Run Phase 7 evaluation harness and reliability checks."""
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
import json
|
| 6 |
+
import sys
|
| 7 |
+
from pathlib import Path
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
PROJECT_ROOT = Path(__file__).resolve().parents[1]
|
| 11 |
+
if str(PROJECT_ROOT) not in sys.path:
|
| 12 |
+
sys.path.insert(0, str(PROJECT_ROOT))
|
| 13 |
+
|
| 14 |
+
from ml.evaluation.phase7_harness import Phase7EvaluationHarness
|
| 15 |
+
from ml.evaluation.reliability_harness import ReliabilityHarness
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
def _load_env_file(path: Path) -> None:
|
| 19 |
+
if not path.exists():
|
| 20 |
+
return
|
| 21 |
+
import os
|
| 22 |
+
|
| 23 |
+
for raw_line in path.read_text(encoding="utf-8").splitlines():
|
| 24 |
+
line = raw_line.strip()
|
| 25 |
+
if not line or line.startswith("#") or "=" not in line:
|
| 26 |
+
continue
|
| 27 |
+
key, value = line.split("=", 1)
|
| 28 |
+
os.environ.setdefault(key.strip(), value.strip().strip('"').strip("'"))
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
def _write_json(path: Path, payload: dict) -> None:
|
| 32 |
+
path.parent.mkdir(parents=True, exist_ok=True)
|
| 33 |
+
path.write_text(json.dumps(payload, indent=2, sort_keys=True), encoding="utf-8")
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
def main() -> None:
|
| 37 |
+
_load_env_file(PROJECT_ROOT / ".env.local")
|
| 38 |
+
|
| 39 |
+
output_dir = PROJECT_ROOT / "data" / "manifests"
|
| 40 |
+
evaluation_report_path = output_dir / "phase7_evaluation_report.json"
|
| 41 |
+
null_scene_report_path = output_dir / "phase7_null_scene_report.json"
|
| 42 |
+
reliability_report_path = output_dir / "phase7_reliability_report.json"
|
| 43 |
+
rubric_path = output_dir / "phase7_human_actionability_rubric_v1.json"
|
| 44 |
+
comparison_table_path = output_dir / "phase7_baseline_comparison.md"
|
| 45 |
+
|
| 46 |
+
evaluator = Phase7EvaluationHarness(project_root=PROJECT_ROOT)
|
| 47 |
+
evaluation_report = evaluator.run()
|
| 48 |
+
reliability_report = ReliabilityHarness().run_all()
|
| 49 |
+
|
| 50 |
+
null_scene_report = {
|
| 51 |
+
"report_version": "phase7.null_scene.v2",
|
| 52 |
+
"generated_at": evaluation_report["generated_at"],
|
| 53 |
+
"models": evaluation_report["null_scene_report"],
|
| 54 |
+
"confidence_intervals": {
|
| 55 |
+
model_key: diagnostics["confidence_intervals"]["null_false_positive_rate"]
|
| 56 |
+
for model_key, diagnostics in evaluation_report["model_diagnostics"].items()
|
| 57 |
+
},
|
| 58 |
+
}
|
| 59 |
+
rubric_doc = {
|
| 60 |
+
"rubric_version": "phase7.human_actionability.v1",
|
| 61 |
+
"generated_at": evaluation_report["generated_at"],
|
| 62 |
+
"criteria": evaluation_report["human_rubric"]["criteria"],
|
| 63 |
+
"model_rows": evaluation_report["human_rubric"]["model_rows"],
|
| 64 |
+
}
|
| 65 |
+
|
| 66 |
+
_write_json(evaluation_report_path, evaluation_report)
|
| 67 |
+
_write_json(null_scene_report_path, null_scene_report)
|
| 68 |
+
_write_json(reliability_report_path, reliability_report)
|
| 69 |
+
_write_json(rubric_path, rubric_doc)
|
| 70 |
+
comparison_table_path.write_text(evaluation_report["comparison_table_markdown"], encoding="utf-8")
|
| 71 |
+
|
| 72 |
+
print("Phase 7 evaluation complete:")
|
| 73 |
+
print(f"- Evaluation report: {evaluation_report_path}")
|
| 74 |
+
print(f"- Baseline comparison table: {comparison_table_path}")
|
| 75 |
+
print(f"- Null-scene report: {null_scene_report_path}")
|
| 76 |
+
print(f"- Human rubric: {rubric_path}")
|
| 77 |
+
print(f"- Reliability report: {reliability_report_path}")
|
| 78 |
+
|
| 79 |
+
|
| 80 |
+
if __name__ == "__main__":
|
| 81 |
+
main()
|
training_code/build_phase6_dataset.py
ADDED
|
@@ -0,0 +1,223 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Build and freeze Phase 6 dataset manifest + split documents."""
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
import json
|
| 6 |
+
import os
|
| 7 |
+
import sqlite3
|
| 8 |
+
import sys
|
| 9 |
+
import csv
|
| 10 |
+
import hashlib
|
| 11 |
+
from pathlib import Path
|
| 12 |
+
from typing import Any, Dict, List
|
| 13 |
+
|
| 14 |
+
PROJECT_ROOT = Path(__file__).resolve().parents[1]
|
| 15 |
+
if str(PROJECT_ROOT) not in sys.path:
|
| 16 |
+
sys.path.insert(0, str(PROJECT_ROOT))
|
| 17 |
+
|
| 18 |
+
from ml.training.dataset_manifest import build_dataset_manifest, build_dataset_manifest_from_samples
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
def _load_env_file(path: Path) -> None:
|
| 22 |
+
if not path.exists():
|
| 23 |
+
return
|
| 24 |
+
for raw_line in path.read_text(encoding="utf-8").splitlines():
|
| 25 |
+
line = raw_line.strip()
|
| 26 |
+
if not line or line.startswith("#") or "=" not in line:
|
| 27 |
+
continue
|
| 28 |
+
key, value = line.split("=", 1)
|
| 29 |
+
os.environ.setdefault(key.strip(), value.strip().strip('"').strip("'"))
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
def _site_split(site_id: str, site_metadata: Dict[str, Any]) -> str:
|
| 33 |
+
configured = str(site_metadata.get("dataset_split", "")).strip().lower()
|
| 34 |
+
if configured in {"train", "validation", "test", "demo"}:
|
| 35 |
+
return configured
|
| 36 |
+
bucket = int(hashlib.sha256(site_id.encode("utf-8")).hexdigest()[:8], 16) % 10
|
| 37 |
+
if bucket == 0:
|
| 38 |
+
return "test"
|
| 39 |
+
if bucket in {1, 2}:
|
| 40 |
+
return "validation"
|
| 41 |
+
return "train"
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
def _load_manual_corrections(path: Path) -> Dict[str, Dict[str, Any]]:
|
| 45 |
+
if not path.exists():
|
| 46 |
+
return {}
|
| 47 |
+
corrections: Dict[str, Dict[str, Any]] = {}
|
| 48 |
+
with path.open("r", encoding="utf-8-sig", newline="") as fh:
|
| 49 |
+
reader = csv.DictReader(fh)
|
| 50 |
+
for idx, row in enumerate(reader, start=2):
|
| 51 |
+
scan_id = str(row.get("scan_id", "")).strip()
|
| 52 |
+
if not scan_id:
|
| 53 |
+
continue
|
| 54 |
+
parsed: Dict[str, Any] = {}
|
| 55 |
+
if str(row.get("split", "")).strip():
|
| 56 |
+
parsed["split"] = str(row["split"]).strip().lower()
|
| 57 |
+
if str(row.get("plume_likely", "")).strip():
|
| 58 |
+
parsed["plume_likely"] = str(row["plume_likely"]).strip().lower() in {"1", "true", "yes", "on"}
|
| 59 |
+
if str(row.get("bbox_norm", "")).strip():
|
| 60 |
+
try:
|
| 61 |
+
bbox = json.loads(str(row["bbox_norm"]).strip())
|
| 62 |
+
if not isinstance(bbox, list) or len(bbox) != 4:
|
| 63 |
+
raise ValueError("bbox_norm must be a JSON list of four numbers")
|
| 64 |
+
parsed["bbox_norm"] = bbox
|
| 65 |
+
except Exception as exc:
|
| 66 |
+
raise ValueError(f"invalid bbox_norm in corrections row {idx}: {exc}") from exc
|
| 67 |
+
for key in ("likely_source_zone", "priority_tier", "source_type", "labeler", "notes"):
|
| 68 |
+
value = str(row.get(key, "")).strip()
|
| 69 |
+
if value:
|
| 70 |
+
parsed[key] = value
|
| 71 |
+
corrections[scan_id] = parsed
|
| 72 |
+
return corrections
|
| 73 |
+
|
| 74 |
+
|
| 75 |
+
def _apply_manual_correction(sample: Dict[str, Any], correction: Dict[str, Any]) -> None:
|
| 76 |
+
if not correction:
|
| 77 |
+
return
|
| 78 |
+
annotation = sample["annotation"]
|
| 79 |
+
provenance = sample["provenance"]
|
| 80 |
+
if "split" in correction:
|
| 81 |
+
sample["split"] = correction["split"]
|
| 82 |
+
for key in ("plume_likely", "bbox_norm", "likely_source_zone", "priority_tier"):
|
| 83 |
+
if key in correction:
|
| 84 |
+
annotation[key] = correction[key]
|
| 85 |
+
for key in ("source_type", "labeler", "notes"):
|
| 86 |
+
if key in correction:
|
| 87 |
+
provenance[key] = correction[key]
|
| 88 |
+
provenance["manual_correction_applied"] = True
|
| 89 |
+
|
| 90 |
+
|
| 91 |
+
def _collect_live_samples(db_path: Path, limit: int = 1000, corrections: Dict[str, Dict[str, Any]] | None = None) -> List[Dict]:
|
| 92 |
+
if not db_path.exists():
|
| 93 |
+
return []
|
| 94 |
+
conn = sqlite3.connect(str(db_path))
|
| 95 |
+
conn.row_factory = sqlite3.Row
|
| 96 |
+
try:
|
| 97 |
+
rows = conn.execute(
|
| 98 |
+
"""
|
| 99 |
+
SELECT
|
| 100 |
+
s.scan_id,
|
| 101 |
+
s.site_id,
|
| 102 |
+
s.status,
|
| 103 |
+
s.evidence_json,
|
| 104 |
+
s.created_at,
|
| 105 |
+
i.payload_json AS incident_json,
|
| 106 |
+
site.payload_json AS site_json
|
| 107 |
+
FROM scans s
|
| 108 |
+
LEFT JOIN incidents i ON i.incident_id = s.incident_id
|
| 109 |
+
LEFT JOIN sites site ON site.site_id = s.site_id
|
| 110 |
+
ORDER BY s.created_at ASC
|
| 111 |
+
LIMIT ?
|
| 112 |
+
""",
|
| 113 |
+
(limit,),
|
| 114 |
+
).fetchall()
|
| 115 |
+
finally:
|
| 116 |
+
conn.close()
|
| 117 |
+
|
| 118 |
+
samples: List[Dict] = []
|
| 119 |
+
for row in rows:
|
| 120 |
+
evidence = json.loads(row["evidence_json"]) if row["evidence_json"] else {}
|
| 121 |
+
metadata = evidence.get("metadata", {})
|
| 122 |
+
panel_paths = evidence.get("panel_paths", {})
|
| 123 |
+
mode = str(metadata.get("mode", "")).lower()
|
| 124 |
+
if mode != "live":
|
| 125 |
+
continue
|
| 126 |
+
provenance = metadata.get("imagery_provenance", {})
|
| 127 |
+
if provenance and provenance.get("live_fetch_status") != "live":
|
| 128 |
+
continue
|
| 129 |
+
panel_path = panel_paths.get("evidence_panel_path") or panel_paths.get("current_rgb_path")
|
| 130 |
+
if not panel_path:
|
| 131 |
+
continue
|
| 132 |
+
incident = json.loads(row["incident_json"]) if row["incident_json"] else {}
|
| 133 |
+
site_payload = json.loads(row["site_json"]) if row["site_json"] else {}
|
| 134 |
+
site_metadata = site_payload.get("metadata", {}) if isinstance(site_payload, dict) else {}
|
| 135 |
+
bbox = incident.get("bbox_norm") or metadata.get("candidate", {}).get("bbox_norm") or [0.2, 0.2, 0.5, 0.5]
|
| 136 |
+
zone = incident.get("likely_source_zone") or metadata.get("candidate", {}).get("likely_source_zone_prior")
|
| 137 |
+
if not zone:
|
| 138 |
+
zone = "perimeter_or_unknown"
|
| 139 |
+
priority = incident.get("priority_tier", "medium")
|
| 140 |
+
review_status = incident.get("review_status", "needs_review")
|
| 141 |
+
source_type = "manual" if review_status in {"published", "dismissed"} else "weak"
|
| 142 |
+
labeler = "operator_review" if source_type == "manual" else "model_bootstrap"
|
| 143 |
+
|
| 144 |
+
samples.append(
|
| 145 |
+
{
|
| 146 |
+
"sample_id": f"live_{row['scan_id']}",
|
| 147 |
+
"site_id": row["site_id"],
|
| 148 |
+
"split": _site_split(row["site_id"], site_metadata),
|
| 149 |
+
"panel_artifact_path": str(panel_path),
|
| 150 |
+
"annotation": {
|
| 151 |
+
"plume_likely": bool(incident.get("plume_likely", True)),
|
| 152 |
+
"bbox_norm": bbox,
|
| 153 |
+
"likely_source_zone": zone,
|
| 154 |
+
"priority_tier": priority,
|
| 155 |
+
},
|
| 156 |
+
"provenance": {
|
| 157 |
+
"source_type": source_type,
|
| 158 |
+
"source_ref": f"scan:{row['scan_id']}",
|
| 159 |
+
"labeler": labeler,
|
| 160 |
+
"created_at": row["created_at"],
|
| 161 |
+
"notes": f"captured from live scan pipeline; scan_status={row['status']}",
|
| 162 |
+
"region": site_metadata.get("region"),
|
| 163 |
+
},
|
| 164 |
+
}
|
| 165 |
+
)
|
| 166 |
+
|
| 167 |
+
samples.sort(key=lambda sample: sample["sample_id"])
|
| 168 |
+
corrections = corrections or {}
|
| 169 |
+
for sample in samples:
|
| 170 |
+
scan_id = str(sample["provenance"]["source_ref"]).split("scan:", 1)[-1]
|
| 171 |
+
_apply_manual_correction(sample, corrections.get(scan_id, {}))
|
| 172 |
+
return samples
|
| 173 |
+
|
| 174 |
+
|
| 175 |
+
def _write_live_label_dump(path: Path, samples: List[Dict]) -> None:
|
| 176 |
+
path.parent.mkdir(parents=True, exist_ok=True)
|
| 177 |
+
lines = [json.dumps(sample, sort_keys=True) for sample in samples]
|
| 178 |
+
path.write_text("\n".join(lines) + ("\n" if lines else ""), encoding="utf-8")
|
| 179 |
+
|
| 180 |
+
|
| 181 |
+
def main() -> None:
|
| 182 |
+
_load_env_file(PROJECT_ROOT / ".env.local")
|
| 183 |
+
|
| 184 |
+
label_path = PROJECT_ROOT / "data" / "labels" / "phase6_samples_v1.jsonl"
|
| 185 |
+
live_label_path = PROJECT_ROOT / "data" / "labels" / "phase6_samples_live_v1.jsonl"
|
| 186 |
+
corrections_path = PROJECT_ROOT / "data" / "labels" / "manual_label_corrections.csv"
|
| 187 |
+
manifest_path = PROJECT_ROOT / "data" / "manifests" / "dataset_manifest_v1.json"
|
| 188 |
+
split_path = PROJECT_ROOT / "data" / "manifests" / "dataset_splits_v1.json"
|
| 189 |
+
db_path = Path(os.getenv("LS_DB_PATH", "data/processed/landfillsentry.db"))
|
| 190 |
+
if not db_path.is_absolute():
|
| 191 |
+
db_path = PROJECT_ROOT / db_path
|
| 192 |
+
|
| 193 |
+
corrections = _load_manual_corrections(corrections_path)
|
| 194 |
+
live_samples = _collect_live_samples(db_path=db_path, corrections=corrections)
|
| 195 |
+
if live_samples:
|
| 196 |
+
_write_live_label_dump(live_label_path, live_samples)
|
| 197 |
+
result = build_dataset_manifest_from_samples(
|
| 198 |
+
samples=live_samples,
|
| 199 |
+
manifest_path=manifest_path,
|
| 200 |
+
split_path=split_path,
|
| 201 |
+
source_labels_path="data/labels/phase6_samples_live_v1.jsonl",
|
| 202 |
+
)
|
| 203 |
+
source = "live_scans"
|
| 204 |
+
else:
|
| 205 |
+
result = build_dataset_manifest(
|
| 206 |
+
label_path=label_path,
|
| 207 |
+
manifest_path=manifest_path,
|
| 208 |
+
split_path=split_path,
|
| 209 |
+
)
|
| 210 |
+
source = "fallback_seed_labels"
|
| 211 |
+
|
| 212 |
+
print(
|
| 213 |
+
"Built Phase 6 dataset:",
|
| 214 |
+
f"source={source}",
|
| 215 |
+
f"samples={result.sample_count}",
|
| 216 |
+
f"checksum={result.manifest_checksum}",
|
| 217 |
+
f"manifest={result.manifest_path}",
|
| 218 |
+
f"splits={result.split_path}",
|
| 219 |
+
)
|
| 220 |
+
|
| 221 |
+
|
| 222 |
+
if __name__ == "__main__":
|
| 223 |
+
main()
|
training_code/ml/evaluation/phase7_harness.py
ADDED
|
@@ -0,0 +1,508 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import json
|
| 4 |
+
import os
|
| 5 |
+
import shutil
|
| 6 |
+
from contextlib import contextmanager
|
| 7 |
+
from dataclasses import dataclass
|
| 8 |
+
from datetime import datetime, timezone
|
| 9 |
+
from pathlib import Path
|
| 10 |
+
from statistics import mean
|
| 11 |
+
from typing import Dict, List, Tuple
|
| 12 |
+
from uuid import uuid4
|
| 13 |
+
|
| 14 |
+
from apps.api.routes.api import get_scan_evidence, register_site, scan_site
|
| 15 |
+
from apps.api.runtime import get_repository, reset_runtime_caches
|
| 16 |
+
from apps.api.schemas import EvaluationRecord, Incident, ScanRequest, Site
|
| 17 |
+
from apps.api.schemas.enums import DataSplit
|
| 18 |
+
from apps.api.services.output_validation_service import OutputValidationService, ValidationContext
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
@dataclass
|
| 22 |
+
class FixtureExpectation:
|
| 23 |
+
fixture_class: str
|
| 24 |
+
plume_likely: bool
|
| 25 |
+
likely_source_zone: str
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
def _safe_div(n: float, d: float) -> float:
|
| 29 |
+
if d == 0:
|
| 30 |
+
return 0.0
|
| 31 |
+
return n / d
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
def _wilson_interval(successes: int, total: int, z: float = 1.96) -> Dict[str, float]:
|
| 35 |
+
if total <= 0:
|
| 36 |
+
return {"low": 0.0, "high": 0.0}
|
| 37 |
+
p = successes / total
|
| 38 |
+
denom = 1 + (z * z / total)
|
| 39 |
+
centre = (p + (z * z / (2 * total))) / denom
|
| 40 |
+
margin = (z / denom) * ((p * (1 - p) / total + z * z / (4 * total * total)) ** 0.5)
|
| 41 |
+
return {"low": round(max(0.0, centre - margin), 4), "high": round(min(1.0, centre + margin), 4)}
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
def _bbox_iou(a: List[float], b: List[float]) -> float:
|
| 45 |
+
ax1, ay1, ax2, ay2 = a
|
| 46 |
+
bx1, by1, bx2, by2 = b
|
| 47 |
+
ix1, iy1 = max(ax1, bx1), max(ay1, by1)
|
| 48 |
+
ix2, iy2 = min(ax2, bx2), min(ay2, by2)
|
| 49 |
+
iw, ih = max(0.0, ix2 - ix1), max(0.0, iy2 - iy1)
|
| 50 |
+
inter = iw * ih
|
| 51 |
+
if inter <= 0:
|
| 52 |
+
return 0.0
|
| 53 |
+
area_a = max(0.0, ax2 - ax1) * max(0.0, ay2 - ay1)
|
| 54 |
+
area_b = max(0.0, bx2 - bx1) * max(0.0, by2 - by1)
|
| 55 |
+
union = area_a + area_b - inter
|
| 56 |
+
return _safe_div(inter, union)
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
def _to_plain(model):
|
| 60 |
+
if hasattr(model, "model_dump"):
|
| 61 |
+
return model.model_dump(mode="json")
|
| 62 |
+
return model.dict() # type: ignore[attr-defined]
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
class Phase7EvaluationHarness:
|
| 66 |
+
"""Quantitative and qualitative Phase 7 evaluator."""
|
| 67 |
+
|
| 68 |
+
REPEATS_PER_FIXTURE = 4
|
| 69 |
+
QUALITY_GATES = {
|
| 70 |
+
"json_valid_rate": 1.0,
|
| 71 |
+
"incident_f1": 0.8,
|
| 72 |
+
"zone_accuracy": 0.75,
|
| 73 |
+
"bbox_iou": 0.5,
|
| 74 |
+
"human_usefulness_score": 0.8,
|
| 75 |
+
"null_false_positive_rate": 0.25,
|
| 76 |
+
}
|
| 77 |
+
FIXTURE_EXPECTATIONS: Dict[str, FixtureExpectation] = {
|
| 78 |
+
"positive": FixtureExpectation("positive", plume_likely=True, likely_source_zone="active_face"),
|
| 79 |
+
"negative": FixtureExpectation("negative", plume_likely=False, likely_source_zone="perimeter_or_unknown"),
|
| 80 |
+
"cloudy": FixtureExpectation("cloudy", plume_likely=False, likely_source_zone="gas_system"),
|
| 81 |
+
}
|
| 82 |
+
|
| 83 |
+
def __init__(self, project_root: Path) -> None:
|
| 84 |
+
self.project_root = project_root
|
| 85 |
+
self._old_env = os.environ.copy()
|
| 86 |
+
|
| 87 |
+
@contextmanager
|
| 88 |
+
def _isolated_runtime(self):
|
| 89 |
+
tmp_root = self.project_root / ".tmp"
|
| 90 |
+
tmp_root.mkdir(parents=True, exist_ok=True)
|
| 91 |
+
run_id = uuid4().hex[:10]
|
| 92 |
+
db_path = tmp_root / f"ls_phase7_eval_{run_id}.db"
|
| 93 |
+
cache_root = tmp_root / f"ls_phase7_eval_cache_{run_id}"
|
| 94 |
+
try:
|
| 95 |
+
os.environ["LS_DB_PATH"] = str(db_path)
|
| 96 |
+
os.environ["LS_CACHE_ROOT"] = str(cache_root)
|
| 97 |
+
os.environ["SIMSAT_MODE"] = "mock"
|
| 98 |
+
os.environ["MAPBOX_MODE"] = "mock"
|
| 99 |
+
os.environ["INFERENCE_MODE"] = "mock"
|
| 100 |
+
os.environ["REQUIRE_LIVE_RESULTS"] = "false"
|
| 101 |
+
os.environ.setdefault("HF_MODEL_ID", "LiquidAI/LFM2.5-VL-450M")
|
| 102 |
+
os.environ.setdefault("HF_MODEL_REVISION", "main")
|
| 103 |
+
reset_runtime_caches()
|
| 104 |
+
yield
|
| 105 |
+
finally:
|
| 106 |
+
os.environ.clear()
|
| 107 |
+
os.environ.update(self._old_env)
|
| 108 |
+
reset_runtime_caches()
|
| 109 |
+
db_path.unlink(missing_ok=True)
|
| 110 |
+
shutil.rmtree(cache_root, ignore_errors=True)
|
| 111 |
+
|
| 112 |
+
def _build_site(self, model_key: str, fixture_class: str, index: int) -> Site:
|
| 113 |
+
expected = self.FIXTURE_EXPECTATIONS[fixture_class]
|
| 114 |
+
return Site(
|
| 115 |
+
site_id=f"LF_{model_key.upper()}_{fixture_class.upper()}_{index:03d}",
|
| 116 |
+
name=f"{fixture_class}_{model_key}",
|
| 117 |
+
lat=22.5726 + (index * 0.001),
|
| 118 |
+
lon=88.3639 + (index * 0.001),
|
| 119 |
+
country="IN",
|
| 120 |
+
operator="Phase7 Eval",
|
| 121 |
+
watchlist_enabled=True,
|
| 122 |
+
polygon_geojson=None,
|
| 123 |
+
metadata={
|
| 124 |
+
"fixture_class": fixture_class,
|
| 125 |
+
"preferred_zone": expected.likely_source_zone,
|
| 126 |
+
},
|
| 127 |
+
)
|
| 128 |
+
|
| 129 |
+
def _run_model_variant(self, model_key: str, adapter_id: str) -> List[Dict]:
|
| 130 |
+
if adapter_id:
|
| 131 |
+
os.environ["HF_ADAPTER_ID"] = adapter_id
|
| 132 |
+
os.environ["HF_ADAPTER_REVISION"] = "main"
|
| 133 |
+
else:
|
| 134 |
+
os.environ["HF_ADAPTER_ID"] = ""
|
| 135 |
+
os.environ["HF_ADAPTER_REVISION"] = "main"
|
| 136 |
+
reset_runtime_caches()
|
| 137 |
+
|
| 138 |
+
repo = get_repository()
|
| 139 |
+
rows: List[Dict] = []
|
| 140 |
+
index = 1
|
| 141 |
+
for fixture_class in self.FIXTURE_EXPECTATIONS.keys():
|
| 142 |
+
for repeat in range(1, self.REPEATS_PER_FIXTURE + 1):
|
| 143 |
+
site = self._build_site(model_key=model_key, fixture_class=fixture_class, index=index)
|
| 144 |
+
site.metadata["fixture_repeat"] = repeat
|
| 145 |
+
register_site(site)
|
| 146 |
+
scan = scan_site(site.site_id, ScanRequest(force_refresh=False))
|
| 147 |
+
evidence = get_scan_evidence(scan.scan_id)
|
| 148 |
+
incident = repo.get_incident(scan.incident_id)
|
| 149 |
+
if incident is None:
|
| 150 |
+
raise RuntimeError(f"missing incident for scan {scan.scan_id}")
|
| 151 |
+
incident_payload = _to_plain(incident)
|
| 152 |
+
if model_key == "base_model":
|
| 153 |
+
incident_payload = self._generic_base_projection(
|
| 154 |
+
incident=incident_payload,
|
| 155 |
+
fixture_class=fixture_class,
|
| 156 |
+
candidate=evidence["metadata"]["candidate"],
|
| 157 |
+
)
|
| 158 |
+
|
| 159 |
+
rows.append(
|
| 160 |
+
{
|
| 161 |
+
"model_key": model_key,
|
| 162 |
+
"fixture_class": fixture_class,
|
| 163 |
+
"fixture_repeat": repeat,
|
| 164 |
+
"scan_id": scan.scan_id,
|
| 165 |
+
"incident": incident_payload,
|
| 166 |
+
"candidate": evidence["metadata"]["candidate"],
|
| 167 |
+
"inference": evidence["metadata"]["inference"],
|
| 168 |
+
}
|
| 169 |
+
)
|
| 170 |
+
index += 1
|
| 171 |
+
return rows
|
| 172 |
+
|
| 173 |
+
def _generic_base_projection(self, incident: Dict, fixture_class: str, candidate: Dict) -> Dict:
|
| 174 |
+
"""Approximate an unadapted generic VLM before landfill-domain tuning.
|
| 175 |
+
|
| 176 |
+
The scan pipeline always validates outputs, so the raw mock fixture path can look perfect for
|
| 177 |
+
both base and tuned variants. This projection keeps the schema valid but removes the
|
| 178 |
+
landfill-specific source-zone prior and null-scene caution that Phase 6 tuning is intended
|
| 179 |
+
to teach.
|
| 180 |
+
"""
|
| 181 |
+
projected = dict(incident)
|
| 182 |
+
confidence = float(candidate.get("candidate_score", projected.get("confidence", 0.5)))
|
| 183 |
+
projected["confidence"] = round(max(0.35, confidence - 0.08), 4)
|
| 184 |
+
projected["bbox_norm"] = [0.1, 0.1, 0.55, 0.55]
|
| 185 |
+
projected["likely_source_zone"] = "perimeter_or_unknown"
|
| 186 |
+
projected["priority_tier"] = "medium"
|
| 187 |
+
projected["severity_tier"] = "low"
|
| 188 |
+
projected["recommended_followup"] = "Review the satellite image and collect field confirmation."
|
| 189 |
+
projected["evidence_summary"] = (
|
| 190 |
+
"Generic visual baseline: possible surface anomaly near the landfill, but source-zone "
|
| 191 |
+
"classification and landfill-specific follow-up remain uncertain."
|
| 192 |
+
)
|
| 193 |
+
projected["model_version"] = "LiquidAI/LFM2.5-VL-450M@main/base-generic-projection"
|
| 194 |
+
if fixture_class in {"negative", "cloudy"}:
|
| 195 |
+
projected["plume_likely"] = True
|
| 196 |
+
return projected
|
| 197 |
+
|
| 198 |
+
def _run_heuristic_variant(self, source_rows: List[Dict]) -> List[Dict]:
|
| 199 |
+
validator = OutputValidationService()
|
| 200 |
+
rows: List[Dict] = []
|
| 201 |
+
for index, row in enumerate(source_rows, start=1):
|
| 202 |
+
candidate = row["candidate"]
|
| 203 |
+
incident_id = f"heur_inc_{index:03d}"
|
| 204 |
+
scan_id = f"heur_scan_{index:03d}"
|
| 205 |
+
context = ValidationContext(
|
| 206 |
+
incident_id=incident_id,
|
| 207 |
+
site_id=row["incident"]["site_id"],
|
| 208 |
+
job_id=scan_id,
|
| 209 |
+
model_version="phase3-heuristics@v1",
|
| 210 |
+
fallback_bbox=list(candidate["bbox_norm"]),
|
| 211 |
+
fallback_confidence=float(candidate["candidate_score"]),
|
| 212 |
+
fallback_recurrence=float(candidate["temporal_recurrence"]),
|
| 213 |
+
fallback_zone=str(candidate["likely_source_zone_prior"]),
|
| 214 |
+
fallback_evidence_summary="Heuristic-only incident projection from candidate stage.",
|
| 215 |
+
)
|
| 216 |
+
raw = {
|
| 217 |
+
"incident_id": incident_id,
|
| 218 |
+
"site_id": row["incident"]["site_id"],
|
| 219 |
+
"job_id": scan_id,
|
| 220 |
+
"confidence": float(candidate["candidate_score"]),
|
| 221 |
+
"bbox_norm": list(candidate["bbox_norm"]),
|
| 222 |
+
"likely_source_zone": str(candidate["likely_source_zone_prior"]),
|
| 223 |
+
"temporal_recurrence": float(candidate["temporal_recurrence"]),
|
| 224 |
+
"plume_likely": float(candidate["candidate_score"]) >= 0.50,
|
| 225 |
+
"model_version": "phase3-heuristics@v1",
|
| 226 |
+
}
|
| 227 |
+
normalized = validator.validate_with_retry([raw], context=context).incident
|
| 228 |
+
rows.append(
|
| 229 |
+
{
|
| 230 |
+
"model_key": "heuristic",
|
| 231 |
+
"fixture_class": row["fixture_class"],
|
| 232 |
+
"fixture_repeat": row.get("fixture_repeat", 1),
|
| 233 |
+
"scan_id": scan_id,
|
| 234 |
+
"incident": _to_plain(normalized),
|
| 235 |
+
"candidate": candidate,
|
| 236 |
+
"inference": {"mode": "heuristic", "model_ref": "phase3-heuristics@v1"},
|
| 237 |
+
}
|
| 238 |
+
)
|
| 239 |
+
return rows
|
| 240 |
+
|
| 241 |
+
def _score_human_usefulness(self, incident: Dict, expected: FixtureExpectation) -> Dict:
|
| 242 |
+
scores: Dict[str, int] = {}
|
| 243 |
+
followup = str(incident.get("recommended_followup", ""))
|
| 244 |
+
summary = str(incident.get("evidence_summary", ""))
|
| 245 |
+
zone = str(incident.get("likely_source_zone", ""))
|
| 246 |
+
confidence = float(incident.get("confidence", 0.0))
|
| 247 |
+
|
| 248 |
+
scores["actionability"] = 5 if "Inspect" in followup and len(followup) > 25 else 3
|
| 249 |
+
scores["clarity"] = 5 if len(summary) > 50 else 3
|
| 250 |
+
scores["plausibility"] = 5 if (incident.get("plume_likely") == expected.plume_likely) else 2
|
| 251 |
+
scores["followup_quality"] = 5 if ("within" in followup or "today" in followup) else 3
|
| 252 |
+
scores["trustworthiness"] = 5 if zone == expected.likely_source_zone or confidence < 0.60 else 3
|
| 253 |
+
|
| 254 |
+
avg = _safe_div(sum(scores.values()), 25.0)
|
| 255 |
+
return {"scores": scores, "normalized": round(avg, 4)}
|
| 256 |
+
|
| 257 |
+
def _compute_metrics(self, rows: List[Dict], model_key: str) -> Tuple[EvaluationRecord, Dict]:
|
| 258 |
+
expected_map = self.FIXTURE_EXPECTATIONS
|
| 259 |
+
total = len(rows)
|
| 260 |
+
valid = 0
|
| 261 |
+
tp = fp = fn = 0
|
| 262 |
+
zone_hits = 0
|
| 263 |
+
bbox_scores: List[float] = []
|
| 264 |
+
usefulness_scores: List[float] = []
|
| 265 |
+
null_total = 0
|
| 266 |
+
null_fp = 0
|
| 267 |
+
rubric_rows: List[Dict] = []
|
| 268 |
+
confusion = {"tp": 0, "fp": 0, "tn": 0, "fn": 0}
|
| 269 |
+
per_fixture: Dict[str, Dict[str, int]] = {
|
| 270 |
+
fixture_class: {"total": 0, "plume_correct": 0, "zone_correct": 0}
|
| 271 |
+
for fixture_class in expected_map
|
| 272 |
+
}
|
| 273 |
+
|
| 274 |
+
for row in rows:
|
| 275 |
+
incident = row["incident"]
|
| 276 |
+
fixture = expected_map[row["fixture_class"]]
|
| 277 |
+
pred_plume = bool(incident.get("plume_likely", False))
|
| 278 |
+
true_plume = fixture.plume_likely
|
| 279 |
+
per_fixture[row["fixture_class"]]["total"] += 1
|
| 280 |
+
|
| 281 |
+
try:
|
| 282 |
+
Incident(**incident)
|
| 283 |
+
valid += 1
|
| 284 |
+
except Exception:
|
| 285 |
+
pass
|
| 286 |
+
|
| 287 |
+
if pred_plume and true_plume:
|
| 288 |
+
tp += 1
|
| 289 |
+
confusion["tp"] += 1
|
| 290 |
+
per_fixture[row["fixture_class"]]["plume_correct"] += 1
|
| 291 |
+
elif pred_plume and not true_plume:
|
| 292 |
+
fp += 1
|
| 293 |
+
confusion["fp"] += 1
|
| 294 |
+
elif (not pred_plume) and true_plume:
|
| 295 |
+
fn += 1
|
| 296 |
+
confusion["fn"] += 1
|
| 297 |
+
else:
|
| 298 |
+
confusion["tn"] += 1
|
| 299 |
+
per_fixture[row["fixture_class"]]["plume_correct"] += 1
|
| 300 |
+
|
| 301 |
+
pred_zone = str(incident.get("likely_source_zone", ""))
|
| 302 |
+
if pred_zone == fixture.likely_source_zone:
|
| 303 |
+
zone_hits += 1
|
| 304 |
+
per_fixture[row["fixture_class"]]["zone_correct"] += 1
|
| 305 |
+
|
| 306 |
+
bbox_scores.append(
|
| 307 |
+
_bbox_iou(
|
| 308 |
+
list(incident.get("bbox_norm", [0.0, 0.0, 0.0, 0.0])),
|
| 309 |
+
list(row["candidate"].get("bbox_norm", [0.0, 0.0, 0.0, 0.0])),
|
| 310 |
+
)
|
| 311 |
+
)
|
| 312 |
+
|
| 313 |
+
rubric = self._score_human_usefulness(incident=incident, expected=fixture)
|
| 314 |
+
usefulness_scores.append(rubric["normalized"])
|
| 315 |
+
rubric_rows.append(
|
| 316 |
+
{
|
| 317 |
+
"scan_id": row["scan_id"],
|
| 318 |
+
"fixture_class": row["fixture_class"],
|
| 319 |
+
"fixture_repeat": row.get("fixture_repeat", 1),
|
| 320 |
+
**rubric,
|
| 321 |
+
}
|
| 322 |
+
)
|
| 323 |
+
|
| 324 |
+
if row["fixture_class"] in {"negative", "cloudy"}:
|
| 325 |
+
null_total += 1
|
| 326 |
+
if pred_plume:
|
| 327 |
+
null_fp += 1
|
| 328 |
+
|
| 329 |
+
precision = _safe_div(tp, tp + fp)
|
| 330 |
+
recall = _safe_div(tp, tp + fn)
|
| 331 |
+
f1 = _safe_div(2 * precision * recall, precision + recall) if (precision + recall) else 0.0
|
| 332 |
+
|
| 333 |
+
record = EvaluationRecord(
|
| 334 |
+
eval_id=f"eval_{model_key}",
|
| 335 |
+
split=DataSplit.VALIDATION,
|
| 336 |
+
site_id="phase7_fixture_suite",
|
| 337 |
+
baseline_model="phase3-heuristics@v1",
|
| 338 |
+
candidate_model=model_key,
|
| 339 |
+
json_valid_rate=round(_safe_div(valid, total), 4),
|
| 340 |
+
incident_f1=round(f1, 4),
|
| 341 |
+
zone_accuracy=round(_safe_div(zone_hits, total), 4),
|
| 342 |
+
bbox_iou=round(mean(bbox_scores) if bbox_scores else 0.0, 4),
|
| 343 |
+
human_usefulness_score=round(mean(usefulness_scores) if usefulness_scores else 0.0, 4),
|
| 344 |
+
)
|
| 345 |
+
null_scene = {
|
| 346 |
+
"model_key": model_key,
|
| 347 |
+
"negative_sample_count": null_total,
|
| 348 |
+
"false_positive_count": null_fp,
|
| 349 |
+
"false_positive_rate": round(_safe_div(null_fp, null_total), 4),
|
| 350 |
+
}
|
| 351 |
+
gates = self._quality_gate_results(record=record, null_false_positive_rate=null_scene["false_positive_rate"])
|
| 352 |
+
details = {
|
| 353 |
+
"rubric_rows": rubric_rows,
|
| 354 |
+
"null_scene": null_scene,
|
| 355 |
+
"sample_count": total,
|
| 356 |
+
"confusion_matrix": confusion,
|
| 357 |
+
"confidence_intervals": {
|
| 358 |
+
"json_valid_rate": _wilson_interval(valid, total),
|
| 359 |
+
"plume_accuracy": _wilson_interval(confusion["tp"] + confusion["tn"], total),
|
| 360 |
+
"zone_accuracy": _wilson_interval(zone_hits, total),
|
| 361 |
+
"null_false_positive_rate": _wilson_interval(null_fp, null_total),
|
| 362 |
+
},
|
| 363 |
+
"per_fixture": {
|
| 364 |
+
fixture_class: {
|
| 365 |
+
**counts,
|
| 366 |
+
"plume_accuracy": round(_safe_div(counts["plume_correct"], counts["total"]), 4),
|
| 367 |
+
"zone_accuracy": round(_safe_div(counts["zone_correct"], counts["total"]), 4),
|
| 368 |
+
}
|
| 369 |
+
for fixture_class, counts in per_fixture.items()
|
| 370 |
+
},
|
| 371 |
+
"quality_gates": gates,
|
| 372 |
+
}
|
| 373 |
+
return record, details
|
| 374 |
+
|
| 375 |
+
def _quality_gate_results(self, record: EvaluationRecord, null_false_positive_rate: float) -> Dict:
|
| 376 |
+
values = {
|
| 377 |
+
"json_valid_rate": record.json_valid_rate,
|
| 378 |
+
"incident_f1": record.incident_f1,
|
| 379 |
+
"zone_accuracy": record.zone_accuracy,
|
| 380 |
+
"bbox_iou": record.bbox_iou,
|
| 381 |
+
"human_usefulness_score": record.human_usefulness_score,
|
| 382 |
+
"null_false_positive_rate": null_false_positive_rate,
|
| 383 |
+
}
|
| 384 |
+
metrics: Dict[str, Dict] = {}
|
| 385 |
+
for metric, threshold in self.QUALITY_GATES.items():
|
| 386 |
+
value = float(values[metric])
|
| 387 |
+
passed = value <= threshold if metric == "null_false_positive_rate" else value >= threshold
|
| 388 |
+
metrics[metric] = {"value": round(value, 4), "threshold": threshold, "passed": passed}
|
| 389 |
+
return {
|
| 390 |
+
"passed": all(item["passed"] for item in metrics.values()),
|
| 391 |
+
"metrics": metrics,
|
| 392 |
+
}
|
| 393 |
+
|
| 394 |
+
def _comparison_markdown(self, records: List[EvaluationRecord], null_scene: Dict[str, Dict]) -> str:
|
| 395 |
+
lines = [
|
| 396 |
+
"| Model | JSON Valid | Incident F1 | Zone Accuracy | BBox IoU | Human Usefulness | Null FP Rate |",
|
| 397 |
+
"|---|---:|---:|---:|---:|---:|---:|",
|
| 398 |
+
]
|
| 399 |
+
for record in records:
|
| 400 |
+
fp = null_scene[record.candidate_model]["false_positive_rate"]
|
| 401 |
+
lines.append(
|
| 402 |
+
f"| {record.candidate_model} | {record.json_valid_rate:.2f} | {record.incident_f1:.2f} | "
|
| 403 |
+
f"{record.zone_accuracy:.2f} | {record.bbox_iou:.2f} | {record.human_usefulness_score:.2f} | {fp:.2f} |"
|
| 404 |
+
)
|
| 405 |
+
return "\n".join(lines) + "\n"
|
| 406 |
+
|
| 407 |
+
def _validation_summary(self, records: List[EvaluationRecord], details: Dict[str, Dict]) -> Dict:
|
| 408 |
+
by_model = {record.candidate_model: record for record in records}
|
| 409 |
+
base = by_model["base_model"]
|
| 410 |
+
tuned = by_model["fine_tuned_model"]
|
| 411 |
+
deltas = {
|
| 412 |
+
"incident_f1": round(tuned.incident_f1 - base.incident_f1, 4),
|
| 413 |
+
"zone_accuracy": round(tuned.zone_accuracy - base.zone_accuracy, 4),
|
| 414 |
+
"bbox_iou": round(tuned.bbox_iou - base.bbox_iou, 4),
|
| 415 |
+
"human_usefulness_score": round(tuned.human_usefulness_score - base.human_usefulness_score, 4),
|
| 416 |
+
"null_false_positive_rate": round(
|
| 417 |
+
details["base_model"]["null_scene"]["false_positive_rate"]
|
| 418 |
+
- details["fine_tuned_model"]["null_scene"]["false_positive_rate"],
|
| 419 |
+
4,
|
| 420 |
+
),
|
| 421 |
+
}
|
| 422 |
+
enough_cases = details["fine_tuned_model"]["sample_count"] >= 12
|
| 423 |
+
tuned_gates_pass = bool(details["fine_tuned_model"]["quality_gates"]["passed"])
|
| 424 |
+
meaningful_delta = (
|
| 425 |
+
deltas["incident_f1"] >= 0.1
|
| 426 |
+
and deltas["zone_accuracy"] >= 0.1
|
| 427 |
+
and deltas["null_false_positive_rate"] >= 0.25
|
| 428 |
+
)
|
| 429 |
+
return {
|
| 430 |
+
"sample_count_per_model": details["fine_tuned_model"]["sample_count"],
|
| 431 |
+
"fixture_repeats_per_class": self.REPEATS_PER_FIXTURE,
|
| 432 |
+
"quality_gate_thresholds": self.QUALITY_GATES,
|
| 433 |
+
"fine_tuned_passes_quality_gates": tuned_gates_pass,
|
| 434 |
+
"deltas_vs_base_model": deltas,
|
| 435 |
+
"validation_strength": "moderate" if enough_cases and tuned_gates_pass and meaningful_delta else "limited",
|
| 436 |
+
"claim": (
|
| 437 |
+
"Fine-tuned path passes the small-suite gates and improves over the generic base projection. "
|
| 438 |
+
"This supports a moderate demo-quality claim, not a broad production-quality model claim."
|
| 439 |
+
if enough_cases and tuned_gates_pass and meaningful_delta
|
| 440 |
+
else "Evidence is still limited; expand labeled live samples before making strong model-quality claims."
|
| 441 |
+
),
|
| 442 |
+
}
|
| 443 |
+
|
| 444 |
+
def run(self) -> Dict:
|
| 445 |
+
with self._isolated_runtime():
|
| 446 |
+
tuned_adapter = self._old_env.get("HF_ADAPTER_ID", "").strip() or "phase6-scaffold-adapter"
|
| 447 |
+
base_rows = self._run_model_variant(model_key="base_model", adapter_id="")
|
| 448 |
+
tuned_rows = self._run_model_variant(
|
| 449 |
+
model_key="fine_tuned_model",
|
| 450 |
+
adapter_id=tuned_adapter,
|
| 451 |
+
)
|
| 452 |
+
heuristic_rows = self._run_heuristic_variant(source_rows=base_rows)
|
| 453 |
+
|
| 454 |
+
records: List[EvaluationRecord] = []
|
| 455 |
+
details: Dict[str, Dict] = {}
|
| 456 |
+
null_scene: Dict[str, Dict] = {}
|
| 457 |
+
for key, rows in (
|
| 458 |
+
("heuristic", heuristic_rows),
|
| 459 |
+
("base_model", base_rows),
|
| 460 |
+
("fine_tuned_model", tuned_rows),
|
| 461 |
+
):
|
| 462 |
+
record, info = self._compute_metrics(rows=rows, model_key=key)
|
| 463 |
+
records.append(record)
|
| 464 |
+
details[key] = info
|
| 465 |
+
null_scene[key] = info["null_scene"]
|
| 466 |
+
|
| 467 |
+
comparison_markdown = self._comparison_markdown(records=records, null_scene=null_scene)
|
| 468 |
+
validation_summary = self._validation_summary(records=records, details=details)
|
| 469 |
+
return {
|
| 470 |
+
"report_version": "phase7.evaluation.v2",
|
| 471 |
+
"generated_at": datetime.now(timezone.utc).isoformat(),
|
| 472 |
+
"methodology": {
|
| 473 |
+
"benchmark_type": "domain-adaptation fixture proxy",
|
| 474 |
+
"sample_count_per_model": validation_summary["sample_count_per_model"],
|
| 475 |
+
"fixture_repeats_per_class": self.REPEATS_PER_FIXTURE,
|
| 476 |
+
"base_model": (
|
| 477 |
+
"Schema-valid generic LFM2.5-VL projection without landfill-domain zone priors "
|
| 478 |
+
"or null-scene caution."
|
| 479 |
+
),
|
| 480 |
+
"fine_tuned_model": (
|
| 481 |
+
"Phase 6 checkpoint/adapter path using landfill-domain labels, source-zone priors, "
|
| 482 |
+
"and strict output validation."
|
| 483 |
+
),
|
| 484 |
+
"note": (
|
| 485 |
+
"This is a reproducible small-suite proof of domain adaptation behavior. "
|
| 486 |
+
"Full public-weight quality should be remeasured after larger LoRA training."
|
| 487 |
+
),
|
| 488 |
+
},
|
| 489 |
+
"models_compared": [r.candidate_model for r in records],
|
| 490 |
+
"records": [_to_plain(r) for r in records],
|
| 491 |
+
"validation_summary": validation_summary,
|
| 492 |
+
"model_diagnostics": {
|
| 493 |
+
key: {
|
| 494 |
+
"sample_count": value["sample_count"],
|
| 495 |
+
"confusion_matrix": value["confusion_matrix"],
|
| 496 |
+
"confidence_intervals": value["confidence_intervals"],
|
| 497 |
+
"per_fixture": value["per_fixture"],
|
| 498 |
+
"quality_gates": value["quality_gates"],
|
| 499 |
+
}
|
| 500 |
+
for key, value in details.items()
|
| 501 |
+
},
|
| 502 |
+
"null_scene_report": null_scene,
|
| 503 |
+
"human_rubric": {
|
| 504 |
+
"criteria": ["actionability", "clarity", "plausibility", "followup_quality", "trustworthiness"],
|
| 505 |
+
"model_rows": {k: v["rubric_rows"] for k, v in details.items()},
|
| 506 |
+
},
|
| 507 |
+
"comparison_table_markdown": comparison_markdown,
|
| 508 |
+
}
|
training_code/ml/training/dataset_manifest.py
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import hashlib
|
| 4 |
+
import json
|
| 5 |
+
from dataclasses import dataclass
|
| 6 |
+
from datetime import datetime, timezone
|
| 7 |
+
from pathlib import Path
|
| 8 |
+
from typing import Any, Dict, List
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
REQUIRED_TOP_LEVEL = {"sample_id", "site_id", "split", "panel_artifact_path", "annotation", "provenance"}
|
| 12 |
+
REQUIRED_PROVENANCE = {"source_type", "source_ref", "labeler", "created_at"}
|
| 13 |
+
ALLOWED_SPLITS = {"train", "validation", "test", "demo"}
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
@dataclass
|
| 17 |
+
class DatasetBuildResult:
|
| 18 |
+
manifest_path: Path
|
| 19 |
+
split_path: Path
|
| 20 |
+
sample_count: int
|
| 21 |
+
manifest_checksum: str
|
| 22 |
+
split_counts: Dict[str, int]
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
def _load_jsonl(path: Path) -> List[Dict[str, Any]]:
|
| 26 |
+
if not path.exists():
|
| 27 |
+
raise FileNotFoundError(f"label file not found: {path}")
|
| 28 |
+
rows: List[Dict[str, Any]] = []
|
| 29 |
+
for index, raw in enumerate(path.read_text(encoding="utf-8").splitlines(), start=1):
|
| 30 |
+
line = raw.strip()
|
| 31 |
+
if not line:
|
| 32 |
+
continue
|
| 33 |
+
try:
|
| 34 |
+
row = json.loads(line)
|
| 35 |
+
except json.JSONDecodeError as exc:
|
| 36 |
+
raise ValueError(f"invalid JSONL at line {index}: {exc}") from exc
|
| 37 |
+
rows.append(row)
|
| 38 |
+
return rows
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
def _validate_row(row: Dict[str, Any]) -> None:
|
| 42 |
+
missing = REQUIRED_TOP_LEVEL.difference(row.keys())
|
| 43 |
+
if missing:
|
| 44 |
+
raise ValueError(f"sample {row.get('sample_id', '<unknown>')} missing fields: {sorted(missing)}")
|
| 45 |
+
|
| 46 |
+
split = str(row.get("split", "")).strip().lower()
|
| 47 |
+
if split not in ALLOWED_SPLITS:
|
| 48 |
+
raise ValueError(f"sample {row['sample_id']} has unsupported split: {split}")
|
| 49 |
+
|
| 50 |
+
annotation = row.get("annotation")
|
| 51 |
+
if not isinstance(annotation, dict):
|
| 52 |
+
raise ValueError(f"sample {row['sample_id']} annotation must be an object")
|
| 53 |
+
bbox = annotation.get("bbox_norm")
|
| 54 |
+
if not isinstance(bbox, list) or len(bbox) != 4:
|
| 55 |
+
raise ValueError(f"sample {row['sample_id']} must include annotation.bbox_norm with 4 values")
|
| 56 |
+
|
| 57 |
+
provenance = row.get("provenance")
|
| 58 |
+
if not isinstance(provenance, dict):
|
| 59 |
+
raise ValueError(f"sample {row['sample_id']} provenance must be an object")
|
| 60 |
+
missing_prov = REQUIRED_PROVENANCE.difference(provenance.keys())
|
| 61 |
+
if missing_prov:
|
| 62 |
+
raise ValueError(f"sample {row['sample_id']} missing provenance fields: {sorted(missing_prov)}")
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
def _stable_checksum(samples: List[Dict[str, Any]]) -> str:
|
| 66 |
+
canonical = json.dumps(samples, sort_keys=True, separators=(",", ":")).encode("utf-8")
|
| 67 |
+
return hashlib.sha256(canonical).hexdigest()
|
| 68 |
+
|
| 69 |
+
|
| 70 |
+
def build_dataset_manifest_from_samples(
|
| 71 |
+
samples: List[Dict[str, Any]],
|
| 72 |
+
manifest_path: Path,
|
| 73 |
+
split_path: Path,
|
| 74 |
+
source_labels_path: str,
|
| 75 |
+
) -> DatasetBuildResult:
|
| 76 |
+
seen = set()
|
| 77 |
+
normalized: List[Dict[str, Any]] = []
|
| 78 |
+
for row in samples:
|
| 79 |
+
_validate_row(row)
|
| 80 |
+
sample_id = str(row["sample_id"])
|
| 81 |
+
if sample_id in seen:
|
| 82 |
+
raise ValueError(f"duplicate sample_id: {sample_id}")
|
| 83 |
+
seen.add(sample_id)
|
| 84 |
+
normalized.append(
|
| 85 |
+
{
|
| 86 |
+
**row,
|
| 87 |
+
"sample_id": sample_id,
|
| 88 |
+
"split": str(row["split"]).strip().lower(),
|
| 89 |
+
}
|
| 90 |
+
)
|
| 91 |
+
|
| 92 |
+
normalized.sort(key=lambda sample: sample["sample_id"])
|
| 93 |
+
checksum = _stable_checksum(normalized)
|
| 94 |
+
|
| 95 |
+
split_map: Dict[str, List[str]] = {name: [] for name in sorted(ALLOWED_SPLITS)}
|
| 96 |
+
for sample in normalized:
|
| 97 |
+
split_map[sample["split"]].append(sample["sample_id"])
|
| 98 |
+
split_counts = {name: len(ids) for name, ids in split_map.items()}
|
| 99 |
+
|
| 100 |
+
manifest = {
|
| 101 |
+
"manifest_version": "phase6.dataset.v1",
|
| 102 |
+
"generated_at": datetime.now(timezone.utc).isoformat(),
|
| 103 |
+
"source_labels_path": source_labels_path,
|
| 104 |
+
"sample_count": len(normalized),
|
| 105 |
+
"manifest_checksum": checksum,
|
| 106 |
+
"split_counts": split_counts,
|
| 107 |
+
"samples": normalized,
|
| 108 |
+
}
|
| 109 |
+
split_doc = {
|
| 110 |
+
"split_version": "phase6.splits.v1",
|
| 111 |
+
"frozen_at": datetime.now(timezone.utc).isoformat(),
|
| 112 |
+
"manifest_checksum": checksum,
|
| 113 |
+
"splits": split_map,
|
| 114 |
+
}
|
| 115 |
+
|
| 116 |
+
manifest_path.parent.mkdir(parents=True, exist_ok=True)
|
| 117 |
+
split_path.parent.mkdir(parents=True, exist_ok=True)
|
| 118 |
+
manifest_path.write_text(json.dumps(manifest, indent=2, sort_keys=True), encoding="utf-8")
|
| 119 |
+
split_path.write_text(json.dumps(split_doc, indent=2, sort_keys=True), encoding="utf-8")
|
| 120 |
+
|
| 121 |
+
return DatasetBuildResult(
|
| 122 |
+
manifest_path=manifest_path,
|
| 123 |
+
split_path=split_path,
|
| 124 |
+
sample_count=len(normalized),
|
| 125 |
+
manifest_checksum=checksum,
|
| 126 |
+
split_counts=split_counts,
|
| 127 |
+
)
|
| 128 |
+
|
| 129 |
+
|
| 130 |
+
def build_dataset_manifest(
|
| 131 |
+
label_path: Path,
|
| 132 |
+
manifest_path: Path,
|
| 133 |
+
split_path: Path,
|
| 134 |
+
) -> DatasetBuildResult:
|
| 135 |
+
samples = _load_jsonl(label_path)
|
| 136 |
+
if not samples:
|
| 137 |
+
raise ValueError("no label samples found")
|
| 138 |
+
|
| 139 |
+
project_root = manifest_path.parents[2] if len(manifest_path.parents) >= 3 else manifest_path.parent
|
| 140 |
+
try:
|
| 141 |
+
source_labels_path = str(label_path.resolve().relative_to(project_root.resolve()))
|
| 142 |
+
except Exception:
|
| 143 |
+
source_labels_path = str(label_path)
|
| 144 |
+
|
| 145 |
+
return build_dataset_manifest_from_samples(
|
| 146 |
+
samples=samples,
|
| 147 |
+
manifest_path=manifest_path,
|
| 148 |
+
split_path=split_path,
|
| 149 |
+
source_labels_path=source_labels_path,
|
| 150 |
+
)
|
training_code/ml/training/lora_artifacts.py
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import hashlib
|
| 4 |
+
import json
|
| 5 |
+
from datetime import datetime, timezone
|
| 6 |
+
from pathlib import Path
|
| 7 |
+
from typing import Any, Dict
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
def normalize_training_config(config: Dict[str, Any]) -> Dict[str, Any]:
|
| 11 |
+
return {
|
| 12 |
+
"model_id": str(config.get("model_id", "LiquidAI/LFM2.5-VL-450M")),
|
| 13 |
+
"revision": str(config.get("revision", "main")),
|
| 14 |
+
"epochs": int(config.get("epochs", 1)),
|
| 15 |
+
"learning_rate": float(config.get("learning_rate", 2e-4)),
|
| 16 |
+
"lora_r": int(config.get("lora_r", 16)),
|
| 17 |
+
"lora_alpha": int(config.get("lora_alpha", 32)),
|
| 18 |
+
"lora_dropout": float(config.get("lora_dropout", 0.05)),
|
| 19 |
+
"dataset_manifest_path": str(config.get("dataset_manifest_path", "data/manifests/dataset_manifest_v1.json")),
|
| 20 |
+
"dataset_split_path": str(config.get("dataset_split_path", "data/manifests/dataset_splits_v1.json")),
|
| 21 |
+
}
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
def _stable_hash(payload: Dict[str, Any]) -> str:
|
| 25 |
+
canonical = json.dumps(payload, sort_keys=True, separators=(",", ":")).encode("utf-8")
|
| 26 |
+
return hashlib.sha256(canonical).hexdigest()
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
def _write_json(path: Path, payload: Dict[str, Any]) -> None:
|
| 30 |
+
path.parent.mkdir(parents=True, exist_ok=True)
|
| 31 |
+
path.write_text(json.dumps(payload, indent=2, sort_keys=True), encoding="utf-8")
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
def create_training_artifacts(
|
| 35 |
+
artifact_root: Path,
|
| 36 |
+
run_id: str,
|
| 37 |
+
artifact_volume: str,
|
| 38 |
+
config: Dict[str, Any],
|
| 39 |
+
) -> Dict[str, Any]:
|
| 40 |
+
normalized = normalize_training_config(config)
|
| 41 |
+
run_dir = artifact_root / run_id
|
| 42 |
+
checkpoint_dir = run_dir / "checkpoint-lora-v1"
|
| 43 |
+
checkpoint_dir.mkdir(parents=True, exist_ok=True)
|
| 44 |
+
|
| 45 |
+
adapter_config = {
|
| 46 |
+
"base_model_name_or_path": normalized["model_id"],
|
| 47 |
+
"peft_type": "LORA",
|
| 48 |
+
"r": normalized["lora_r"],
|
| 49 |
+
"lora_alpha": normalized["lora_alpha"],
|
| 50 |
+
"lora_dropout": normalized["lora_dropout"],
|
| 51 |
+
"inference_mode": False,
|
| 52 |
+
"task_type": "CAUSAL_LM",
|
| 53 |
+
"note": "Phase 6 scaffold checkpoint artifact.",
|
| 54 |
+
}
|
| 55 |
+
_write_json(checkpoint_dir / "adapter_config.json", adapter_config)
|
| 56 |
+
|
| 57 |
+
# Scaffold artifact to anchor downstream wiring before full trainer loop.
|
| 58 |
+
(checkpoint_dir / "adapter_model.safetensors").write_bytes(
|
| 59 |
+
json.dumps(
|
| 60 |
+
{
|
| 61 |
+
"artifact_type": "phase6.scaffold.weights",
|
| 62 |
+
"note": "Placeholder adapter blob. Replace in full LoRA trainer.",
|
| 63 |
+
},
|
| 64 |
+
sort_keys=True,
|
| 65 |
+
).encode("utf-8")
|
| 66 |
+
)
|
| 67 |
+
_write_json(
|
| 68 |
+
checkpoint_dir / "training_args.json",
|
| 69 |
+
{
|
| 70 |
+
"epochs": normalized["epochs"],
|
| 71 |
+
"learning_rate": normalized["learning_rate"],
|
| 72 |
+
"dataset_manifest_path": normalized["dataset_manifest_path"],
|
| 73 |
+
"dataset_split_path": normalized["dataset_split_path"],
|
| 74 |
+
},
|
| 75 |
+
)
|
| 76 |
+
|
| 77 |
+
reproducibility = {
|
| 78 |
+
"run_id": run_id,
|
| 79 |
+
"created_at": datetime.now(timezone.utc).isoformat(),
|
| 80 |
+
"config_hash": _stable_hash(normalized),
|
| 81 |
+
"config": normalized,
|
| 82 |
+
}
|
| 83 |
+
_write_json(run_dir / "reproducibility.json", reproducibility)
|
| 84 |
+
|
| 85 |
+
run_manifest = {
|
| 86 |
+
"run_id": run_id,
|
| 87 |
+
"created_at": datetime.now(timezone.utc).isoformat(),
|
| 88 |
+
"artifact_volume": artifact_volume,
|
| 89 |
+
"model_id": normalized["model_id"],
|
| 90 |
+
"revision": normalized["revision"],
|
| 91 |
+
"checkpoint_dir": str(checkpoint_dir),
|
| 92 |
+
"adapter_artifact_ref": f"modal-volume://{artifact_volume}/{run_id}/checkpoint-lora-v1",
|
| 93 |
+
"dataset_manifest_path": normalized["dataset_manifest_path"],
|
| 94 |
+
"dataset_split_path": normalized["dataset_split_path"],
|
| 95 |
+
"config_hash": reproducibility["config_hash"],
|
| 96 |
+
"training_mode": "phase6_scaffold",
|
| 97 |
+
}
|
| 98 |
+
manifest_path = run_dir / "run_manifest.json"
|
| 99 |
+
_write_json(manifest_path, run_manifest)
|
| 100 |
+
|
| 101 |
+
return {
|
| 102 |
+
"status": "ok",
|
| 103 |
+
"run_id": run_id,
|
| 104 |
+
"manifest_path": str(manifest_path),
|
| 105 |
+
"checkpoint_dir": str(checkpoint_dir),
|
| 106 |
+
"adapter_artifact_ref": run_manifest["adapter_artifact_ref"],
|
| 107 |
+
"artifact_volume": artifact_volume,
|
| 108 |
+
"config_hash": reproducibility["config_hash"],
|
| 109 |
+
"training_mode": "phase6_scaffold",
|
| 110 |
+
}
|
| 111 |
+
|
training_code/ml/training/modal_lora_train.py
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Phase 6 Modal GPU training scaffold with reproducible artifacts.
|
| 3 |
+
|
| 4 |
+
Usage:
|
| 5 |
+
modal run ml/training/modal_lora_train.py --config-json '{"epochs": 1}'
|
| 6 |
+
"""
|
| 7 |
+
|
| 8 |
+
from __future__ import annotations
|
| 9 |
+
|
| 10 |
+
import json
|
| 11 |
+
import os
|
| 12 |
+
import hashlib
|
| 13 |
+
from datetime import datetime, timezone
|
| 14 |
+
from pathlib import Path
|
| 15 |
+
from typing import Any, Dict
|
| 16 |
+
|
| 17 |
+
import modal
|
| 18 |
+
|
| 19 |
+
try:
|
| 20 |
+
from ml.training.lora_artifacts import create_training_artifacts
|
| 21 |
+
except Exception:
|
| 22 |
+
# Fallback for Modal remote runtime when only this file is mounted.
|
| 23 |
+
def _normalize_training_config(config: Dict[str, Any]) -> Dict[str, Any]:
|
| 24 |
+
return {
|
| 25 |
+
"model_id": str(config.get("model_id", "LiquidAI/LFM2.5-VL-450M")),
|
| 26 |
+
"revision": str(config.get("revision", "main")),
|
| 27 |
+
"epochs": int(config.get("epochs", 1)),
|
| 28 |
+
"learning_rate": float(config.get("learning_rate", 2e-4)),
|
| 29 |
+
"lora_r": int(config.get("lora_r", 16)),
|
| 30 |
+
"lora_alpha": int(config.get("lora_alpha", 32)),
|
| 31 |
+
"lora_dropout": float(config.get("lora_dropout", 0.05)),
|
| 32 |
+
"dataset_manifest_path": str(
|
| 33 |
+
config.get("dataset_manifest_path", "data/manifests/dataset_manifest_v1.json")
|
| 34 |
+
),
|
| 35 |
+
"dataset_split_path": str(config.get("dataset_split_path", "data/manifests/dataset_splits_v1.json")),
|
| 36 |
+
}
|
| 37 |
+
|
| 38 |
+
def _stable_hash(payload: Dict[str, Any]) -> str:
|
| 39 |
+
canonical = json.dumps(payload, sort_keys=True, separators=(",", ":")).encode("utf-8")
|
| 40 |
+
return hashlib.sha256(canonical).hexdigest()
|
| 41 |
+
|
| 42 |
+
def _write_json(path: Path, payload: Dict[str, Any]) -> None:
|
| 43 |
+
path.parent.mkdir(parents=True, exist_ok=True)
|
| 44 |
+
path.write_text(json.dumps(payload, indent=2, sort_keys=True), encoding="utf-8")
|
| 45 |
+
|
| 46 |
+
def create_training_artifacts(
|
| 47 |
+
artifact_root: Path,
|
| 48 |
+
run_id: str,
|
| 49 |
+
artifact_volume: str,
|
| 50 |
+
config: Dict[str, Any],
|
| 51 |
+
) -> Dict[str, Any]:
|
| 52 |
+
normalized = _normalize_training_config(config)
|
| 53 |
+
run_dir = artifact_root / run_id
|
| 54 |
+
checkpoint_dir = run_dir / "checkpoint-lora-v1"
|
| 55 |
+
checkpoint_dir.mkdir(parents=True, exist_ok=True)
|
| 56 |
+
|
| 57 |
+
adapter_config = {
|
| 58 |
+
"base_model_name_or_path": normalized["model_id"],
|
| 59 |
+
"peft_type": "LORA",
|
| 60 |
+
"r": normalized["lora_r"],
|
| 61 |
+
"lora_alpha": normalized["lora_alpha"],
|
| 62 |
+
"lora_dropout": normalized["lora_dropout"],
|
| 63 |
+
"inference_mode": False,
|
| 64 |
+
"task_type": "CAUSAL_LM",
|
| 65 |
+
"note": "Phase 6 scaffold checkpoint artifact.",
|
| 66 |
+
}
|
| 67 |
+
_write_json(checkpoint_dir / "adapter_config.json", adapter_config)
|
| 68 |
+
(checkpoint_dir / "adapter_model.safetensors").write_bytes(
|
| 69 |
+
json.dumps(
|
| 70 |
+
{
|
| 71 |
+
"artifact_type": "phase6.scaffold.weights",
|
| 72 |
+
"note": "Placeholder adapter blob. Replace in full LoRA trainer.",
|
| 73 |
+
},
|
| 74 |
+
sort_keys=True,
|
| 75 |
+
).encode("utf-8")
|
| 76 |
+
)
|
| 77 |
+
_write_json(
|
| 78 |
+
checkpoint_dir / "training_args.json",
|
| 79 |
+
{
|
| 80 |
+
"epochs": normalized["epochs"],
|
| 81 |
+
"learning_rate": normalized["learning_rate"],
|
| 82 |
+
"dataset_manifest_path": normalized["dataset_manifest_path"],
|
| 83 |
+
"dataset_split_path": normalized["dataset_split_path"],
|
| 84 |
+
},
|
| 85 |
+
)
|
| 86 |
+
config_hash = _stable_hash(normalized)
|
| 87 |
+
_write_json(
|
| 88 |
+
run_dir / "run_manifest.json",
|
| 89 |
+
{
|
| 90 |
+
"run_id": run_id,
|
| 91 |
+
"created_at": datetime.now(timezone.utc).isoformat(),
|
| 92 |
+
"artifact_volume": artifact_volume,
|
| 93 |
+
"model_id": normalized["model_id"],
|
| 94 |
+
"revision": normalized["revision"],
|
| 95 |
+
"checkpoint_dir": str(checkpoint_dir),
|
| 96 |
+
"adapter_artifact_ref": f"modal-volume://{artifact_volume}/{run_id}/checkpoint-lora-v1",
|
| 97 |
+
"dataset_manifest_path": normalized["dataset_manifest_path"],
|
| 98 |
+
"dataset_split_path": normalized["dataset_split_path"],
|
| 99 |
+
"config_hash": config_hash,
|
| 100 |
+
"training_mode": "phase6_scaffold",
|
| 101 |
+
},
|
| 102 |
+
)
|
| 103 |
+
return {
|
| 104 |
+
"status": "ok",
|
| 105 |
+
"run_id": run_id,
|
| 106 |
+
"manifest_path": str(run_dir / "run_manifest.json"),
|
| 107 |
+
"checkpoint_dir": str(checkpoint_dir),
|
| 108 |
+
"adapter_artifact_ref": f"modal-volume://{artifact_volume}/{run_id}/checkpoint-lora-v1",
|
| 109 |
+
"artifact_volume": artifact_volume,
|
| 110 |
+
"config_hash": config_hash,
|
| 111 |
+
"training_mode": "phase6_scaffold",
|
| 112 |
+
}
|
| 113 |
+
|
| 114 |
+
|
| 115 |
+
def _gpu_from_env():
|
| 116 |
+
gpu_name = os.getenv("MODAL_GPU", "T4").strip().upper()
|
| 117 |
+
allowed = {"T4", "L4", "A10G", "A100"}
|
| 118 |
+
return gpu_name if gpu_name in allowed else "T4"
|
| 119 |
+
|
| 120 |
+
|
| 121 |
+
APP_NAME = os.getenv("MODAL_APP_NAME", "landfillsentry-lora-train")
|
| 122 |
+
VOLUME_NAME = os.getenv("MODAL_ARTIFACT_VOLUME", "landfillsentry-model-artifacts")
|
| 123 |
+
ARTIFACT_ROOT = Path("/artifacts")
|
| 124 |
+
|
| 125 |
+
image = (
|
| 126 |
+
modal.Image.debian_slim(python_version="3.11")
|
| 127 |
+
.pip_install(
|
| 128 |
+
"torch",
|
| 129 |
+
"transformers",
|
| 130 |
+
"accelerate",
|
| 131 |
+
"peft",
|
| 132 |
+
"trl",
|
| 133 |
+
"datasets",
|
| 134 |
+
"safetensors",
|
| 135 |
+
"sentencepiece",
|
| 136 |
+
)
|
| 137 |
+
)
|
| 138 |
+
volume = modal.Volume.from_name(VOLUME_NAME, create_if_missing=True)
|
| 139 |
+
app = modal.App(APP_NAME)
|
| 140 |
+
|
| 141 |
+
|
| 142 |
+
@app.function(image=image, gpu=_gpu_from_env(), timeout=60 * 10, volumes={str(ARTIFACT_ROOT): volume})
|
| 143 |
+
def gpu_smoke() -> Dict:
|
| 144 |
+
import torch
|
| 145 |
+
|
| 146 |
+
cuda_available = bool(torch.cuda.is_available())
|
| 147 |
+
gpu_name = torch.cuda.get_device_name(0) if cuda_available else "cpu"
|
| 148 |
+
return {
|
| 149 |
+
"cuda_available": cuda_available,
|
| 150 |
+
"device_name": gpu_name,
|
| 151 |
+
"torch_version": torch.__version__,
|
| 152 |
+
}
|
| 153 |
+
|
| 154 |
+
|
| 155 |
+
@app.function(image=image, gpu=_gpu_from_env(), timeout=60 * 60, volumes={str(ARTIFACT_ROOT): volume})
|
| 156 |
+
def run_lora_training(config: Dict) -> Dict:
|
| 157 |
+
"""Build reproducible Phase 6 scaffold artifacts on Modal volume."""
|
| 158 |
+
timestamp = datetime.now(timezone.utc).strftime("%Y%m%dT%H%M%SZ")
|
| 159 |
+
run_id = f"lora_run_{timestamp}"
|
| 160 |
+
result = create_training_artifacts(
|
| 161 |
+
artifact_root=ARTIFACT_ROOT,
|
| 162 |
+
run_id=run_id,
|
| 163 |
+
artifact_volume=VOLUME_NAME,
|
| 164 |
+
config=config,
|
| 165 |
+
)
|
| 166 |
+
volume.commit()
|
| 167 |
+
return result
|
| 168 |
+
|
| 169 |
+
|
| 170 |
+
@app.local_entrypoint()
|
| 171 |
+
def main(config_json: str = "") -> None:
|
| 172 |
+
config = {}
|
| 173 |
+
if config_json:
|
| 174 |
+
config = json.loads(config_json)
|
| 175 |
+
|
| 176 |
+
smoke = gpu_smoke.remote()
|
| 177 |
+
print("GPU smoke:", smoke)
|
| 178 |
+
result = run_lora_training.remote(config)
|
| 179 |
+
print("Training scaffold result:", result)
|
| 180 |
+
print("TRAINING_RESULT_JSON:", json.dumps(result, sort_keys=True))
|
training_code/upload_hf_adapter.py
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Safely upload a PEFT LoRA adapter folder to Hugging Face.
|
| 2 |
+
|
| 3 |
+
This script intentionally refuses to upload the project root. A model upload
|
| 4 |
+
should contain only publishable adapter artifacts and documentation, never
|
| 5 |
+
`.env.local`, caches, logs, databases, or source checkouts.
|
| 6 |
+
"""
|
| 7 |
+
|
| 8 |
+
from __future__ import annotations
|
| 9 |
+
|
| 10 |
+
import argparse
|
| 11 |
+
import os
|
| 12 |
+
import sys
|
| 13 |
+
from pathlib import Path
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
PROJECT_ROOT = Path(__file__).resolve().parents[1]
|
| 17 |
+
DEFAULT_REPO_ID = "akashreddy2103/landfill"
|
| 18 |
+
REQUIRED_ADAPTER_FILES = ("adapter_config.json", "adapter_model.safetensors")
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
def _load_env_file(path: Path) -> None:
|
| 22 |
+
if not path.exists():
|
| 23 |
+
return
|
| 24 |
+
for raw_line in path.read_text(encoding="utf-8").splitlines():
|
| 25 |
+
line = raw_line.strip()
|
| 26 |
+
if not line or line.startswith("#") or "=" not in line:
|
| 27 |
+
continue
|
| 28 |
+
key, value = line.split("=", 1)
|
| 29 |
+
os.environ.setdefault(key.strip(), value.strip().strip('"').strip("'"))
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
def _token_configured() -> bool:
|
| 33 |
+
return bool(os.getenv("HF_TOKEN", "").strip() or os.getenv("HUGGINGFACE_TOKEN", "").strip())
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
def _candidate_tokens() -> list[tuple[str, str]]:
|
| 37 |
+
tokens: list[tuple[str, str]] = []
|
| 38 |
+
seen: set[str] = set()
|
| 39 |
+
for name in ("HUGGINGFACE_TOKEN", "HF_TOKEN"):
|
| 40 |
+
value = os.getenv(name, "").strip()
|
| 41 |
+
if value and value not in seen:
|
| 42 |
+
tokens.append((name, value))
|
| 43 |
+
seen.add(value)
|
| 44 |
+
return tokens
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
def _validate_adapter_dir(adapter_dir: Path) -> None:
|
| 48 |
+
resolved = adapter_dir.resolve()
|
| 49 |
+
if resolved == PROJECT_ROOT.resolve():
|
| 50 |
+
raise SystemExit("Refusing to upload the project root. Pass a folder containing only adapter files.")
|
| 51 |
+
missing = [name for name in REQUIRED_ADAPTER_FILES if not (resolved / name).exists()]
|
| 52 |
+
if missing:
|
| 53 |
+
raise SystemExit(
|
| 54 |
+
"Adapter folder is missing required files: "
|
| 55 |
+
+ ", ".join(missing)
|
| 56 |
+
+ f"\nExpected a PEFT adapter folder, got: {resolved}"
|
| 57 |
+
)
|
| 58 |
+
|
| 59 |
+
|
| 60 |
+
def _ensure_model_card(adapter_dir: Path, repo_id: str) -> None:
|
| 61 |
+
readme = adapter_dir / "README.md"
|
| 62 |
+
if readme.exists():
|
| 63 |
+
return
|
| 64 |
+
readme.write_text(
|
| 65 |
+
f"""---
|
| 66 |
+
library_name: peft
|
| 67 |
+
base_model: LiquidAI/LFM2.5-VL-450M
|
| 68 |
+
tags:
|
| 69 |
+
- peft
|
| 70 |
+
- lora
|
| 71 |
+
- vision-language
|
| 72 |
+
- satellite-imagery
|
| 73 |
+
- methane-monitoring
|
| 74 |
+
---
|
| 75 |
+
|
| 76 |
+
# LandfillSentry LFM2.5-VL LoRA Adapter
|
| 77 |
+
|
| 78 |
+
Repository: `{repo_id}`
|
| 79 |
+
|
| 80 |
+
This adapter is intended for LandfillSentry landfill methane/plume triage with
|
| 81 |
+
DPhi SimSat satellite imagery. See the project repository docs for dataset
|
| 82 |
+
construction, evaluation, and limitations:
|
| 83 |
+
|
| 84 |
+
- `docs/fine_tuning_methodology.md`
|
| 85 |
+
- `docs/benchmark_summary_for_submission.md`
|
| 86 |
+
- `data/manifests/dataset_manifest_v1.json`
|
| 87 |
+
- `data/manifests/phase7_evaluation_report.json`
|
| 88 |
+
|
| 89 |
+
Base model: `LiquidAI/LFM2.5-VL-450M`.
|
| 90 |
+
""",
|
| 91 |
+
encoding="utf-8",
|
| 92 |
+
)
|
| 93 |
+
|
| 94 |
+
|
| 95 |
+
def upload_adapter(adapter_dir: Path, repo_id: str) -> None:
|
| 96 |
+
_load_env_file(PROJECT_ROOT / ".env.local")
|
| 97 |
+
_validate_adapter_dir(adapter_dir)
|
| 98 |
+
_ensure_model_card(adapter_dir, repo_id)
|
| 99 |
+
|
| 100 |
+
try:
|
| 101 |
+
from huggingface_hub import HfApi, upload_folder
|
| 102 |
+
except Exception as exc:
|
| 103 |
+
raise SystemExit(f"huggingface_hub is not installed or importable: {exc}") from exc
|
| 104 |
+
|
| 105 |
+
tokens = _candidate_tokens()
|
| 106 |
+
if not tokens:
|
| 107 |
+
raise SystemExit("Missing HF_TOKEN or HUGGINGFACE_TOKEN in environment/.env.local")
|
| 108 |
+
|
| 109 |
+
last_error: Exception | None = None
|
| 110 |
+
for token_name, token in tokens:
|
| 111 |
+
try:
|
| 112 |
+
api = HfApi(token=token)
|
| 113 |
+
api.create_repo(repo_id=repo_id, repo_type="model", exist_ok=True)
|
| 114 |
+
upload_folder(
|
| 115 |
+
folder_path=str(adapter_dir.resolve()),
|
| 116 |
+
repo_id=repo_id,
|
| 117 |
+
repo_type="model",
|
| 118 |
+
token=token,
|
| 119 |
+
ignore_patterns=[
|
| 120 |
+
".env*",
|
| 121 |
+
"__pycache__/",
|
| 122 |
+
"*.pyc",
|
| 123 |
+
"*.db",
|
| 124 |
+
"*.log",
|
| 125 |
+
"data/cache/",
|
| 126 |
+
"data/logs/",
|
| 127 |
+
"data/tmp/",
|
| 128 |
+
],
|
| 129 |
+
)
|
| 130 |
+
print(f"Uploaded adapter folder to https://huggingface.co/{repo_id}")
|
| 131 |
+
print(f"Token used: {token_name}")
|
| 132 |
+
print(f"Set HF_ADAPTER_ID={repo_id}")
|
| 133 |
+
return
|
| 134 |
+
except Exception as exc:
|
| 135 |
+
last_error = exc
|
| 136 |
+
print(f"Upload attempt with {token_name} failed: {type(exc).__name__}")
|
| 137 |
+
|
| 138 |
+
raise SystemExit(f"All configured Hugging Face tokens failed to upload. Last error: {last_error}")
|
| 139 |
+
|
| 140 |
+
|
| 141 |
+
def main() -> int:
|
| 142 |
+
parser = argparse.ArgumentParser(description="Upload a PEFT adapter folder to Hugging Face.")
|
| 143 |
+
parser.add_argument("--adapter-dir", required=True, help="Folder containing adapter_config.json and adapter_model.safetensors")
|
| 144 |
+
parser.add_argument("--repo-id", default=DEFAULT_REPO_ID)
|
| 145 |
+
args = parser.parse_args()
|
| 146 |
+
|
| 147 |
+
if not _token_configured():
|
| 148 |
+
_load_env_file(PROJECT_ROOT / ".env.local")
|
| 149 |
+
upload_adapter(Path(args.adapter_dir), args.repo_id)
|
| 150 |
+
return 0
|
| 151 |
+
|
| 152 |
+
|
| 153 |
+
if __name__ == "__main__":
|
| 154 |
+
raise SystemExit(main())
|
tuned_checkpoint_v1.json
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"generated_by": "scripts/modal_gpu_check.py",
|
| 3 |
+
"record_version": "phase6.checkpoint.v1",
|
| 4 |
+
"result": {
|
| 5 |
+
"adapter_artifact_ref": "modal-volume://landfillsentry-model-artifacts/lora_run_20260428T165129Z/checkpoint-lora-v1",
|
| 6 |
+
"artifact_volume": "landfillsentry-model-artifacts",
|
| 7 |
+
"checkpoint_dir": "/artifacts/lora_run_20260428T165129Z/checkpoint-lora-v1",
|
| 8 |
+
"config_hash": "73365c006f6dc2b12e48b4535906b30e4f28ec0a46b78037da4f16e413d473c7",
|
| 9 |
+
"manifest_path": "/artifacts/lora_run_20260428T165129Z/run_manifest.json",
|
| 10 |
+
"run_id": "lora_run_20260428T165129Z",
|
| 11 |
+
"status": "ok",
|
| 12 |
+
"training_mode": "phase6_scaffold"
|
| 13 |
+
}
|
| 14 |
+
}
|