Library / SDK
segment-any-text/wtpsplit avatar
segment-any-text/wtpsplit

wtpsplit: Sentence Segmentation With SaT Models, LoRA Adaptation and ONNX Inference

Toolkit to segment text into sentences or other semantic units in a robust, efficient and adaptable way.

1,339 stars84 forksPythonMIT

At a glance

What is it?
wtpsplit packages the SaT and WtP sentence segmentation models behind a single Python class. The -sm variants and LoRA adaptation are the parts worth understanding before you pick a checkpoint.
Who is it for?
Adopt wtpsplit if you need sentence boundaries in messy or multilingual text and can spend GPU memory on a transformer checkpoint; the sat-3l-sm model is the reasonable default and the LoRA modules cover language and domain adaptation without fine-tuning. Do not adopt it if you only split well-formed English prose, because nltk.sent_tokenize already reaches 92.2 English F1 in the project's own table and needs no model download.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 38 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

The Segmentation Problem wtpsplit Targets

Sentence segmentation looks trivial until the text stops being well-formed. Missing punctuation, lowercase-only input, mixed languages inside one document, and domain-specific abbreviations all break rule-based splitters. wtpsplit exists to handle those cases with a trained model rather than a pattern list. The README frames the toolkit as segmenting text "into sentences or other semantic units", and the two papers behind it (SaT and the earlier WtP) are both about robustness when punctuation is unreliable. The intended user is someone building a pipeline where sentence boundaries feed something downstream: translation, embedding, chunking for retrieval, or corpus preparation. The repository topics list sentence-boundary-detection and pretrained-models, which matches that framing. If your input is clean English prose with normal punctuation, you are not the target user, and the model comparison table in the README says so indirectly: Punkt and the spaCy sentencizer already score in the low nineties on English.

How SaT Splits a String: One Class, a Model Name, a List of Strings

The public surface is deliberately small. You construct a model by name, optionally move it to a device, and call split. The README example is `sat = SaT("sat-3l")`, followed by `sat.half().to("cuda")` for GPU execution, then `sat.split("This is a test This is another test.")`, which returns `["This is a test ", "This is another test."]`. Note that the trailing space stays attached to the first segment; the splitter returns spans of the original string rather than trimmed sentences, so downstream code should not assume stripped output. The same object accepts a list of texts: `sat.split([...])` returns an iterator yielding a list of sentences per input. The README explicitly recommends the batched form, writing "do this instead of calling sat.split on every text individually for much better performance". That is the main architectural fact worth internalising: the model is a sequence labeller over subword tokens, and batching is how you amortise it. TPU execution is mentioned via `sat.to("xla:0")`, with the caveat that you must pass `pad_last_batch=True` to split in that configuration. The older WtP models are still reachable through a parallel class, `WtP("wtp-bert-mini")`, with what the README describes as similar functionality.

Choosing Between -sm, Layer Count and the LoRA Modules

The naming scheme encodes two independent decisions. Layer count (1l through 12l) controls capacity and cost. The -sm suffix marks models the README recommends "for general sentence segmentation tasks", and the numbers back that up: sat-3l-sm scores 96.5 English and 93.5 multilingual, while the larger sat-3l scores 93.7 and 89.2. A three-layer -sm model beating a three-layer base model by roughly three English points is the single most useful thing in the model table, and it means the intuitive "bigger is better" reading of the names is wrong here. The 12-layer -sm model tops the table at 97.4 English and 96.0 multilingual, and the README calls the 12-layer models "the best". For latency-sensitive work it points at the 3-layer models as a speed and performance tradeoff. The second decision is adaptation. Passing `style_or_domain="ud"` and `language="en"` to the constructor loads a trained LoRA module, and the README's example shows the same input producing `['This is a test ', 'This is another test']` with the adapted model. Adaptation is not cosmetic: the table lists sat-3l-lora at 96.7 English and 94.8 multilingual against 93.7 and 89.2 for the unadapted sat-3l, and sat-12l-lora at 97.3 and 95.9 against 94.0 and 90.4. The scores are macro-average F1 across the datasets the project evaluated, so they are comparable to each other and not directly to your corpus.

Installation, ONNX Export and the LoRA Merge Step

Installation is one command: `pip install wtpsplit`. For ONNX inference there are two extras, `pip install wtpsplit[onnx-gpu]` and `pip install wtpsplit[onnx-cpu]`. Enabling ONNX is a constructor argument rather than a separate API: `SaT("sat-3l-sm", ort_providers=["CUDAExecutionProvider", "CPUExecutionProvider"])`. The README reports a timing comparison on an RTX 3090 where the PyTorch GPU path takes 144 ms per loop and the onnxruntime GPU path takes 94.9 ms per loop over 1000 short texts, and states the ONNX path "should be ~50% faster". Those are the project's own numbers on one GPU with one input shape; treat them as an indication of direction, not a guarantee for your batch sizes. The LoRA plus ONNX combination is the fiddly part and is worth reading twice. You cannot simply pass ort_providers alongside style_or_domain. The documented route is to run `scripts/export_to_onnx_sat.py` with `use_lora: True` and an `output_dir`, supplying either a local `lora_path` or a `style_or_domain` and `language` pair to pull from the HuggingFace hub. The exported directory is then loaded as `SaT(<OUTPUT_DIR>, onnx_providers=[...])`. That is a build step in your deployment, not a runtime flag, and it means an adapted ONNX model has to be re-exported whenever the LoRA module or the base checkpoint changes.

