""" Run v3 detector (framing_v2 signals + frame_library_v3 rules) on the expanded corpus. Writes labels/labels_detector_v3.json with per-doc v3 labels. """ 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)) sys.path.insert(0, str(STUDY_DIR)) from framing import detect_voice, temporal_orientation, detect_epistemic_basis # noqa: E402 from framing_v2 import ( # noqa: E402 detect_coverage_v2, detect_named_author_citation, detect_growth_vocabulary, detect_efficiency_vocabulary, ) from frame_library_v3 import suggest_frames_v3, V3_STATUS # noqa: E402 FRAME_IDS_ALL = [f"FVS-{i:03d}" for i in range(1, 21)] def main(): with open(CORPUS_DIR / "manifest.json") as f: manifest = json.load(f) out = { "version": 3, "labeler": "detector_v3", "labeler_note": ( "framing_v2 signals (S-1 expanded uncertainty regex, S-2 " "named-author-citation, S-3 growth vocabulary, S-4 " "efficiency vocabulary) + frame_library_v3 rules " "(v2 rule revisions + signal-rewires per RULE_AUDIT + " "DESIGN_v2 ยง4)." ), "v3_status": V3_STATUS, "frame_universe": FRAME_IDS_ALL, "labels": {}, "signals": {}, } for doc in manifest["documents"]: doc_id = doc["doc_id"] path = CORPUS_DIR / f"{doc_id}.txt" if not path.exists(): continue text = path.read_text(encoding="utf-8") cov_v2 = detect_coverage_v2(text) voice = detect_voice(text) temp = temporal_orientation(text) epist = detect_epistemic_basis(text) na = detect_named_author_citation(text) gv = detect_growth_vocabulary(text) ev = detect_efficiency_vocabulary(text) suggs = suggest_frames_v3(cov_v2, voice, temp, epist, na, gv, ev) suggested_ids = {s["fvs_id"] for s in suggs} out["labels"][doc_id] = { fid: {"exhibits": fid in suggested_ids} for fid in FRAME_IDS_ALL } out["signals"][doc_id] = { "coverage_v2": {k: (len(v) if isinstance(v, list) else v) for k, v in cov_v2.items() if k != "categories"}, "coverage_v2_density": { c: cov_v2["categories"][c]["density_per_1kw"] for c in cov_v2.get("categories", {}) }, "voice": voice.get("voice"), "named_author_density": na["density_per_1kw"], "named_author_matches": na["matches"], "growth_density": gv["density_per_1kw"], "efficiency_density": ev["density_per_1kw"], "temporal": { "past": temp.get("past_pct"), "present": temp.get("present_pct"), "future": temp.get("future_pct"), }, "epistemic_sourced_pct": epist.get("sourced_pct"), } n = sum(1 for v in out["labels"][doc_id].values() if v["exhibits"]) sigs_brief = f"[na={na['density_per_1kw']}, gv={gv['density_per_1kw']}, ev={ev['density_per_1kw']}, unc={cov_v2['categories'].get('uncertainty',{}).get('density_per_1kw',0)}]" print(f" {doc_id}: {n} frames {sorted([fid for fid, e in out['labels'][doc_id].items() if e['exhibits']])} {sigs_brief}") with open(LABELS_DIR / "labels_detector_v3.json", "w") as f: json.dump(out, f, indent=2) print(f"\nWritten: {LABELS_DIR / 'labels_detector_v3.json'}") if __name__ == "__main__": main()