"""Public demo (read-only) entry for HF Spaces — Reconciliation Workbench. This is the PUBLIC demo build. It ships ONLY the frontend, two pre-computed sample payloads and the demo video. The deterministic reconciliation engine and the LLM extraction pipeline are deliberately NOT included; live PDF upload and on-the-fly recompute are disabled (see the demo video). The two bundled samples render fully from cached results, so the PDF ↔ live-sheet ↔ finding linkage all works. """ from __future__ import annotations import json from pathlib import Path from urllib.parse import quote import gradio as gr from wbdemo import enrich _HERE = Path(__file__).resolve().parent _WB = _HERE / "wbdemo" _ASSETS = _WB / "assets" _PDFJS = "https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174" # pdf.js (UMD) 注入 head:挂 window.pdfjsLib + window.__pdfReady 就绪 Promise(与全功能版一致)。 _HEAD = f""" """ _SAMPLES = { "sanmu": { "title": "Sanmu Group 2024 Annual Report (Consolidated)", "payload": "sanmu.json", "pdf": "sanmu.pdf", }, "prospectus": { "title": "A Prospectus (Consolidated)", "payload": "prospectus.json", "pdf": None, }, } _DEFAULT = "sanmu" def _read(name: str) -> str: return (_ASSETS / name).read_text(encoding="utf-8") def _file_url(abspath: str | None) -> str | None: if not abspath: return None return "/gradio_api/file=" + quote(abspath, safe="/:") def _load_payload(fn: str) -> dict: p = json.loads((_WB / "payloads" / fn).read_text(encoding="utf-8")) return enrich.attach_review_cells(p) # 纯函数:挂 manual_review→cell_ids,供三联动 def _focus_table_id(payload: dict) -> str | None: for t in payload.get("tables", []): if t.get("kind") == "BS": return t.get("table_id") return None def _initial_value() -> dict: samples: dict = {} for k, s in _SAMPLES.items(): payload = _load_payload(s["payload"]) pdf_abs = str(_WB / s["pdf"]) if s["pdf"] else None samples[k] = { "sample_key": k, "title": s["title"], "payload": payload, "pdf_url": _file_url(pdf_abs), "focus_table_id": _focus_table_id(payload), } return {"samples": samples, "default": _DEFAULT} # 公开演示为只读:上传/人工修正重算/导出均需确定性引擎(未随本演示发布),统一桩成友好提示。 _DISABLED = { "ok": False, "error": ("This public demo is read-only. Live PDF upload, human correction + recompute, " "and Excel export run the deterministic engine, which isn't shipped in this public " "demo — watch the demo video for them. Full interactive version available on request."), } def submit_override(req: dict) -> dict: return dict(_DISABLED) def process_pdf(req: dict) -> dict: return dict(_DISABLED) def export_xlsx(req: dict) -> dict: return dict(_DISABLED) def build_demo() -> gr.Blocks: pdf_files = [str(_WB / s["pdf"]) for s in _SAMPLES.values() if s["pdf"]] if pdf_files: gr.set_static_paths(paths=pdf_files) init = _initial_value() with gr.Blocks(title="Reconciliation Workbench") as demo: gr.HTML( value=init, html_template=_read("island.html"), css_template=_read("island.css"), js_on_load=_read("island.js"), head=_HEAD, apply_default_css=False, min_height=640, server_functions=[submit_override, process_pdf, export_xlsx], ) return demo demo = build_demo() if __name__ == "__main__": # HF Spaces(Gradio SDK)期望端口 7860——用裸 launch() 让 gradio 自动适配 Spaces 环境 # (与全功能版一致,实证可达 RUNNING)。本地调试用 GRADIO_SERVER_PORT 环境变量覆盖。 demo.launch()