"""Math Ink 0.6 P-track trajectory federation의 권리·중복·coverage를 감사한다.""" from __future__ import annotations import argparse from hashlib import sha256 import json import sys from pathlib import Path import torch PROJECT_ROOT = Path(__file__).parents[1] SOURCE_ROOT = PROJECT_ROOT / "src" if str(SOURCE_ROOT) not in sys.path: sys.path.insert(0, str(SOURCE_ROOT)) from math_grid_drawer.research.ink06_federation import federation_audit06, load_product_federation06 from math_grid_drawer.research.ink06_source_registry import ( SourceRegistryEntry06, load_source_registry06, source_registry_audit06, ) def _file_sha25606(path: Path) -> str: """필요 변수: 감사 대상 checkpoint. 작동 원리: 최종 model bundle과 연결할 byte-level SHA-256을 계산한다.""" digest = sha256() with path.open("rb") as stream: for chunk in iter(lambda: stream.read(1024 * 1024), b""): digest.update(chunk) return digest.hexdigest() def build_federation_release_gate06( *, checkpoint: dict, federation: dict, registry_entries: tuple[SourceRegistryEntry06, ...], registry_audit: dict, ) -> dict: """필요 변수: checkpoint provenance·실데이터 audit·registry. 작동 원리: 200/30·중복·split 조건을 AND로 검증한다.""" declared = tuple( sorted(set(str(value) for value in checkpoint.get("training_source_ids", []))) ) by_id = {entry.source_id: entry for entry in registry_entries} unknown = sorted(source_id for source_id in declared if source_id not in by_id) ineligible = sorted( source_id for source_id in declared if source_id in by_id and ( by_id[source_id].stage != "approved" or by_id[source_id].deployment_role == "evaluation_only" ) ) groups = { by_id[source_id].independent_source_group for source_id in declared if source_id in by_id and by_id[source_id].stage == "approved" and by_id[source_id].deployment_role != "evaluation_only" and by_id[source_id].independent_source_group } checks = { "discovered_200": bool(registry_audit["discovery_gate_passed"]), "approved_independent_30": bool(registry_audit["approved_group_gate_passed"]), "checkpoint_declares_30_groups": len(groups) >= 30, "checkpoint_sources_approved": not unknown and not ineligible, "origin_overlap_zero": int(federation["origin_overlap_total"]) == 0, "trajectory_signature_overlap_zero": int( federation["trajectory_signature_overlap_total"], ) == 0, "writer_device_origin_split_leakage_zero": int( federation["split_identity_leakage_total"], ) == 0, } return { "required_discovered_sources": 200, "required_independent_groups": 30, "checkpoint_training_source_ids": list(declared), "checkpoint_training_source_count": len(declared), "checkpoint_independent_groups": len(groups), "unknown_checkpoint_sources": unknown, "ineligible_checkpoint_sources": ineligible, "checks": checks, "passed": all(checks.values()), } def main() -> None: """필요 변수: 0.6 checkpoint·registry·shard. 작동 원리: fail-closed source audit를 UTF-8 JSON으로 고정한다.""" parser = argparse.ArgumentParser(description="Audit Math Ink 0.6 product trajectory federation") parser.add_argument("--checkpoint", type=Path, required=True) parser.add_argument("--registry", type=Path, default=PROJECT_ROOT / "research/dataset_registry.json") parser.add_argument("--source-registry", type=Path, default=PROJECT_ROOT / "research/math_ink_06_source_registry.json") parser.add_argument("--commercial", type=Path, default=PROJECT_ROOT / "research/data/external_trajectory_v1/commercial_ccby4.jsonl.gz") parser.add_argument("--hwrt", type=Path, default=PROJECT_ROOT / "research/data/open_pretrain/hwrt_expanded_v2/hwrt_expanded.jsonl.gz") parser.add_argument("--approval", type=Path, default=PROJECT_ROOT / "research/approvals/HWRT-ODBL-USE-APPROVAL-v1.json") parser.add_argument("--output", type=Path, required=True) args = parser.parse_args() checkpoint = torch.load(args.checkpoint, map_location="cpu", weights_only=False) sources = load_product_federation06( registry_path=args.registry, commercial_path=args.commercial, hwrt_path=args.hwrt, approval_path=args.approval, allowed_labels=checkpoint["exact_labels"], source_registry_path=args.source_registry, ) report = federation_audit06(sources) registry_entries = load_source_registry06(args.source_registry) discovery = source_registry_audit06( registry_entries, required_discovered_sources=200, required_approved_groups=30, ) release_gate = build_federation_release_gate06( checkpoint=checkpoint, federation=report, registry_entries=registry_entries, registry_audit=discovery, ) report.update({ "schema": "aiflow-math-ink-06-federation-audit-v1", "checkpoint_sha256": _file_sha25606(args.checkpoint), "vocabulary": len(checkpoint["exact_labels"]), "source_registry": discovery, "release_source_gate": release_gate, "product_validation": False, "note": "HWRT official test는 모델 선택·writer-independent 제품 gate에 사용하지 않는다.", }) args.output.mkdir(parents=True, exist_ok=True) (args.output / "federation_audit.json").write_text( json.dumps(report, ensure_ascii=False, indent=2) + "\n", encoding="utf-8", ) print(json.dumps(report, ensure_ascii=False, indent=2)) if __name__ == "__main__": main()