Where wtpsplit Is the Wrong Tool

The clearest limitation is the one the README's own comparison table creates. Against PySBD at 69.6 English F1 the gap is enormous, but against the spaCy sentencizer at 92.9, Punkt at 92.2 and Ersatz at 91.4, the margin for a base 3-layer model at 93.7 is about one point. You are trading a dependency-free tokenizer for a transformer checkpoint, a model download, and either CPU inference cost or GPU memory. For clean English text that trade is hard to justify on accuracy alone. The second limitation is scope of adaptation. LoRA is presented as the mechanism for "strong adaptation to language & domain/style", and the constructor takes a style_or_domain and a language. The material shows exactly one pair, `style_or_domain="ud"` with `language="en"`, plus links to lora directories on two model pages. Nothing here enumerates the full set of available pairs, so if your language or domain is not covered you are back to the unadapted model or to fine-tuning, which the README does not walk through. Third, the returned spans keep their original whitespace, as the `"This is a test "` output shows. Code that assumes trimmed sentences will need an explicit strip. Fourth, TPU use carries an extra constraint, `pad_last_batch=True`, which suggests the batching path is not shape-agnostic on that backend.

Alternatives and the Actual Difference in Approach

The honest alternative for English is NLTK's Punkt, invoked as `nltk.sent_tokenize`. Punkt is an unsupervised, language-specific statistical model trained on unlabelled text; it learns abbreviation and collocation statistics per language and needs a separate model per language. wtpsplit's SaT models are supervised neural sequence labellers that share parameters across languages, which is why the project can report one multilingual score across 85 languages rather than maintaining per-language resources. That difference shows up in the failure modes. Punkt degrades when punctuation is absent or non-standard, because its evidence is punctuation distribution; a neural labeller trained on punctuation-agnostic data is designed for exactly that input. The README's own example, `sat_sm.split("this is a test this is another test")` returning two segments from an unpunctuated lowercase string, is a case Punkt would struggle with. The cost is the opposite direction: Punkt runs on CPU with no model weights, while wtpsplit pulls a checkpoint and, per the README, benefits from `half().to("cuda")`. spaCy's sentencizer is the other realistic option, and the table puts its monolingual variant at 92.9 English, close enough to sat-3l that the decision should rest on whether you need multilingual coverage and punctuation-agnostic behaviour rather than on the English number.

Maintenance, Versioning and the MIT Licence

The project is active rather than archived. The last push recorded is 2026-08-08, and the release history shows 2.2.1 in April 2026, 2.2.0 in February 2026 and 2.1.7 in November 2025, so roughly two to three releases a year over that window. The README keeps WtP explicitly "maintained for reproducibility" alongside the newer SaT models, which is a real cost for anyone still on WtP: you inherit the older architecture and its lower score, 93.9 English for the 3-layer variant, without the newer models' gains. The upgrade path from WtP to SaT is mostly a class swap, `WtP("wtp-bert-mini")` to `SaT("sat-3l")`, since the README describes the functionality as similar. The heavier upgrade cost sits in the ONNX plus LoRA path, where a version bump can mean re-running `scripts/export_to_onnx_sat.py` and re-validating the exported directory. The licence is MIT, which is permissive and imposes no copyleft obligation on your code. That covers the wtpsplit library. The model weights are hosted separately on HuggingFace under the segment-any-text organisation, and the supplied material does not state their licence terms, so verify those independently before shipping a model in a commercial product. This is a description of the repository metadata, not legal advice.

Editorial conclusion

Adopt wtpsplit if you need sentence boundaries in messy or multilingual text and can spend GPU memory on a transformer checkpoint; the sat-3l-sm model is the reasonable default and the LoRA modules cover language and domain adaptation without fine-tuning. Do not adopt it if you only split well-formed English prose, because nltk.sent_tokenize already reaches 92.2 English F1 in the project's own table and needs no model download. Before committing, verify two things in your own environment: which checkpoint fits your latency budget, and whether your target language has a LoRA module, since LoRA is the documented mechanism for adaptation and not every style_or_domain or language pair is listed.

Official sources

  1. Issues
  2. License: MIT
  3. README
  4. Releases
  5. segment-any-text/wtpsplit on GitHub
Community notes

Community notes