'
f'Page {page.number} / {len(document.pages)}'
f'{e(LABELS[page.method])}'
)
def source_html(document: documents.Document, sample: str | None) -> str:
scans = sum(p.method == "ocr" for p in document.pages)
review = sum(p.method in ("partial", "unreadable") for p in document.pages)
caption = "Original fictional sample. Not an accuracy benchmark." if sample else "Your uploaded PDF."
facts = [_count(len(document.pages), "page")]
if scans:
facts.append(f"{scans} OCR")
elif not review:
facts.append("selectable text")
if review:
facts.append(f"{_count(review, 'page')} need text review")
return (
f'
')
model_ms = result["metrics"].get("model_ms")
call_ms = result["metrics"].get("latency_ms")
timing = (f'{model_ms:,.0f} ms model only' if model_ms is not None else
f'{call_ms:,.0f} ms decision call' if call_ms is not None else "")
return (
f'
'
f'
'
f'{SOURCES[kind]} / {e(model)}
'
f'
{e(title)}
{note}
'
+ body
+ '
'
+ (f'{result["document"]["preparation_ms"]:,.0f} ms PDF preparation'
if kind != "saved" else "")
+ timing
+ '0 generated tokens
'
'What the scores and timings mean'
f'
{e(result["probability_note"])}
'
'
Review flags use fixed demo thresholds, not a fitted confidence guarantee. '
'Nearly blank pages stay with the preceding document; leading blank pages stay with the first. '
'Unreadable pages are attached provisionally and always need review.
'
'
Model-only time excludes PDF preparation and the wait for a device. '
'Preparation includes text extraction, scan OCR when needed, and preview rendering. '
'Saved model timings describe the recorded Space call, not this page load. '
'The saved JSON also retains separately measured preparation from the recording client; '
'it is not a hosted parse measurement and is omitted from this timing strip.
'
+ (f'
Decision call: {call_ms:,.0f} ms. This includes the device wait inside the call.
'
if call_ms is not None else "")
+ '
'
)
def _cache_exports(document: documents.Document, result: dict, component: gr.File) -> list[str]:
paths = []
for name, data in documents.export_bytes(document, result).items():
path = gr.processing_utils.save_bytes_to_cache(data, name, component.GRADIO_CACHE)
component.temp_files.add(path)
paths.append(path)
return paths
def document_api(file: str, mode: str, category_rules: str, model: str) -> dict:
"""File-component preprocessing permits only uploaded files, not arbitrary local paths."""
try:
document = documents.parse_document(file)
return documents.process(document, category_rules, mode, model)
except (documents.DocumentError, blink.BlinkError) as exc:
raise gr.Error(str(exc)) from None
def build() -> dict:
sample = sample_document("packet")
model = documents.default_model()
saved = documents.saved_result(sample, documents.DEFAULT_RULES, "split", model)
held = gr.State(sample)
result = gr.State(saved)
source_seen = gr.State({"source": 0, "selection": ("packet", None)})
rev = gr.Textbox("0", visible=False)
gr.HTML(intro())
with gr.Row(equal_height=False, elem_classes="doc-workspace"):
with gr.Column(scale=5, min_width=280, elem_classes="doc-col doc-col-source"):
gr.HTML(step(1, "Choose a PDF"), elem_classes="doc-stephead")
preset = gr.Radio(choices=[(s["title"], s["id"]) for s in documents.sample_info()],
value="packet", show_label=False, elem_id="doc-samples")
with gr.Accordion("Use your own PDF", open=False, elem_id="doc-own"):
upload = gr.File(label="PDF \u00b7 up to 12 MiB and 12 pages", file_types=[".pdf"], type="filepath",
elem_id="doc-upload")
gr.HTML('
Processing runs in this Space. Temporary uploads and downloads '
'are cleared within an hour. Do not upload confidential documents.
Classify gives one label. Split packet finds separate documents, '
'including adjacent documents with the same label.
')
with gr.Accordion("Categories and rules", open=False, elem_id="doc-rules-fold"):
rule = gr.Code(documents.rules_text(), language="json", label="Your categories and boundary rules",
lines=18, wrap_lines=True, elem_id="doc-rules")
with gr.Accordion("Run options", open=False, elem_id="doc-options"):
models = gr.State(model)
compare = gr.Checkbox(False, label="Compare with Blink 4B on the same page text",
visible=len(blink.models()) == 2, elem_id="doc-compare")
gr.HTML('
The PDF is prepared once. Each model gets identical text and rules. '
'This is a demonstration, not a matched accuracy study.
')
run = gr.Button("Run Blink", variant="primary", elem_id="doc-run")
gr.HTML('
This button runs the live model. The initial result, '
'when present, is a saved run on the fictional sample.
')
gr.HTML(step(3, "Inspect the result"), elem_classes="doc-stephead")
with gr.Column(elem_id="doc-outputs", elem_classes="doc-panel"):
answer = gr.HTML(result_html(saved), elem_id="doc-result")
inspect = gr.Dropdown(
choices=[(f'{s["label"]} / {_range(s["pages"])}', s["pages"][0]) for s in saved["segments"]]
if saved else [], value=None, label="Jump to a document", visible=saved is not None,
info="Shows the document's first page in the preview.", elem_id="doc-inspect",
)
comparison = gr.HTML(visible=False, elem_id="doc-comparison")
files = gr.File(label="Download result JSON and split PDFs", file_count="multiple",
interactive=False, visible=saved is not None, elem_id="doc-downloads")
if saved is not None:
initial = files.postprocess(_cache_exports(sample, saved, files))
files.value = gr.processing_utils.move_files_to_cache(
initial.model_dump(), files, postprocess=True, keep_in_cache=True,
)
with gr.Accordion("Full result JSON", open=False, elem_id="doc-json-fold"):
raw = gr.JSON(saved, label="Page decisions, review flags, and provenance", elem_id="doc-json")
gr.HTML('
Inputs changed. Run Blink again to see a matching result.
', gr.update(value=None, visible=False),
gr.update(choices=[], value=None, visible=False), None,
gr.update(value="", visible=False), revision)
warning = ""
try:
recorded = documents.saved_result(document, current_rules, current_mode, current_model)
except documents.DocumentError as exc:
recorded = None
warning = f'
{e(str(exc))} The PDF remains loaded.
'
return (document, gr.update(choices=page_choices(document), value=1), preview_html(document),
document.pages[0].text, source_html(document, None if uploaded else key),
recorded, warning or result_html(recorded, revision),
gr.update(value=_cache_exports(document, recorded, files) if recorded else None, visible=bool(recorded)),
gr.update(choices=[(f'{s["label"]} / {_range(s["pages"])}', s["pages"][0])
for s in recorded["segments"]] if recorded else [],
value=None, visible=bool(recorded)),
recorded, gr.update(value="", visible=False), revision)
load_out = [held, page, preview, text, source, result, answer, files, inspect, raw, comparison, rev]
def preparing(revision, key, uploaded, seen):
if not ui.newest(seen, "source", revision):
return [gr.skip() for _ in preparing_out]
seen["selection"] = (key, str(uploaded) if uploaded else None)
return [None, '
Preparing the selected PDF
',
"", *clear(revision), gr.update(interactive=False)]
preparing_out = [held, preview, source, *clear_out, run]
def load_ready(revision, key, uploaded, current_mode, current_rules, current_model, seen):
selection = (key, str(uploaded) if uploaded else None)
if ui.as_rev(revision) < ui.applied(seen, "source") or seen.get("selection") != selection:
return [gr.skip() for _ in [*load_out, run]]
source_revision = ui.applied(seen, "source")
loaded = load_source(revision, key, uploaded, current_mode, current_rules, current_model)
if source_revision != ui.applied(seen, "source") or seen.get("selection") != selection:
return [gr.skip() for _ in [*load_out, run]]
return [*loaded, gr.update(interactive=loaded[0] is not None)]
for event in (upload.upload, upload.clear):
event(preparing, [rev, preset, upload, source_seen], preparing_out, js=REVISION, queue=False,
show_progress="hidden", api_visibility="private").then(
load_ready, [rev, preset, upload, mode, rule, models, source_seen], [*load_out, run],
js=READ_REVISION, show_progress="minimal", api_visibility="private",
)
def pick_sample(revision, key, seen):
if ui.as_rev(revision) < ui.applied(seen, "source"):
return [gr.skip() for _ in [upload, mode, *preparing_out]]
return [None, next(s["mode"] for s in documents.sample_info() if s["id"] == key),
*preparing(revision, key, None, seen)]
preset.input(pick_sample, [rev, preset, source_seen], [upload, mode, *preparing_out],
js=REVISION, queue=False, api_visibility="private").then(
load_ready, [rev, preset, upload, mode, rule, models, source_seen], [*load_out, run],
js=READ_REVISION, show_progress="minimal", api_visibility="private",
)
def show_page(document, number):
if document is None or number is None:
return preview_html(None), ""
return preview_html(document, int(number)), document.pages[int(number) - 1].text
page.change(show_page, [held, page], [preview, text], queue=False,
show_progress="hidden", api_visibility="private")
inspect.input(lambda number: gr.update(value=number), [inspect], [page], queue=False,
show_progress="hidden", api_visibility="private")
def run_document(revision, document, category_rules, task, selected, both, uploaded, selected_sample):
if document is None:
return (None, '
Choose a readable PDF before running Blink.
',
gr.update(value=None, visible=False), gr.update(value=None, visible=False),
None, gr.update(value="", visible=False))
try:
source_path = uploaded or documents.sample_path(selected_sample)
if not documents.matches_source(document, source_path):
raise documents.DocumentError("The new PDF is still being prepared. Wait for its page preview before running Blink.")
outcome = documents.process(document, category_rules, task, selected)
other = None
if both and len(blink.models()) == 2:
other_model = next(m for m in blink.models() if m != selected)
other = documents.process(document, category_rules, task, other_model)
paths = _cache_exports(document, outcome, files)
except (documents.DocumentError, blink.BlinkError) as exc:
return (None, f'