""" Labeler C: Frame Check detector labels. Runs framing.py + frame_library.suggest_frames on each corpus document. Produces binary per-frame labels: frame is exhibited iff it appears in the detector's output list. Per DESIGN.md ยง4: run AFTER A and B are frozen. """ from __future__ import annotations import json import sys from pathlib import Path REPO_ROOT = Path(__file__).resolve().parents[2] STUDY_DIR = REPO_ROOT / "fvs_eval" / "validation_study" CORPUS_DIR = STUDY_DIR / "corpus" LABELS_DIR = STUDY_DIR / "labels" sys.path.insert(0, str(REPO_ROOT)) from framing import ( detect_coverage, detect_voice, temporal_orientation, detect_epistemic_basis, ) from frame_library import suggest_frames FRAME_IDS = [f"FVS-{i:03d}" for i in range(1, 21)] def analyze_document(text: str) -> list: """Run full framing analysis and return list of suggested FVS IDs.""" cov = detect_coverage(text) voice = detect_voice(text) temp = temporal_orientation(text) epist = detect_epistemic_basis(text) suggestions = suggest_frames(cov, voice, temp, epist) return suggestions, { "coverage": {k: (len(v) if isinstance(v, list) else v) for k, v in cov.items() if k != "categories"}, "coverage_density": {c: cov["categories"][c]["density_per_1kw"] for c in cov.get("categories", {})}, "voice": voice.get("voice"), "voice_total_sentences": voice.get("total_sentences"), "temporal": { "past": temp.get("past_pct"), "present": temp.get("present_pct"), "future": temp.get("future_pct"), }, "epistemic_sourced_pct": epist.get("sourced_pct"), } def main(): with open(CORPUS_DIR / "manifest.json", "r", encoding="utf-8") as f: manifest = json.load(f) detector_labels = { "version": 1, "labeler": "detector", "labeler_note": ( "frame_library.suggest_frames via framing.py signals. " "Deterministic regex-based detection. Labels are binary: " "frame listed in output = exhibits, else not." ), "frame_universe": FRAME_IDS, "labels": {}, "signals": {}, } for doc in manifest["documents"]: doc_id = doc["doc_id"] doc_path = CORPUS_DIR / f"{doc_id}.txt" if not doc_path.exists(): print(f" {doc_id}: SKIP (file missing)") continue text = doc_path.read_text(encoding="utf-8") try: suggestions, signals = analyze_document(text) except Exception as e: print(f" {doc_id}: FAILED - {e}") continue suggested_ids = {s["fvs_id"] for s in suggestions} labels = {} for fid in FRAME_IDS: labels[fid] = { "exhibits": fid in suggested_ids, "signal": next( (s["signal"] for s in suggestions if s["fvs_id"] == fid), "", ) if fid in suggested_ids else "", } detector_labels["labels"][doc_id] = labels detector_labels["signals"][doc_id] = signals n_exhibits = sum(1 for v in labels.values() if v["exhibits"]) print(f" {doc_id}: {n_exhibits} frames suggested " f"({[fid for fid in FRAME_IDS if labels[fid]['exhibits']]})") out_path = LABELS_DIR / "labels_detector_v1_v3corpus.json" with open(out_path, "w", encoding="utf-8") as f: json.dump(detector_labels, f, indent=2) print(f"\nWritten: {out_path}") print(f"Total documents: {len(detector_labels['labels'])}") if __name__ == "__main__": main()