Download ocr_release.py from vomebook/Search: direct link, hf CLI and curl.
- Browser
- Download file 15.2 kB
-
https://huggingface.co/spaces/vomebook/Search/resolve/main/ocr_release.py
- Command line
-
hf download hf://spaces/vomebook/Search/ocr_release.py
-
curl -L -o ocr_release.py https://huggingface.co/spaces/vomebook/Search/resolve/main/ocr_release.py
15.2 kB
| """Strict, startup-pinned OCR text/index releases. No network, pointers or deletion.""" | |
| from __future__ import annotations | |
| from collections import defaultdict | |
| import hashlib | |
| import json | |
| import os | |
| from pathlib import Path, PurePosixPath | |
| import re | |
| import shutil | |
| import sqlite3 | |
| import tempfile | |
| import unicodedata | |
| BUCKET = "vomebook/ccrd-index" | |
| TOKENIZER = "cjk-bigram-boundary-fts5-v6-snippet-anchors" | |
| SOURCE = "OCR" | |
| SOURCE_NAME = "VoiceOfML" | |
| def canonical(value) -> bytes: | |
| return (json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":"), allow_nan=False) + "\n").encode() | |
| def digest(value) -> str: | |
| return hashlib.sha256(value).hexdigest() | |
| def sha(value) -> str: | |
| if not isinstance(value, str) or not re.fullmatch(r"[0-9a-f]{64}", value): | |
| raise ValueError("invalid release SHA-256") | |
| return value | |
| def safe_path(value: str) -> str: | |
| if (not isinstance(value, str) or not value or value.startswith("/") or "\\" in value | |
| or any(part in {"", ".", ".."} for part in value.split("/")) | |
| or any(ord(char) < 32 for char in value)): | |
| raise ValueError("invalid release relative path") | |
| return value | |
| def read_resource(root: Path, ref: dict) -> bytes: | |
| if not isinstance(ref, dict) or ref.get("bucket") != BUCKET: | |
| raise ValueError("wrong OCR release bucket") | |
| path = safe_path(ref.get("path")) | |
| if not path.startswith(("text/objects/", "text/exports/", "indexes/ocr/")): | |
| raise ValueError("unmanaged OCR resource path") | |
| expected = sha(ref.get("sha256")) | |
| size = ref.get("bytes") | |
| if type(size) is not int or size < 1: | |
| raise ValueError("invalid OCR resource size") | |
| root = root.resolve() | |
| target = (root / path).resolve() | |
| if root not in target.parents or not target.is_file(): | |
| raise ValueError("missing OCR release resource or path escape") | |
| raw = target.read_bytes() | |
| if len(raw) != size or digest(raw) != expected: | |
| raise ValueError("OCR release resource checksum mismatch") | |
| return raw | |
| def export_record(root: Path, ref: dict) -> tuple[dict, dict]: | |
| export = json.loads(read_resource(root, ref)) | |
| if (export.get("kind") != "ccrd-ocr-text-export" or export.get("version") != 1 | |
| or export.get("complete") is not True or export.get("recipe") != "ocr-plain-utf8-pages-v1"): | |
| raise ValueError("unsupported OCR export schema") | |
| if not re.fullmatch(r"OCR:[1-9][0-9]*", str(export.get("doc_id", ""))) or int(export["doc_id"].split(":")[1]) >= 1 << 63: | |
| raise ValueError("invalid OCR document identity") | |
| repo, original = export.get("origin_repo"), safe_path(export.get("origin_rel_path")) | |
| if not isinstance(repo, str) or not re.fullmatch(r"[A-Za-z0-9][A-Za-z0-9_.-]*/[A-Za-z0-9][A-Za-z0-9_.-]*", repo): | |
| raise ValueError("invalid OCR origin repository") | |
| if not re.fullmatch(r"[0-9a-f]{40}", str(export.get("origin_revision", ""))): | |
| raise ValueError("OCR origin must be commit-pinned") | |
| readable = safe_path(export.get("readable_rel_path")) | |
| expected_path = repo + "/" + str(PurePosixPath(original).with_suffix(".txt")) | |
| if readable != expected_path: | |
| raise ValueError("OCR readable path does not preserve its origin") | |
| raw = read_resource(root, export.get("text")) | |
| body = raw.decode("utf-8", errors="strict") | |
| if body.startswith("\ufeff"): | |
| raise ValueError("plain OCR export must not hide a BOM in its indexed text") | |
| identity = {key: export[key] for key in ("recipe", "origin_repo", "origin_rel_path", "origin_revision", "origin_sha256", | |
| "source_sha256", "doc_id", "text_generation", "txt_sha256")} | |
| if export.get("source_sha256_role") != "reader-primary": | |
| raise ValueError("OCR source/derived digest roles must be explicit") | |
| for field in ("origin_sha256", "source_sha256", "text_generation", "txt_sha256", "generation", "book_id"): | |
| sha(export.get(field)) | |
| generation = digest(canonical(identity).rstrip(b"\n")) | |
| if (generation != export["generation"] or digest(raw) != export["txt_sha256"] | |
| or export["book_id"] != digest((repo + "\0" + original).encode())): | |
| raise ValueError("OCR export generation mismatch") | |
| pages, count = export.get("pages"), export.get("page_count") | |
| if type(count) is not int or count < 1 or not isinstance(pages, list) or len(pages) != count: | |
| raise ValueError("OCR export is missing pages") | |
| if export.get("offset_unit") != "unicode-codepoint" or export.get("page_separator") != "\n\n": | |
| raise ValueError("unsupported page offset recipe") | |
| cursor, reviewed = 0, 0 | |
| for number, page in enumerate(pages, 1): | |
| start, end = page.get("start"), page.get("end") | |
| if (page.get("page") != number or type(start) is not int or type(end) is not int | |
| or start != cursor or not start <= end <= len(body)): | |
| raise ValueError("OCR page order/offset mismatch") | |
| sha(page.get("text_generation")) | |
| if digest(body[start:end].encode()) != sha(page.get("text_sha256")): | |
| raise ValueError("OCR page text digest mismatch") | |
| if page.get("quality") not in {"unreviewed", "partially-reviewed", "accepted"}: | |
| raise ValueError("invalid OCR page quality") | |
| reviewed += page["quality"] != "unreviewed" | |
| cursor = end | |
| if number < count: | |
| if body[cursor:cursor + 2] != "\n\n": | |
| raise ValueError("OCR page separator mismatch") | |
| cursor += 2 | |
| if cursor != len(body) or export.get("accepted_pages") != reviewed or export.get("quality") != ("partially-reviewed" if reviewed else "unreviewed"): | |
| raise ValueError("OCR whole-book coverage/quality mismatch") | |
| resources = export.get("retained_reader_resources") | |
| if not isinstance(resources, list) or not resources: | |
| raise ValueError("OCR provenance retention references are missing") | |
| for resource in resources: | |
| if resource.get("bucket") not in {"vomebook/reader-assets-v2", "vomebook/pdf-pages-v2", "melsm/pdf-archive-v2"}: | |
| raise ValueError("unknown retained Reader bucket") | |
| safe_path(resource.get("path")); sha(resource.get("sha256")) | |
| if type(resource.get("bytes")) is not int or resource["bytes"] < 1: | |
| raise ValueError("invalid retained Reader resource") | |
| path = PurePosixPath(readable) | |
| record = {"doc_id": export["doc_id"], "source": SOURCE, "source_name": SOURCE_NAME, | |
| "display_name": path.stem, "display_dirs": list(path.parts[:-1]), "display_rel_path": readable, | |
| "extension": "txt", "size": len(raw), "has_preview": True, "has_fulltext": True, | |
| "storage_root": ".", "storage_rel_path": export["text"]["path"], | |
| "text_generation": export["text_generation"], "text_quality": export["quality"]} | |
| return record, export | |
| def directory_payloads(records: list[dict]) -> tuple[list[dict], dict]: | |
| browser = defaultdict(lambda: {"folders": [], "files": []}) | |
| counts, direct = defaultdict(int), defaultdict(list) | |
| children = defaultdict(set) | |
| for record in records: | |
| parts = record["display_dirs"] | |
| for depth in range(len(parts) + 1): | |
| path = "/".join(parts[:depth]); counts[path] += 1 | |
| if depth < len(parts): children[path].add(parts[depth]) | |
| direct["/".join(parts)].append(record) | |
| for path in counts: | |
| browser[path]["folders"] = [{"name": name, "path": (path + "/" if path else "") + name, | |
| "count": counts[(path + "/" if path else "") + name]} for name in sorted(children[path])] | |
| browser[path]["files"] = [{"name": item["display_name"], "ext": "txt", "size": item["size"], | |
| "doc_id": item["doc_id"]} for item in sorted(direct[path], key=lambda item: (item["display_name"], item["doc_id"]))] | |
| def node(name, path): | |
| return {"name": name, "path": path, "count": counts[path], "hasDirectFiles": bool(direct[path]), | |
| "hasChildren": bool(children[path]), "showSelfToggle": bool(path and direct[path] and children[path]), | |
| "children": [node(child, (path + "/" if path else "") + child) for child in sorted(children[path])]} | |
| # The source slug identifies this collection. Do not add a synthetic | |
| # `OCR成果/` directory above the preserved origin hierarchy; VoiceOfML, | |
| # or any other source owner, is the actual top-level folder. | |
| return [node(child, child) for child in sorted(children[""])], dict(browser) | |
| def zip_member(record: dict) -> str: | |
| """Keep legal origin hierarchy; bound non-portable components deterministically.""" | |
| replacements = {":": ":", "*": "*", "?": "?", '"': """, "<": "<", ">": ">", "|": "|", "\\": "\"} | |
| def component(original): | |
| value = re.sub(r'[:*?"<>|\\\x00-\x1f]', lambda match: replacements.get(match[0], "_"), original).rstrip(" .") or "_" | |
| if value != original or len(value.encode()) > 180: | |
| suffix = "-" + digest(original.encode())[:12] | |
| raw = value.encode()[:180 - len(suffix)] | |
| value = raw.decode("utf-8", errors="ignore").rstrip(" .") + suffix | |
| return value | |
| parts = safe_path(record["display_rel_path"]).split("/") | |
| parts[-1] = component(PurePosixPath(parts[-1]).stem) + ".txt" | |
| return "/".join([component(part) for part in parts]) | |
| def validate_database(path: Path, records: list[dict], text_root: Path) -> None: | |
| with sqlite3.connect(f"file:{path.resolve()}?mode=ro&immutable=1", uri=True) as db: | |
| if db.execute("PRAGMA integrity_check").fetchone()[0] != "ok": | |
| raise ValueError("invalid OCR SQLite integrity") | |
| version = db.execute("SELECT value FROM metadata WHERE key='tokenizer_version'").fetchone() | |
| if version != (TOKENIZER,): | |
| raise ValueError("OCR tokenizer mismatch") | |
| ids = {row[0] for row in db.execute("SELECT doc_id FROM documents")} | |
| if ids != {record["doc_id"] for record in records}: | |
| raise ValueError("OCR metadata/database ID set mismatch") | |
| if db.execute("SELECT COUNT(*) FROM content_fts").fetchone()[0] != len(records): | |
| raise ValueError("OCR content rows mismatch") | |
| for record in records: | |
| body = (text_root / record["storage_rel_path"]).read_text(encoding="utf-8", errors="strict") | |
| normalized = unicodedata.normalize("NFKC", body).casefold().replace("\0", " ") | |
| row = db.execute("SELECT content FROM content_fts WHERE rowid=?", (int(record["doc_id"].split(":")[1]),)).fetchone() | |
| if row != (normalized,): | |
| raise ValueError("OCR indexed content differs from readable TXT") | |
| columns = {row[1] for row in db.execute("PRAGMA table_info(snippet_anchors)")} | |
| if not {"token_hash", "doc_number", "char_offset"} <= columns: | |
| raise ValueError("OCR snippet anchor schema missing") | |
| def load_release(bucket: Path, manifest_path: str, expected_sha256: str, base_metadata_sha256: str) -> dict: | |
| safe_path(manifest_path); sha(expected_sha256); sha(base_metadata_sha256) | |
| bucket = bucket.resolve() | |
| target = (bucket / manifest_path).resolve() | |
| if bucket not in target.parents or not manifest_path.startswith("releases/ocr-"): | |
| raise ValueError("OCR release path outside immutable release namespace") | |
| raw = target.read_bytes() | |
| if digest(raw) != expected_sha256: | |
| raise ValueError("OCR release manifest checksum mismatch") | |
| manifest = json.loads(raw) | |
| if (manifest.get("kind") != "ccrd-ocr-search-release" or manifest.get("version") != 1 | |
| or manifest.get("tokenizer_version") != TOKENIZER or manifest.get("base_metadata_sha256") != base_metadata_sha256): | |
| raise ValueError("incompatible OCR release/base snapshot") | |
| sha(manifest.get("generation")); sha(manifest.get("builder_sha256")) | |
| identity = {key: manifest[key] for key in ("version", "kind", "base_metadata_sha256", "builder_sha256", "tokenizer_version", "exports")} | |
| if (manifest["generation"] != digest(canonical(identity)) | |
| or manifest_path != f'releases/ocr-{manifest["generation"]}/manifest.json'): | |
| raise ValueError("OCR release identity/path mismatch") | |
| refs = manifest.get("exports") | |
| if not isinstance(refs, list) or not refs: | |
| raise ValueError("empty OCR release") | |
| records, exports = [], [] | |
| ids, paths = set(), set() | |
| for ref in refs: | |
| record, export = export_record(bucket, ref) | |
| if record["doc_id"] in ids or record["display_rel_path"] in paths: | |
| raise ValueError("OCR ID/readable-path collision; no silent overwrite") | |
| ids.add(record["doc_id"]); paths.add(record["display_rel_path"]) | |
| records.append(record); exports.append(export) | |
| if manifest.get("documents") != len(records): | |
| raise ValueError("OCR release count mismatch") | |
| database = manifest.get("database") | |
| read_resource(bucket, database) | |
| db_path = (bucket / database["path"]).resolve() | |
| if database["path"] != f'indexes/ocr/{manifest["generation"]}/OCR.sqlite3': | |
| raise ValueError("OCR database must retain its source namespace") | |
| validate_database(db_path, records, bucket) | |
| tree, browser = directory_payloads(records) | |
| return {"manifest": manifest, "records": records, "exports": exports, "database": db_path, | |
| "text_paths": {record["doc_id"]: bucket / record["storage_rel_path"] for record in records}, | |
| "tree": tree, "browser": browser} | |
| def pin_release(bucket: Path, manifest_path: str, expected_sha256: str, | |
| base_metadata_sha256: str, cache: Path) -> dict: | |
| """Verify the whole candidate before pinning a process-local immutable copy.""" | |
| candidate = load_release(bucket, manifest_path, expected_sha256, base_metadata_sha256) | |
| cache.mkdir(parents=True, exist_ok=True) | |
| destination = cache / candidate["manifest"]["generation"] | |
| if destination.exists(): | |
| return load_release(destination, manifest_path, expected_sha256, base_metadata_sha256) | |
| stage = Path(tempfile.mkdtemp(prefix="ocr-stage-", dir=cache)) | |
| try: | |
| refs = [*candidate["manifest"]["exports"], candidate["manifest"]["database"], | |
| *[export["text"] for export in candidate["exports"]]] | |
| for ref in refs: | |
| raw = read_resource(bucket, ref) | |
| target = stage / ref["path"] | |
| target.parent.mkdir(parents=True, exist_ok=True) | |
| with target.open("wb") as output: | |
| output.write(raw); output.flush(); os.fsync(output.fileno()) | |
| target = stage / manifest_path | |
| target.parent.mkdir(parents=True, exist_ok=True) | |
| manifest_raw = (bucket / manifest_path).read_bytes() | |
| if digest(manifest_raw) != expected_sha256: | |
| raise ValueError("OCR candidate changed while being copied") | |
| target.write_bytes(manifest_raw) | |
| load_release(stage, manifest_path, expected_sha256, base_metadata_sha256) | |
| stage.rename(destination) | |
| finally: | |
| if stage.exists(): | |
| shutil.rmtree(stage) | |
| return load_release(destination, manifest_path, expected_sha256, base_metadata_sha256) | |