"""Rangkum semua run jadi angka siap pakai untuk justifikasi resource. py -m src.knowledge_parsing.report py -m src.knowledge_parsing.report --scale 6800 `--scale` mengekstrapolasi ke jumlah halaman tertentu (mis. skala BUMA ±6.700-6.800 halaman) memakai detik/halaman yang TERUKUR — dokumen yang diambil dari cache tidak ikut dihitung, karena akan membuat angkanya terlihat jauh lebih cepat dari kenyataan. """ from __future__ import annotations import argparse import json from pathlib import Path from .config import PipelineConfig # Di bawah ini, ongkos tetap pemuatan model mendominasi sehingga detik/halaman # jauh lebih besar dari kenyataan pada dokumen besar. Terukur di mesin ini: # 1 hal -> 54,0 dtk/hal, 5 hal -> 33,2, 20 hal -> 19,0. MIN_HALAMAN_SAHIH = 20 def _jam(detik: float) -> str: if detik < 90: return f"{detik:.0f} dtk" if detik < 5400: return f"{detik/60:.1f} mnt" return f"{detik/3600:.1f} jam" def kumpulkan(runs_dir: Path) -> list[dict]: hasil = [] for m in sorted(runs_dir.glob("*/manifest.json")): try: hasil.append(json.loads(m.read_text(encoding="utf-8"))) except (OSError, json.JSONDecodeError) as e: # Manifest rusak/separuh tertulis (run yang mati di tengah). Dilewati, # tapi disebutkan — laporan yang diam-diam kehilangan satu run akan # memberi angka detik/halaman yang salah tanpa ada tandanya. print(f" ! lewati manifest tidak terbaca: {m} ({type(e).__name__})") return hasil def cetak(runs: list[dict], scale: int | None) -> None: if not runs: print("Belum ada run. Jalankan: py -m src.knowledge_parsing.run --input dokumen/") return print(f"{'run':<17} {'backend':<9} {'ver':<8} {'dok':>4} {'hal':>5} " f"{'dtk/hal':>8} {'peringatan':>11}") print("-" * 68) for r in runs: s = r.get("ringkasan", {}) cfg = r.get("config", {}) # backend yang tercatat di output MinerU lebih dipercaya daripada config tercatat = {d.get("backend_tercatat") for d in r.get("dokumen", [])} - {None} backend = "/".join(sorted(tercatat)) if tercatat else cfg.get("backend", "?") dph = s.get("detik_per_halaman") print(f"{r.get('run_id',''):<17} {backend:<9} " f"{str(r.get('mineru_version') or '?')[:7]:<8} " f"{s.get('dokumen_ok',0):>4} {s.get('total_halaman',0):>5} " f"{(f'{dph:.2f}' if dph else '-'):>8} {s.get('total_peringatan',0):>11}") terukur = [r for r in runs if (r.get("ringkasan") or {}).get("detik_per_halaman")] if not terukur: print("\n(belum ada run yang benar-benar diparse — semua dari cache)") return print("\n--- ekstrapolasi ---") target = scale or 6800 meragukan = False for r in terukur: s = r["ringkasan"] dph = s["detik_per_halaman"] hal = s.get("halaman_terukur", 0) cfg = r.get("config", {}) tanda = "" if hal < MIN_HALAMAN_SAHIH: tanda = f" ⚠ hanya {hal} hal." meragukan = True print(f" {cfg.get('backend','?'):<9} {dph:>6.2f} dtk/hal → " f"{target} halaman = {_jam(dph * target)}{tanda}") if meragukan: print( f"\n ⚠ Run dengan < {MIN_HALAMAN_SAHIH} halaman TIDAK layak diekstrapolasi.\n" " Pemuatan model adalah ongkos tetap, jadi dokumen kecil terlihat jauh\n" " lebih lambat per halaman. Terukur sebelumnya di mesin ini:\n" " 1 hal -> 54,0 dtk/hal | 5 hal -> 33,2 | 20 hal -> 19,0\n" " Pakai dokumen >= 20 halaman sebelum angkanya dibawa ke luar." ) if len(terukur) >= 2: cepat = min(terukur, key=lambda r: r["ringkasan"]["detik_per_halaman"]) lambat = max(terukur, key=lambda r: r["ringkasan"]["detik_per_halaman"]) a = lambat["ringkasan"]["detik_per_halaman"] b = cepat["ringkasan"]["detik_per_halaman"] if b: print(f"\n selisih tercepat vs terlambat: {a/b:.1f}×") def main() -> None: p = argparse.ArgumentParser(description="Ringkasan angka dari semua run") p.add_argument("--runs-dir", type=Path, default=None) p.add_argument("--scale", type=int, default=None, help="ekstrapolasi ke berapa halaman (mis. 6800)") a = p.parse_args() cfg = PipelineConfig() cetak(kumpulkan(a.runs_dir or cfg.runs_dir), a.scale) if __name__ == "__main__": main()