Graf

Paragraphs will be stacked until morale improves.

A small encoder that recovers paragraph breaks in flattened news text.

Task Paragraph break recovery (token classification)
Size ~19 MB weights (trimmed vocab, int8 embeddings, fp16 rest)
Runtime CPU/GPU (PyTorch); browser (ONNX Runtime WASM)
Weights main · web/ for WASM
Demo localangle/graf-demo (in-browser)

Graf does not rewrite the article. It scores eligible token ends as BREAK / NO_BREAK, then splits the flattened string at predicted breaks.

Flattened: City council approved a library renovation… Construction starts in March… → Graf inserts breaks between the recovered paragraphs.

Try it

  • Live demo: localangle/graf-demo — paste flattened news, paragraphize in the browser.
  • Local UI: from the graf repo, graf-web --web-package checkpoints/graf-web.

Install

pip install "graf @ git+https://github.com/cjdd3b/graf.git"
# or, from a checkout:
pip install -e .

Python ≥ 3.11. Needs torch, transformers, safetensors, huggingface_hub.

Usage

from graf.infer import GrafParagraphizer

graf = GrafParagraphizer("localangle/graf")  # thr from graf.json (0.41)
result = graf.paragraphize(
    "City council approved a library renovation after public comment. "
    "Paulsen and Chen voted against the measure. Construction starts in March."
)
print("\n\n".join(result.paragraphs))

Load a local package folder the same way:

graf = GrafParagraphizer("checkpoints/graf")

Files

File Contents
graf.json Format, threshold, kept vocabulary, source metadata
weights.safetensors Trimmed encoder + head (int8 token embeddings, fp16 elsewhere)
encoder/ Ettin/ModernBERT config for the trimmed vocabulary
tokenizer/ Full tokenizer (ids remapped at inference via vocab_map)
web/ Browser package: model.onnx (~17 MB int8), vocab_map.bin, graf-web.json, tokenizer/

PyTorch weights expand to fp32 at load; only storage is compressed. The web/ ONNX graph is what the static demo runs in WASM (soft cap 2048 tokens).

Accuracy

Held-out Extra structure v0.2 test set (publisher-disjoint), package threshold 0.41 (student before compression; packaging keeps break agreement ≈ Jaccard 0.98):

Metric Value
Boundary F1 0.871
Precision / Recall 0.845 / 0.900
PR-AUC 0.948
Body–body precision / recall 0.829 / 0.894
Median |Δ block count| 2.0

How it works

Flattened article text is packed into overlapping 2048-token windows. Eligible token ends (positions that can host a paragraph boundary) are scored for BREAK. Probabilities are averaged across windows, thresholded, and used to reconstruct paragraph blocks.

The published package is a compressed Ettin student (~17M) distilled for on-device / in-browser use.

Limitations

  • English news-style prose; other domains are unmeasured.
  • Training text / Extra labels are not redistributed here.
  • Exact full-article reconstruction is intentionally hard; treat boundary F1 / body–body metrics as the primary quality signal.
  • Browser int8 ONNX drifts slightly vs the PyTorch package; the web meta threshold is nudged for recall (see web/graf-web.json).

Built on

  • Ettin encoder 17M (MIT) by JHU CLSP. Fine-tuned and compressed for Graf; see THIRD_PARTY_NOTICES.md.

License

MIT for the Graf code and this package's weights, subject to the Ettin MIT notice above.

Citation

@software{graf_2026,
  title  = {Graf: Paragraph break recovery for flattened news},
  author = {Local Angle},
  year   = {2026},
  url    = {https://huggingface.co/localangle/graf},
}
Downloads last month

-

Downloads are not tracked for this model. How to track
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support

Model tree for localangle/graf

Quantized
(15)
this model

Space using localangle/graf 1