| from __future__ import annotations |
|
|
| import argparse |
| import json |
| import sys |
| from collections.abc import Callable, Sequence |
| from dataclasses import asdict |
| from datetime import UTC, datetime |
| from pathlib import Path |
| from typing import Any, TextIO |
|
|
| from .ajimee import ( |
| AJIMEE_LICENSE, |
| AJIMEE_REPOSITORY, |
| AjimeeArtifact, |
| audit_ajimee_boundary, |
| load_pinned_items, |
| ) |
| from .mozc import MozcDictionaryIndex |
|
|
| ArtifactLoader = Callable[[Path], AjimeeArtifact] |
|
|
|
|
| def _parser() -> argparse.ArgumentParser: |
| parser = argparse.ArgumentParser(prog="deberta-ime-audit-ajimee") |
| parser.add_argument("--index", type=Path, required=True) |
| parser.add_argument("--data-dir", type=Path, default=Path("work/data/ajimee")) |
| parser.add_argument("--output-dir", type=Path, default=Path("outputs")) |
| parser.add_argument("--stem", default="ajimee_boundary_audit") |
| return parser |
|
|
|
|
| def run( |
| argv: Sequence[str] | None = None, |
| *, |
| stdout: TextIO | None = None, |
| artifact_loader: ArtifactLoader = load_pinned_items, |
| ) -> int: |
| args = _parser().parse_args(argv) |
| artifact = artifact_loader(args.data_dir) |
| with MozcDictionaryIndex(args.index) as index: |
| audit = audit_ajimee_boundary(artifact.items, index=index) |
| report: dict[str, Any] = { |
| "schema_version": 1, |
| "generated_at": datetime.now(UTC).isoformat(), |
| "status": "LOCAL_INPUT_BOUNDARY_AUDIT", |
| "dataset": { |
| "repository": AJIMEE_REPOSITORY, |
| "revision": artifact.revision, |
| "license": AJIMEE_LICENSE, |
| "file": { |
| "url": artifact.url, |
| "sha256": artifact.sha256, |
| "size_bytes": artifact.size_bytes, |
| "cache_path": str(artifact.path), |
| }, |
| }, |
| "candidate_source": { |
| "kind": "Mozc OSS dictionary SQLite index", |
| "manifest": asdict(index.manifest), |
| }, |
| "audit": audit, |
| "claim_boundaries": [ |
| ( |
| "AJIMEE sequence accuracy is not computed without a sequence " |
| "candidate generator." |
| ), |
| "Exact full-sequence Mozc lookup is a compatibility probe, not IME accuracy.", |
| "No DeBERTa inference is performed by this audit.", |
| ], |
| } |
| args.output_dir.mkdir(parents=True, exist_ok=True) |
| json_path = args.output_dir / f"{args.stem}.json" |
| markdown_path = args.output_dir / f"{args.stem}.md" |
| json_path.write_text( |
| json.dumps(report, ensure_ascii=False, indent=2) + "\n", |
| encoding="utf-8", |
| ) |
| markdown_path.write_text(_render_markdown(report), encoding="utf-8") |
| (stdout or sys.stdout).write( |
| json.dumps( |
| {"ok": True, "json": str(json_path), "markdown": str(markdown_path)}, |
| ensure_ascii=False, |
| indent=2, |
| ) |
| + "\n" |
| ) |
| return 0 |
|
|
|
|
| def _render_markdown(report: dict[str, Any]) -> str: |
| audit = report["audit"] |
| input_chars = audit["input_chars"] |
| lookup = audit["mozc_full_sequence_lookup"] |
| return "\n".join( |
| [ |
| "# AJIMEE input-boundary audit", |
| "", |
| f"生成日時: {report['generated_at']}", |
| "", |
| f"Rows: {audit['rows']}", |
| ( |
| f"Input length: max {input_chars['maximum']} chars, " |
| f"over reranker limit {input_chars['over_reranker_limit']} rows" |
| ), |
| ( |
| "Exact full-sequence Mozc lookup: " |
| f"any {lookup['rows_with_any_candidate']}, " |
| f"ambiguous {lookup['ambiguous_candidate_rows']}" |
| ), |
| "", |
| ( |
| "Sequence accuracy: **not computed**. A sequence candidate generator " |
| "is out of scope." |
| ), |
| "", |
| "No DeBERTa inference was performed.", |
| "", |
| ] |
| ) |
|
|
|
|
| def main() -> None: |
| raise SystemExit(run()) |
|
|
|
|
| if __name__ == "__main__": |
| main() |
|
|