"""AIFlow Math Ink 0.6의 Android low/mid/high 실기기 benchmark를 AND gate로 요약한다.""" from __future__ import annotations import argparse from datetime import datetime, timezone import hashlib import json from pathlib import Path import re from typing import Any, Sequence REQUIRED_TIERS06 = frozenset({"low", "mid", "high"}) def model_bundle_sha25606(online_sha256: str, raster_sha256: str) -> str: """필요 변수: 두 flatbuffer hash. 작동 원리: Android와 같은 순서·UTF-8 계약으로 bundle 지문을 만든다.""" payload = f"online:{online_sha256}\nraster:{raster_sha256}\n".encode("utf-8") return hashlib.sha256(payload).hexdigest() def summarize_android_benchmarks06( reports: Sequence[dict[str, Any]], ) -> dict[str, Any]: """필요 변수: 세 기기 benchmark report. 작동 원리: 동일 모델·고유 tier·전 metric gate를 AND로 검증한다.""" tiers = [str(report.get("device_tier") or "") for report in reports] if len(reports) != 3 or set(tiers) != REQUIRED_TIERS06 or len(set(tiers)) != 3: raise ValueError("Android benchmark는 low·mid·high report가 정확히 하나씩 필요합니다.") if any( report.get("schema") != "aiflow-math-ink-06-android-benchmark-v1" for report in reports ): raise ValueError("지원하지 않는 Android benchmark schema입니다.") versions = {str(report.get("model_version") or "") for report in reports} online_hashes = {str(report.get("online_model_sha256") or "") for report in reports} raster_hashes = {str(report.get("raster_model_sha256") or "") for report in reports} bundle_hashes = {str(report.get("model_bundle_sha256") or "") for report in reports} if len(versions) != 1 or "" in versions: raise ValueError("세 기기의 model_version이 동일하고 비어 있지 않아야 합니다.") for name, hashes in ( ("online", online_hashes), ("raster", raster_hashes), ("bundle", bundle_hashes), ): if ( len(hashes) != 1 or "" in hashes or re.fullmatch(r"[0-9a-f]{64}", next(iter(hashes))) is None ): raise ValueError(f"세 기기의 {name} SHA-256이 동일하고 유효해야 합니다.") expected_bundle = model_bundle_sha25606( next(iter(online_hashes)), next(iter(raster_hashes)), ) if next(iter(bundle_hashes)) != expected_bundle: raise ValueError("model_bundle_sha256이 online/raster ordered pair와 일치하지 않습니다.") ordered = sorted(reports, key=lambda report: ("low", "mid", "high").index(report["device_tier"])) tier_rows: dict[str, Any] = {} for report in ordered: checks = report.get("checks") or {} required_checks = { "online_p95", "raster_p95", "peak_memory", "battery_measurement", } if set(checks) != required_checks: raise ValueError(f"{report['device_tier']} report의 metric check 계약이 다릅니다.") if report.get("product_validation") is not False: raise ValueError("개별 benchmark가 임의로 product_validation을 true로 만들 수 없습니다.") tier_rows[report["device_tier"]] = { "device": report["device"], "online_p95_ms": float(report["online"]["p95_ms"]), "raster_p95_ms": float(report["raster"]["p95_ms"]), "peak_pss_bytes": int(report["peak_pss_bytes"]), "battery_charge_delta_micro_ah": report["battery_charge_delta_micro_ah"], "checks": checks, "passed": bool(report.get("gate_passed") and all(checks.values())), } all_passed = all(row["passed"] for row in tier_rows.values()) return { "schema": "aiflow-math-ink-06-android-3tier-summary-v1", "generated_at": datetime.now(timezone.utc).isoformat(), "model_version": next(iter(versions)), "online_model_sha256": next(iter(online_hashes)), "raster_model_sha256": next(iter(raster_hashes)), "model_bundle_sha256": next(iter(bundle_hashes)), "tiers": tier_rows, "android_hardware_validation": all_passed, "android_release_gate_passed": all_passed, "product_validation": False, "next_gate": ( "P writer/device-disjoint model gate와 Android gate를 함께 release manifest에서 결합" if all_passed else "실패 tier의 latency·memory·battery를 개선한 뒤 같은 model SHA로 재측정" ), } def main() -> None: """필요 변수: tier별 UTF-8 JSON report·출력. 작동 원리: 검증된 3-tier summary를 원자적으로 기록한다.""" parser = argparse.ArgumentParser(description="Summarize Math Ink 0.6 Android benchmarks") parser.add_argument("--report", type=Path, action="append", required=True) parser.add_argument("--output", type=Path, required=True) args = parser.parse_args() reports = [ json.loads(path.read_text(encoding="utf-8")) for path in args.report ] summary = summarize_android_benchmarks06(reports) args.output.parent.mkdir(parents=True, exist_ok=True) temporary = args.output.with_suffix(args.output.suffix + ".part") temporary.write_text( json.dumps(summary, ensure_ascii=False, indent=2) + "\n", encoding="utf-8", ) temporary.replace(args.output) print(json.dumps(summary, ensure_ascii=False, indent=2)) if __name__ == "__main__": main()