File size: 8,421 Bytes
9be8fa9 5ee3a5e 9be8fa9 c228b1d 9be8fa9 5ee3a5e c228b1d 9be8fa9 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 | """AIFlow Math Ink 0.6 LiteRT ๋ณํ์ ํ์ํ ์ต์ Colab ZIP์ ๊ฒฐ์ ์ ์ผ๋ก ๋ง๋ ๋ค."""
from __future__ import annotations
import argparse
from datetime import datetime, timezone
from hashlib import sha256
import json
from pathlib import Path, PurePosixPath
from zipfile import ZIP_DEFLATED, ZIP_STORED, ZipFile, ZipInfo
PROJECT_ROOT = Path(__file__).parents[1]
MANIFEST_NAME_06 = "LITERT_COLAB_BUNDLE_MANIFEST.json"
def _sha256_bytes06(value: bytes) -> str:
"""ํ์ ๋ณ์: file bytes. ์๋ ์๋ฆฌ: bundle entry์ manifest ๊ฒ์ฆ์ฉ SHA-256์ ๋ฐํํ๋ค."""
return sha256(value).hexdigest()
def _bundle_files06(
base_checkpoint: Path, adapter_checkpoint: Path, representative_inputs: Path,
) -> dict[str, Path]:
"""ํ์ ๋ณ์: seed-17 composite artifact. ์๋ ์๋ฆฌ: converter๊ฐ importํ๋ ๋ซํ ์ต์ ํ์ผ ์งํฉ์ ๋งคํํ๋ค."""
return {
"pyproject.toml": PROJECT_ROOT / "pyproject.toml",
"src/math_grid_drawer/__init__.py": PROJECT_ROOT / "research/colab/litert_bundle_package_init.py",
"src/math_grid_drawer/research/__init__.py": PROJECT_ROOT / "src/math_grid_drawer/research/__init__.py",
"src/math_grid_drawer/research/ink06_canonical.py": PROJECT_ROOT / "src/math_grid_drawer/research/ink06_canonical.py",
"src/math_grid_drawer/research/ink06_export.py": PROJECT_ROOT / "src/math_grid_drawer/research/ink06_export.py",
"src/math_grid_drawer/research/math_ink_06.py": PROJECT_ROOT / "src/math_grid_drawer/research/math_ink_06.py",
"src/math_grid_drawer/research/raster_skeleton06.py": PROJECT_ROOT / "src/math_grid_drawer/research/raster_skeleton06.py",
"src/math_grid_drawer/research/skeleton_adapter06.py": PROJECT_ROOT / "src/math_grid_drawer/research/skeleton_adapter06.py",
"src/math_grid_drawer/research/trajectory_sequence.py": PROJECT_ROOT / "src/math_grid_drawer/research/trajectory_sequence.py",
"scripts/export_math_ink_06_litert.py": PROJECT_ROOT / "scripts/export_math_ink_06_litert.py",
"artifacts/base_378.pt": base_checkpoint,
"artifacts/online_adapter.pt": adapter_checkpoint,
"artifacts/representative_inputs.pt": representative_inputs,
}
def build_litert_colab_bundle06(
base_checkpoint: Path,
adapter_checkpoint: Path,
representative_inputs: Path,
output: Path,
) -> dict:
"""ํ์ ๋ณ์: composite checkpointยท๋ํ ์
๋ ฅยทZIP ์ถ๋ ฅ. ์๋ ์๋ฆฌ: ๊ฒฝ๋กยทํฌ๊ธฐยทํด์๊ฐ ๊ณ ์ ๋ Linux ๋ณํ bundle์ ๋ง๋ ๋ค."""
files = _bundle_files06(base_checkpoint, adapter_checkpoint, representative_inputs)
missing = [name for name, path in files.items() if not path.is_file()]
if missing:
raise FileNotFoundError(f"LiteRT bundle ํ์ ํ์ผ์ด ์์ต๋๋ค: {missing}")
entries = []
payloads: dict[str, bytes] = {}
for name, path in files.items():
payload = path.read_bytes()
payloads[name] = payload
entries.append({
"path": name, "bytes": len(payload), "sha256": _sha256_bytes06(payload),
})
manifest = {
"schema": "aiflow-math-ink-06-litert-colab-bundle-v1",
"generated_at": datetime.now(timezone.utc).isoformat(),
"track": "R_public_conversion_only",
"p_student_included": False,
"product_bundle": False,
"seed": 17,
"litert_torch_version": "0.9.1",
"representative_samples": 76,
"raster_output_contract": {
"outputs": [
{"index": 0, "name": "exact_logits", "shape": [1, 378]},
{"index": 1, "name": "coordinates", "shape": [1, 4, 128, 2]},
{"index": 2, "name": "state_logits", "shape": [1, 4, 128, 3]},
{"index": 3, "name": "progress", "shape": [1, 4, 128]},
{"index": 4, "name": "hypothesis_scores", "shape": [1, 4]},
],
"direct_raster_label_shortcut": False,
},
"files": entries,
"product_validation": False,
}
output.parent.mkdir(parents=True, exist_ok=True)
temporary = output.with_suffix(output.suffix + ".part")
with ZipFile(temporary, "w", allowZip64=True) as bundle:
for name, payload in payloads.items():
compression = ZIP_STORED if PurePosixPath(name).suffix in {".pt"} else ZIP_DEFLATED
info = ZipInfo(name, date_time=(1980, 1, 1, 0, 0, 0))
info.compress_type = compression
info.external_attr = 0o644 << 16
bundle.writestr(info, payload)
info = ZipInfo(MANIFEST_NAME_06, date_time=(1980, 1, 1, 0, 0, 0))
info.compress_type = ZIP_DEFLATED
info.external_attr = 0o644 << 16
bundle.writestr(
info, json.dumps(manifest, ensure_ascii=False, indent=2).encode("utf-8") + b"\n",
)
temporary.replace(output)
manifest["bundle"] = str(output)
manifest["bundle_bytes"] = output.stat().st_size
manifest["bundle_sha256"] = sha256(output.read_bytes()).hexdigest()
return manifest
def verify_litert_colab_bundle06(bundle_path: Path) -> dict:
"""ํ์ ๋ณ์: ์์ฑ ZIP. ์๋ ์๋ฆฌ: ์ถ์ถ ์์ด ๋ชจ๋ entry์ ์์ ๊ฒฝ๋กยทํฌ๊ธฐยทSHA-256์ ์ฌ๊ฒ์ฆํ๋ค."""
failures = []
with ZipFile(bundle_path) as bundle:
manifest = json.loads(bundle.read(MANIFEST_NAME_06).decode("utf-8"))
if (
manifest.get("track") != "R_public_conversion_only"
or manifest.get("p_student_included") is not False
or manifest.get("product_bundle") is not False
):
failures.append({"path": MANIFEST_NAME_06, "reason": "research_track_contract"})
contract = manifest.get("raster_output_contract") or {}
output_names = [
str(row.get("name") or "")
for row in contract.get("outputs", [])
]
if output_names != [
"exact_logits",
"coordinates",
"state_logits",
"progress",
"hypothesis_scores",
]:
failures.append({"path": MANIFEST_NAME_06, "reason": "raster_output_contract"})
if contract.get("direct_raster_label_shortcut") is not False:
failures.append({"path": MANIFEST_NAME_06, "reason": "raster_shortcut_contract"})
names = set(bundle.namelist())
for row in manifest["files"]:
name = str(row["path"])
path = PurePosixPath(name)
if path.is_absolute() or ".." in path.parts:
failures.append({"path": name, "reason": "unsafe_path"})
elif name not in names:
failures.append({"path": name, "reason": "missing"})
else:
payload = bundle.read(name)
if len(payload) != int(row["bytes"]):
failures.append({"path": name, "reason": "bytes"})
elif _sha256_bytes06(payload) != str(row["sha256"]):
failures.append({"path": name, "reason": "sha256"})
return {
"schema": manifest["schema"], "files": len(manifest["files"]),
"failures": failures, "passed": not failures, "product_validation": False,
}
def main() -> None:
"""ํ์ ๋ณ์: CLI artifact ๊ฒฝ๋ก. ์๋ ์๋ฆฌ: bundle ์์ฑ ์งํ archive ์์ฒด ๊ฒ์ฆ๊น์ง ์ํํ๋ค."""
parser = argparse.ArgumentParser(description="Build Math Ink 0.6 LiteRT Colab bundle")
parser.add_argument("--base-checkpoint", type=Path, required=True)
parser.add_argument("--adapter-checkpoint", type=Path, required=True)
parser.add_argument("--representative-inputs", type=Path, required=True)
parser.add_argument("--output", type=Path, required=True)
args = parser.parse_args()
report = build_litert_colab_bundle06(
args.base_checkpoint, args.adapter_checkpoint, args.representative_inputs, args.output,
)
report["verification"] = verify_litert_colab_bundle06(args.output)
if not report["verification"]["passed"]:
raise ValueError(f"LiteRT Colab bundle ๊ฒ์ฆ ์คํจ: {report['verification']['failures']}")
report_path = args.output.with_suffix(args.output.suffix + ".manifest.json")
report_path.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()
|