Spaces:
Runtime error
Runtime error
| """Merge independently gated real GEC and SOAP components into one private bundle.""" | |
| from __future__ import annotations | |
| import argparse | |
| import hashlib | |
| import json | |
| import shutil | |
| import sys | |
| from pathlib import Path | |
| ROOT = Path(__file__).resolve().parents[3] | |
| sys.path.insert(0, str(ROOT / "scribe" / "training")) | |
| from soap.bundle import validate_bundle # noqa: E402 | |
| def main() -> None: | |
| parser = argparse.ArgumentParser(description=__doc__) | |
| parser.add_argument("--soap-bundle", type=Path, required=True) | |
| component = parser.add_mutually_exclusive_group(required=True) | |
| component.add_argument("--gec-bundle", type=Path) | |
| component.add_argument("--asr-component", type=Path) | |
| parser.add_argument("--output", type=Path, required=True) | |
| parser.add_argument("--confirm-stack", action="store_true") | |
| args = parser.parse_args() | |
| if not args.confirm_stack: | |
| raise SystemExit("Pass --confirm-stack after both components independently pass their gates") | |
| soap = validate_bundle(args.soap_bundle.resolve()) | |
| if args.output.exists(): | |
| shutil.rmtree(args.output) | |
| shutil.copytree(args.soap_bundle.resolve(), args.output) | |
| (args.output / "reports").mkdir() | |
| if args.gec_bundle: | |
| _merge_gec(soap, args.gec_bundle.resolve(), args.output) | |
| else: | |
| _record_direct_asr(soap, args.asr_component.resolve(), args.output) | |
| manifest_path = args.output / "scribe_manifest.json" | |
| manifest_path.unlink(missing_ok=True) | |
| soap["files"] = { | |
| str(path.relative_to(args.output)).replace("\\", "/"): hashlib.sha256( | |
| path.read_bytes() | |
| ).hexdigest() | |
| for path in sorted(args.output.rglob("*")) | |
| if path.is_file() | |
| } | |
| manifest_path.write_text( | |
| json.dumps(soap, ensure_ascii=False, indent=2, sort_keys=True), encoding="utf-8" | |
| ) | |
| validate_bundle(args.output) | |
| print(f"Wrote independently gated Scribe research bundle to {args.output}") | |
| def _merge_gec(soap: dict, gec_path: Path, output: Path) -> None: | |
| gec = json.loads((gec_path / "serve_manifest.json").read_text(encoding="utf-8")) | |
| if gec.get("schema") != "carepath.gec.serve/1" or gec.get("gate_accepted") is not True: | |
| raise SystemExit("GEC bundle lacks an independently accepted safety gate") | |
| if gec.get("base_model") != soap["base_model"]: | |
| raise SystemExit("GEC and SOAP adapters do not share the same base model") | |
| if gec.get("base_revision") and gec["base_revision"] != soap.get("base_revision"): | |
| raise SystemExit("GEC and SOAP adapters do not share the same base revision") | |
| adapter, datastore = gec_path / gec["adapter_dir"], gec_path / gec["datastore"] | |
| if not adapter.is_dir() or not datastore.is_file(): | |
| raise SystemExit("GEC bundle is missing its adapter or retrieval datastore") | |
| shutil.copytree(adapter, output / "adapters" / "gec") | |
| (output / "retrieval").mkdir() | |
| shutil.copy2(datastore, output / "retrieval" / "term_datastore.json") | |
| shutil.copy2(gec_path / "serve_manifest.json", output / "reports" / "gec_manifest.json") | |
| soap["adapters"]["gec"] = "adapters/gec" | |
| soap["correction_mode"] = "adapter" | |
| soap.setdefault("prompts", {})["gec"] = gec["prompt"]["system"] | |
| soap.setdefault("max_new_tokens", {})["gec"] = int(gec.get("max_new_tokens", 256)) | |
| soap["retrieval"] = {"datastore": "retrieval/term_datastore.json"} | |
| soap["transcript_component"] = {"kind": "gec_adapter", "gate": "reports/gec_manifest.json"} | |
| soap["component_gates"] = {"soap": "evaluation.json", "gec": "reports/gec_manifest.json"} | |
| def _record_direct_asr(soap: dict, component_path: Path, output: Path) -> None: | |
| if not component_path.is_dir(): | |
| raise SystemExit("--asr-component must be a gated component directory") | |
| component_manifest = component_path / "asr_component.json" | |
| component = json.loads(component_manifest.read_text(encoding="utf-8")) | |
| if component.get("schema") != "carepath.asr.component/1": | |
| raise SystemExit("unsupported direct-ASR component schema") | |
| if component.get("gate_accepted") is not True: | |
| raise SystemExit("direct-ASR component lacks an independently accepted safety gate") | |
| if component.get("selected_for_serving") is not True: | |
| raise SystemExit("direct-ASR component did not win final transcript selection") | |
| if component.get("usage_scope") != "research_only": | |
| raise SystemExit("direct-ASR component must remain research_only") | |
| revision = str(component.get("revision", "")) | |
| if ( | |
| len(revision) != 40 | |
| or set(revision.lower()) - set("0123456789abcdef") | |
| or component.get("tokenizer_revision") != revision | |
| ): | |
| raise SystemExit("direct-ASR component requires exact matching model/tokenizer revisions") | |
| files = component.get("files") | |
| if not isinstance(files, dict) or not files: | |
| raise SystemExit("direct-ASR component must contain hashed model/tokenizer files") | |
| for relative, expected in files.items(): | |
| path = component_path / relative | |
| if not path.is_file() or hashlib.sha256(path.read_bytes()).hexdigest() != expected: | |
| raise SystemExit(f"direct-ASR component file hash mismatch: {relative}") | |
| for role in ("adapter", "tokenizer", "metrics", "staging_evidence"): | |
| relative = str(component.get(role, "")) | |
| if not relative or relative not in files: | |
| raise SystemExit(f"direct-ASR component manifest must hash its {role} artifact") | |
| evidence_path = component_path / str(component.get("staging_evidence", "")) | |
| try: | |
| evidence = json.loads(evidence_path.read_text(encoding="utf-8")) | |
| except (OSError, json.JSONDecodeError) as exc: | |
| raise SystemExit("direct-ASR component requires real-GPU staging evidence") from exc | |
| if evidence.get("status") != "passed" or evidence.get("real_gpu") is not True: | |
| raise SystemExit("direct-ASR component real-GPU staging evidence has not passed") | |
| bundled = output / "transcript" / "asr" | |
| bundled.parent.mkdir() | |
| shutil.copytree(component_path, bundled) | |
| soap["correction_mode"] = "identity" | |
| soap["transcript_component"] = { | |
| "kind": "direct_asr", | |
| "model": component.get("model"), | |
| "revision": component.get("revision"), | |
| "path": "transcript/asr", | |
| "manifest": "transcript/asr/asr_component.json", | |
| "staging_evidence": f"transcript/asr/{component['staging_evidence']}", | |
| } | |
| soap["component_gates"] = {"soap": "evaluation.json", "asr": "transcript/asr/asr_component.json"} | |
| if __name__ == "__main__": | |
| main() | |