cleave-sbd 0.2.0b1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cleave_sbd-0.2.0b1/.github/ISSUE_TEMPLATE/bug_report.md +54 -0
- cleave_sbd-0.2.0b1/.github/workflows/python-package.yml +43 -0
- cleave_sbd-0.2.0b1/.gitignore +78 -0
- cleave_sbd-0.2.0b1/CHANGELOG.md +44 -0
- cleave_sbd-0.2.0b1/CONTRIBUTING.md +64 -0
- cleave_sbd-0.2.0b1/LICENSE +21 -0
- cleave_sbd-0.2.0b1/PKG-INFO +204 -0
- cleave_sbd-0.2.0b1/README.md +169 -0
- cleave_sbd-0.2.0b1/benchmarking/benchmark_output.txt +18 -0
- cleave_sbd-0.2.0b1/benchmarking/bigtext_speed_benchmark.py +575 -0
- cleave_sbd-0.2.0b1/benchmarking/english_accuracy_benchmark.py +159 -0
- cleave_sbd-0.2.0b1/profiling/profile_benchmarks.py +109 -0
- cleave_sbd-0.2.0b1/profiling/profile_normalizer.py +102 -0
- cleave_sbd-0.2.0b1/pyproject.toml +140 -0
- cleave_sbd-0.2.0b1/src/csbd/__init__.py +11 -0
- cleave_sbd-0.2.0b1/src/csbd/disambiguator.py +380 -0
- cleave_sbd-0.2.0b1/src/csbd/language/__init__.py +17 -0
- cleave_sbd-0.2.0b1/src/csbd/language/configs/am.toml +8 -0
- cleave_sbd-0.2.0b1/src/csbd/language/configs/ar.toml +40 -0
- cleave_sbd-0.2.0b1/src/csbd/language/configs/bg.toml +80 -0
- cleave_sbd-0.2.0b1/src/csbd/language/configs/da.toml +521 -0
- cleave_sbd-0.2.0b1/src/csbd/language/configs/de.toml +209 -0
- cleave_sbd-0.2.0b1/src/csbd/language/configs/el.toml +8 -0
- cleave_sbd-0.2.0b1/src/csbd/language/configs/en.toml +260 -0
- cleave_sbd-0.2.0b1/src/csbd/language/configs/es.toml +355 -0
- cleave_sbd-0.2.0b1/src/csbd/language/configs/fa.toml +17 -0
- cleave_sbd-0.2.0b1/src/csbd/language/configs/fr.toml +98 -0
- cleave_sbd-0.2.0b1/src/csbd/language/configs/hi.toml +9 -0
- cleave_sbd-0.2.0b1/src/csbd/language/configs/hy.toml +7 -0
- cleave_sbd-0.2.0b1/src/csbd/language/configs/it.toml +2363 -0
- cleave_sbd-0.2.0b1/src/csbd/language/configs/ja.toml +9 -0
- cleave_sbd-0.2.0b1/src/csbd/language/configs/kk.toml +306 -0
- cleave_sbd-0.2.0b1/src/csbd/language/configs/mr.toml +7 -0
- cleave_sbd-0.2.0b1/src/csbd/language/configs/my.toml +8 -0
- cleave_sbd-0.2.0b1/src/csbd/language/configs/nl.toml +1573 -0
- cleave_sbd-0.2.0b1/src/csbd/language/configs/pl.toml +137 -0
- cleave_sbd-0.2.0b1/src/csbd/language/configs/ru.toml +78 -0
- cleave_sbd-0.2.0b1/src/csbd/language/configs/sk.toml +236 -0
- cleave_sbd-0.2.0b1/src/csbd/language/configs/ur.toml +8 -0
- cleave_sbd-0.2.0b1/src/csbd/language/configs/zh.toml +5 -0
- cleave_sbd-0.2.0b1/src/csbd/language/lang.py +179 -0
- cleave_sbd-0.2.0b1/src/csbd/language/protocols.py +35 -0
- cleave_sbd-0.2.0b1/src/csbd/normalizer.py +246 -0
- cleave_sbd-0.2.0b1/src/csbd/processors/__init__.py +29 -0
- cleave_sbd-0.2.0b1/src/csbd/processors/abbreviation.py +299 -0
- cleave_sbd-0.2.0b1/src/csbd/processors/lists.py +480 -0
- cleave_sbd-0.2.0b1/src/csbd/py.typed +0 -0
- cleave_sbd-0.2.0b1/src/csbd/rules/__init__.py +231 -0
- cleave_sbd-0.2.0b1/src/csbd/rules/abbreviation.py +234 -0
- cleave_sbd-0.2.0b1/src/csbd/rules/boundary.py +187 -0
- cleave_sbd-0.2.0b1/src/csbd/rules/disambiguation.py +71 -0
- cleave_sbd-0.2.0b1/src/csbd/rules/normalizer.py +72 -0
- cleave_sbd-0.2.0b1/src/csbd/rules/pua.py +133 -0
- cleave_sbd-0.2.0b1/src/csbd/segmenter.py +198 -0
- cleave_sbd-0.2.0b1/tests/__init__.py +0 -0
- cleave_sbd-0.2.0b1/tests/data/issues.toml +111 -0
- cleave_sbd-0.2.0b1/tests/data/lang/amharic.toml +4 -0
- cleave_sbd-0.2.0b1/tests/data/lang/arabic.toml +20 -0
- cleave_sbd-0.2.0b1/tests/data/lang/armenian.toml +104 -0
- cleave_sbd-0.2.0b1/tests/data/lang/bulgarian.toml +16 -0
- cleave_sbd-0.2.0b1/tests/data/lang/burmese.toml +4 -0
- cleave_sbd-0.2.0b1/tests/data/lang/chinese.toml +8 -0
- cleave_sbd-0.2.0b1/tests/data/lang/danish.toml +196 -0
- cleave_sbd-0.2.0b1/tests/data/lang/deutsch.toml +140 -0
- cleave_sbd-0.2.0b1/tests/data/lang/dutch.toml +12 -0
- cleave_sbd-0.2.0b1/tests/data/lang/english.toml +193 -0
- cleave_sbd-0.2.0b1/tests/data/lang/english_clean.toml +504 -0
- cleave_sbd-0.2.0b1/tests/data/lang/french.toml +20 -0
- cleave_sbd-0.2.0b1/tests/data/lang/greek.toml +4 -0
- cleave_sbd-0.2.0b1/tests/data/lang/hindi.toml +4 -0
- cleave_sbd-0.2.0b1/tests/data/lang/italian.toml +144 -0
- cleave_sbd-0.2.0b1/tests/data/lang/japanese.toml +20 -0
- cleave_sbd-0.2.0b1/tests/data/lang/kazakh.toml +52 -0
- cleave_sbd-0.2.0b1/tests/data/lang/marathi.toml +20 -0
- cleave_sbd-0.2.0b1/tests/data/lang/persian.toml +4 -0
- cleave_sbd-0.2.0b1/tests/data/lang/polish.toml +4 -0
- cleave_sbd-0.2.0b1/tests/data/lang/russian.toml +168 -0
- cleave_sbd-0.2.0b1/tests/data/lang/slovak.toml +20 -0
- cleave_sbd-0.2.0b1/tests/data/lang/spanish.toml +144 -0
- cleave_sbd-0.2.0b1/tests/data/lang/urdu.toml +4 -0
- cleave_sbd-0.2.0b1/tests/data/list_items.toml +90 -0
- cleave_sbd-0.2.0b1/tests/data/normalizer.toml +8 -0
- cleave_sbd-0.2.0b1/tests/data/pdf.toml +24 -0
- cleave_sbd-0.2.0b1/tests/loaders.py +278 -0
- cleave_sbd-0.2.0b1/tests/test_backtracking.py +48 -0
- cleave_sbd-0.2.0b1/tests/test_languages.py +84 -0
- cleave_sbd-0.2.0b1/tests/test_list_masking.py +45 -0
- cleave_sbd-0.2.0b1/tests/test_normalizer.py +34 -0
- cleave_sbd-0.2.0b1/tests/test_regressions.py +49 -0
- cleave_sbd-0.2.0b1/tests/test_segmenter.py +174 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Bug Report
|
|
3
|
+
about: Create a report and test case to help us improve cleave-sbd
|
|
4
|
+
title: "[BUG] "
|
|
5
|
+
labels: ["bug"]
|
|
6
|
+
assignees: ""
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
### Description
|
|
11
|
+
A clear and concise description of what the bug is.
|
|
12
|
+
|
|
13
|
+
### Reproduction Code
|
|
14
|
+
|
|
15
|
+
```python
|
|
16
|
+
import csbd
|
|
17
|
+
|
|
18
|
+
seg = csbd.Segmenter(language="en", clean=False, char_span=False)
|
|
19
|
+
text = "Your sample text here."
|
|
20
|
+
sentences = seg.segment(text)
|
|
21
|
+
print(sentences)
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### Expected Behavior
|
|
25
|
+
A clear and concise description of what sentences should have been segmented.
|
|
26
|
+
|
|
27
|
+
```python
|
|
28
|
+
["Expected sentence 1.", "Expected sentence 2."]
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Actual Behavior
|
|
32
|
+
The actual output returned by `cleave-sbd`.
|
|
33
|
+
|
|
34
|
+
```python
|
|
35
|
+
["Actual sentence 1."]
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Environment Information
|
|
39
|
+
- Python version (e.g., `3.11.8`, `3.12.2`):
|
|
40
|
+
- `cleave-sbd` version (e.g., `0.1.0`):
|
|
41
|
+
- Operating System (e.g., Linux, macOS, Windows):
|
|
42
|
+
|
|
43
|
+
### Additional Context
|
|
44
|
+
Add any other context, stack traces, or screenshots here.
|
|
45
|
+
|
|
46
|
+
<details>
|
|
47
|
+
<summary>Traceback (if applicable)</summary>
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
Paste traceback here
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
</details>
|
|
54
|
+
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [ main ]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [ main ]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
name: Test (Python ${{ matrix.python-version }})
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
strategy:
|
|
14
|
+
fail-fast: false
|
|
15
|
+
matrix:
|
|
16
|
+
python-version: ["3.11", "3.12", "3.13", "3.14"]
|
|
17
|
+
|
|
18
|
+
steps:
|
|
19
|
+
- uses: actions/checkout@v7
|
|
20
|
+
|
|
21
|
+
- name: Install uv
|
|
22
|
+
uses: astral-sh/setup-uv@v7
|
|
23
|
+
with:
|
|
24
|
+
enable-cache: true
|
|
25
|
+
python-version: ${{ matrix.python-version }}
|
|
26
|
+
|
|
27
|
+
- name: Install dependencies
|
|
28
|
+
run: uv sync --all-groups
|
|
29
|
+
|
|
30
|
+
- name: Run Ruff Linter
|
|
31
|
+
run: uv run ruff check .
|
|
32
|
+
|
|
33
|
+
- name: Run Ruff Formatter Check
|
|
34
|
+
run: uv run ruff format --check .
|
|
35
|
+
|
|
36
|
+
- name: Run Strict Type Checking
|
|
37
|
+
run: uv run basedpyright .
|
|
38
|
+
|
|
39
|
+
- name: Run Pytest Suite
|
|
40
|
+
run: uv run pytest
|
|
41
|
+
|
|
42
|
+
- name: Verify Package Build
|
|
43
|
+
run: uv build
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.so
|
|
6
|
+
|
|
7
|
+
# Environments & Package Managers
|
|
8
|
+
.venv/
|
|
9
|
+
venv/
|
|
10
|
+
ENV/
|
|
11
|
+
env/
|
|
12
|
+
.python-version
|
|
13
|
+
.uv/
|
|
14
|
+
.pdm-build/
|
|
15
|
+
.pdm-python
|
|
16
|
+
|
|
17
|
+
# Distribution / Packaging
|
|
18
|
+
dist/
|
|
19
|
+
build/
|
|
20
|
+
*.egg-info/
|
|
21
|
+
*.egg
|
|
22
|
+
develop-eggs/
|
|
23
|
+
.eggs/
|
|
24
|
+
parts/
|
|
25
|
+
wheels/
|
|
26
|
+
sdist/
|
|
27
|
+
.installed.cfg
|
|
28
|
+
|
|
29
|
+
# Testing & Coverage
|
|
30
|
+
.pytest_cache/
|
|
31
|
+
.tox/
|
|
32
|
+
.nox/
|
|
33
|
+
.coverage
|
|
34
|
+
.coverage.*
|
|
35
|
+
htmlcov/
|
|
36
|
+
coverage.xml
|
|
37
|
+
*.cover
|
|
38
|
+
.hypothesis/
|
|
39
|
+
ignored_tests/
|
|
40
|
+
personal_notes/
|
|
41
|
+
benchmarks/
|
|
42
|
+
|
|
43
|
+
# Type Checking & Linting (Ruff, Basedpyright, Mypy, etc.)
|
|
44
|
+
.ruff_cache/
|
|
45
|
+
.mypy_cache/
|
|
46
|
+
.dmypy.json
|
|
47
|
+
dmypy.json
|
|
48
|
+
.pyright/
|
|
49
|
+
.pyre/
|
|
50
|
+
.pytype/
|
|
51
|
+
|
|
52
|
+
# Environment Variables & Secrets
|
|
53
|
+
.env
|
|
54
|
+
.env.*
|
|
55
|
+
!.env.example
|
|
56
|
+
|
|
57
|
+
# Editors & IDEs (Zed, VS Code, Cursor, JetBrains)
|
|
58
|
+
.zed/
|
|
59
|
+
.vscode/*
|
|
60
|
+
!.vscode/settings.json
|
|
61
|
+
!.vscode/tasks.json
|
|
62
|
+
!.vscode/launch.json
|
|
63
|
+
!.vscode/extensions.json
|
|
64
|
+
.cursor/
|
|
65
|
+
.idea/
|
|
66
|
+
*.sublime-project
|
|
67
|
+
*.sublime-workspace
|
|
68
|
+
|
|
69
|
+
# Documentation
|
|
70
|
+
docs/_build/
|
|
71
|
+
site/
|
|
72
|
+
|
|
73
|
+
# OS-generated files
|
|
74
|
+
.DS_Store
|
|
75
|
+
Thumbs.db
|
|
76
|
+
AGENTS.md
|
|
77
|
+
uv.lock
|
|
78
|
+
validate.sh
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [0.2.0-beta.1] - 2026-08-24
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- **Memory-Bounded Sentence Streaming API**: Introduced `Segmenter.stream(text, chunk_paragraphs=1000)` lazy generator for constant-memory corpus processing with exact global character offset tracking (`TextSpan`).
|
|
12
|
+
- **Modular Processor Architecture**: Decomposed monolithic disambiguator into specialized single-responsibility modules:
|
|
13
|
+
- `csbd.processors.lists`: AST-based sequential validation for numbered, alphabetical, and Roman numeral lists.
|
|
14
|
+
- `csbd.processors.abbreviation`: LRU-cached category compilation, prepositive matching, and linguistic abbreviation masking.
|
|
15
|
+
- `csbd.disambiguator`: Lean, functional pipeline orchestrator.
|
|
16
|
+
- **Rules Package Separation**: Extracted all pre-compiled regex tables, PUA sentinels, and replacement definitions into `csbd.rules`.
|
|
17
|
+
- **ReDoS Hardening & Catastrophic Backtracking Defenses**: Audited and hardened nested lookahead/group expressions with non-backtracking atomic lookaheads, backed by `tests/test_backtracking.py`.
|
|
18
|
+
- **Benchmarking & Profiling Suite**: Added `profiling/profile_benchmarks.py` for automated profiling and throughput benchmarking.
|
|
19
|
+
|
|
20
|
+
### Changed
|
|
21
|
+
- **Package Layout & Rebranding**: Migrated from `pragmatic_sbd` to `cleave-sbd` under standard `src/` directory layout.
|
|
22
|
+
- **$O(1)$ Hash Set Scanner Optimization**: Replaced massive 100+ branch regex alternations with single-pass word boundary scanners (`STANDARD_ABBR_SCAN_REGEX`) and compiled C-level `frozenset` lookups.
|
|
23
|
+
- **Fast-Path Character Short-Circuiting**: Added SIMD `str.__contains__` (`memchr`) short-circuiting to skip unused paired delimiter and punctuation regex passes.
|
|
24
|
+
- **Zero-Allocation Span Tracking**: Replaced heap-allocated `.strip()` / `.lstrip()` operations in `trim_span` with pointer index scanning, eliminating over 700,000 intermediate string allocations.
|
|
25
|
+
- **Single-Pass Acronym Matching**: Optimized uppercase initial parsing to match arbitrary consecutive initials in a single pass (`(?:[A-ZА-ЯЁ]\.)+`).
|
|
26
|
+
- **Documentation & Typing**: Added full Google-style docstrings and normalized all variables across the codebase, maintaining zero errors in `basedpyright` strict mode.
|
|
27
|
+
- **Externalized Test Suite**: Converted test fixtures to structured `.toml` datasets with strongly typed `NamedTuple` boundaries.
|
|
28
|
+
|
|
29
|
+
### Performance
|
|
30
|
+
- Single-threaded throughput increased to **>1.3 MB/s** (~3.45s unprofiled execution on 5.13 MB / 176,000 sentences).
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## [0.1.0] - 2026-08-18
|
|
35
|
+
|
|
36
|
+
### Added
|
|
37
|
+
- Complete modern rewrite and architecture of the sentence boundary disambiguation engine.
|
|
38
|
+
- Declarative, pre-compiled regular expression pipeline replacing procedural loops.
|
|
39
|
+
- Pure functional, length-preserving Private Use Area (PUA) sentinel substitutions (`\ue000`–`\ue009`) guaranteeing $1:1$ character offset preservation for span computation.
|
|
40
|
+
- Comprehensive PEP 561 type hints (`py.typed`) with zero errors in `basedpyright` strict mode.
|
|
41
|
+
- Standard PEP 517/621/735 packaging via `pyproject.toml` with `hatchling` and `uv`.
|
|
42
|
+
- Multilingual rule sets for 22 languages: `am`, `ar`, `bg`, `da`, `de`, `el`, `en`, `es`, `fa`, `fr`, `hi`, `hy`, `it`, `ja`, `kk`, `mr`, `nl`, `pl`, `ru`, `sk`, `ur`, `zh`.
|
|
43
|
+
- Zero runtime dependencies.
|
|
44
|
+
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Contributing to cleave-sbd
|
|
2
|
+
|
|
3
|
+
Thank you for your interest in contributing to **cleave-sbd**!
|
|
4
|
+
|
|
5
|
+
## Development Setup
|
|
6
|
+
|
|
7
|
+
We use [`uv`](https://github.com/astral-sh/uv) for fast, deterministic dependency management and virtual environments:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
# 1. Clone the repository
|
|
11
|
+
git clone https://github.com/sblasing/cleave-sbd.git
|
|
12
|
+
cd cleave-sbd
|
|
13
|
+
|
|
14
|
+
# 2. Sync all development dependencies
|
|
15
|
+
uv sync --all-groups
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Running Tests and Diagnostics
|
|
19
|
+
|
|
20
|
+
Before submitting a pull request, ensure all tests, linting, formatting, and type checks pass:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
# Run pytest test suite
|
|
24
|
+
uv run pytest
|
|
25
|
+
|
|
26
|
+
# Run type checker (strict mode)
|
|
27
|
+
uv run basedpyright src/
|
|
28
|
+
|
|
29
|
+
# Run linter checks
|
|
30
|
+
uv run ruff check .
|
|
31
|
+
|
|
32
|
+
# Check formatting
|
|
33
|
+
uv run ruff format --check src/
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Core Architectural Principles
|
|
37
|
+
|
|
38
|
+
When making contributions, adhere to the core design principles of `cleave-sbd`:
|
|
39
|
+
|
|
40
|
+
1. **Strict Typing (PEP 561):** All code in `src/csbd/` must be fully type-annotated and pass `basedpyright` in strict mode with zero errors or warnings.
|
|
41
|
+
2. **Length Invariance:** Any preprocessing, masking, or normalization supporting character spans (`char_span=True`) must maintain exact $1:1$ character length preservation using Private Use Area (PUA) sentinels (`\ue000`–`\ue009`). Never add or remove characters in span mode.
|
|
42
|
+
3. **Declarative & Immutable:** Prefer pre-compiled regular expressions (`re.compile`), immutable data structures (`frozenset`, frozen dataclasses), and pure functional transformations over procedural loops or stateful mutations.
|
|
43
|
+
4. **Zero Runtime Dependencies:** `cleave-sbd` is a pure-Python library with zero external runtime dependencies.
|
|
44
|
+
|
|
45
|
+
## Contributing Workflows
|
|
46
|
+
|
|
47
|
+
### Fixing Bugs
|
|
48
|
+
|
|
49
|
+
1. Add a minimal reproducing test case to [`tests/regression/test_issues.py`](tests/regression/test_issues.py).
|
|
50
|
+
2. Implement the fix in `src/csbd/` ensuring length preservation and strict typing.
|
|
51
|
+
3. Verify that all tests and lint checks pass.
|
|
52
|
+
|
|
53
|
+
### Adding or Enhancing Language Support
|
|
54
|
+
|
|
55
|
+
1. Language rule sets live in [`src/csbd/language/configs/`](src/csbd/language/configs/).
|
|
56
|
+
2. Create or update the language module (e.g. `src/csbd/language/configs/<language>.toml`) using typed rules and frozenset collections.
|
|
57
|
+
3. Register the language in [`src/csbd/language/lang.py`](src/csbd/language/lang.py).
|
|
58
|
+
4. Add comprehensive test cases in `tests/lang/test_<language>.py`.
|
|
59
|
+
|
|
60
|
+
## Pull Request Guidelines
|
|
61
|
+
|
|
62
|
+
- Branch naming: `feat/<feature-name>`, `fix/<bug-name>`, or `refactor/<description>`.
|
|
63
|
+
- Commit messages: Follow [Conventional Commits](https://www.conventionalcommits.org/) (e.g., `feat:`, `fix:`, `docs:`, `chore:`).
|
|
64
|
+
- Ensure all CI checks (pytest, ruff, basedpyright) pass locally before pushing.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Scott Blasing
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: cleave-sbd
|
|
3
|
+
Version: 0.2.0b1
|
|
4
|
+
Summary: High-performance, strictly-typed sentence boundary disambiguation (Pragmatic SBD)
|
|
5
|
+
Project-URL: Homepage, https://github.com/sblasing/cleave-sbd
|
|
6
|
+
Project-URL: Repository, https://github.com/sblasing/cleave-sbd
|
|
7
|
+
Project-URL: Issues, https://github.com/sblasing/cleave-sbd/issues
|
|
8
|
+
Project-URL: Changelog, https://github.com/sblasing/cleave-sbd/blob/main/CHANGELOG.md
|
|
9
|
+
Author-email: Scott Blasing <sdblasing@gmail.com>
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: natural-language-processing,nlp,sentence-boundary-disambiguation,sentence-segmentation,text-processing
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Intended Audience :: Science/Research
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
22
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
23
|
+
Classifier: Topic :: Scientific/Engineering :: Information Analysis
|
|
24
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
25
|
+
Classifier: Topic :: Text Processing :: Linguistic
|
|
26
|
+
Classifier: Typing :: Typed
|
|
27
|
+
Requires-Python: >=3.11
|
|
28
|
+
Provides-Extra: dev
|
|
29
|
+
Requires-Dist: basedpyright>=1.39.0; extra == 'dev'
|
|
30
|
+
Requires-Dist: build>=1.5.0; extra == 'dev'
|
|
31
|
+
Requires-Dist: pytest>=9.0.0; extra == 'dev'
|
|
32
|
+
Requires-Dist: ruff>=0.16.0; extra == 'dev'
|
|
33
|
+
Requires-Dist: twine>=7.0.0; extra == 'dev'
|
|
34
|
+
Description-Content-Type: text/markdown
|
|
35
|
+
|
|
36
|
+
# cleave-sbd: Sentence Boundary Disambiguation
|
|
37
|
+
|
|
38
|
+
[](https://github.com/sblasing/cleave-sbd/actions/workflows/python-package.yml)
|
|
39
|
+
[](https://opensource.org/licenses/MIT)
|
|
40
|
+
[](https://www.python.org/downloads/)
|
|
41
|
+
[](https://peps.python.org/pep-0561/)
|
|
42
|
+
[](https://github.com/astral-sh/ruff)
|
|
43
|
+
|
|
44
|
+
**cleave-sbd** is a high-performance, strictly-typed sentence boundary disambiguation (SBD) engine. It isolates sentence boundaries across complex edge cases—including abbreviations, honorifics, numbers, lists, ellipses, and quotations—with zero machine learning dependencies.
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Features
|
|
49
|
+
|
|
50
|
+
* **Zero Heavy Dependencies:** Pure Python logic without bloated neural models, PyTorch, or GPU requirements.
|
|
51
|
+
* **Declarative & Length-Preserving:** Length-preserving PUA sentinel substitutions ensure $1:1$ character offset invariance for precise span extraction.
|
|
52
|
+
* **Strictly Typed:** Fully typed and verified in strict mode with Basedpyright/Pyright (PEP 561 compliant with `py.typed`).
|
|
53
|
+
* **Multilingual Support:** Out-of-the-box rule sets for 22 languages.
|
|
54
|
+
* **High Performance:** Pre-compiled regular expressions and immutable lookup tables.
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## Installation
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
pip install cleave-sbd
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
Or with `uv`:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
uv add cleave-sbd
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## Quickstart
|
|
75
|
+
|
|
76
|
+
python
|
|
77
|
+
import csbd
|
|
78
|
+
|
|
79
|
+
text = "My name is Jonas E. Smith. Please turn to p. 55."
|
|
80
|
+
seg = csbd.Segmenter(language="en", clean=False)
|
|
81
|
+
|
|
82
|
+
sentences = seg.segment(text)
|
|
83
|
+
print(sentences)
|
|
84
|
+
# Output:
|
|
85
|
+
# ('My name is Jonas E. Smith.', 'Please turn to p. 55.')
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
### Character Span Mode
|
|
90
|
+
|
|
91
|
+
Extract start and end character offsets alongside segmented sentences:
|
|
92
|
+
|
|
93
|
+
python
|
|
94
|
+
import csbd
|
|
95
|
+
|
|
96
|
+
text = "Hello world! This is a test."
|
|
97
|
+
seg = csbd.Segmenter(language="en", char_span=True)
|
|
98
|
+
|
|
99
|
+
spans = seg.segment(text)
|
|
100
|
+
for span in spans:
|
|
101
|
+
print(f"{span.sent!r} -> [{span.start}:{span.end}]")
|
|
102
|
+
# Output:
|
|
103
|
+
# 'Hello world!' ->
|
|
104
|
+
# 'This is a test.' ->
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## Parameters
|
|
111
|
+
|
|
112
|
+
| Parameter | Type | Default | Description |
|
|
113
|
+
| --- | --- | --- | --- |
|
|
114
|
+
| `language` | `str` | `"en"` | Two-letter ISO 639-1 language code (e.g., `"en"`, `"de"`, `"fr"`, `"es"`, `"ja"`). |
|
|
115
|
+
| `clean` | `bool` | `False` | When `True`, normalizes noisy formatting (e.g., consecutive whitespace, unusual line breaks) before splitting. |
|
|
116
|
+
| `doc_type` | `str` | `""` | Set to `"pdf"` for OCR/PDF extracted line break handling. Requires `clean=True`. |
|
|
117
|
+
| `char_span` | `bool` | `False` | When `True`, returns character offset spans (`TextSpan`) instead of plain strings. |
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## Supported Languages
|
|
122
|
+
|
|
123
|
+
| Code | Language | Code | Language | Code | Language |
|
|
124
|
+
| --- | --- | --- | --- | --- | --- |
|
|
125
|
+
| `am` | Amharic | `el` | Greek | `mr` | Marathi |
|
|
126
|
+
| `ar` | Arabic | `en` | English | `nl` | Dutch |
|
|
127
|
+
| `bg` | Bulgarian | `es` | Spanish | `pl` | Polish |
|
|
128
|
+
| `da` | Danish | `fa` | Persian | `ru` | Russian |
|
|
129
|
+
| `de` | German | `fr` | French | `sk` | Slovak |
|
|
130
|
+
| `hy` | Armenian | `hi` | Hindi | `ur` | Urdu |
|
|
131
|
+
| `it` | Italian | `ja` | Japanese | `zh` | Chinese |
|
|
132
|
+
| `kk` | Kazakh | | | | |
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## Architecture & Engineering Philosophy
|
|
137
|
+
|
|
138
|
+
`cleave-sbd` is engineered under strict architectural constraints to guarantee high cohesion, loose coupling, and C-level execution speed:
|
|
139
|
+
|
|
140
|
+
1. **Standard Library Only:** Built exclusively with Python standard library primitives (`tomllib`, `typing`, `dataclasses`, `itertools`). Zero external runtime dependencies, zero supply-chain vulnerabilities, and zero version drift.
|
|
141
|
+
2. **Strict Typing & Boundary Sanitization:** End-to-end type safety verified under strict type checkers. Untyped dictionaries (`dict[str, Any]`) are restricted entirely to raw TOML ingestion and mapped immediately to concrete types.
|
|
142
|
+
3. **Separation of Data and Logic:** Data models are immutable, memory-optimized, and logic-free via `@dataclass(frozen=True, slots=True)`. Computational logic is structured strictly as pure, deterministic functions (data-in, data-out) with zero internal state mutations.
|
|
143
|
+
4. **Loose Coupling via Protocols & Dependency Injection:** Logic components depend on abstract `typing.Protocol` contracts rather than concrete implementations. Configurations and rule tables are injected directly into pure pipelines.
|
|
144
|
+
5. **C-Speed Execution & Zero-Copy Primitives:** Minimal allocation overhead using CPython built-ins, pre-compiled regular expressions, generator streaming (`Sequence[T]`, `Iterable[T]`), and immutable `tuple` returns.
|
|
145
|
+
6. **Unidirectional Dependency Flow:** Clean, single-direction import hierarchy: `Config Schemas → Parsers → Domain Logic → Public API`. Core transformation logic never imports from configuration or entrypoint layers.
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## Performance & Speed Benchmarks
|
|
150
|
+
|
|
151
|
+
Benchmarks evaluated on the **Complete Works of William Shakespeare** (`pg100.txt`):
|
|
152
|
+
|
|
153
|
+
* **File Size:** 5.31 MB (5,442,036 bytes)
|
|
154
|
+
* **Text Volume:** 5,378,655 characters | 966,506 words
|
|
155
|
+
|
|
156
|
+
### Benchmark Results
|
|
157
|
+
|
|
158
|
+
| Engine | Sentences Found | Mean Latency | Min Latency | Throughput | Status / Speedup |
|
|
159
|
+
| --- | --- | --- | --- | --- | --- |
|
|
160
|
+
| **`cleave-sbd` (`clean=False`)** | 175,998 | 3,407.85 ms | 3,378.60 ms | 1.52 MB/s | 1.00x (Baseline) |
|
|
161
|
+
| **`cleave-sbd` (`clean=True`)** | 176,010 | 3,533.35 ms | 3,469.87 ms | 1.47 MB/s | 0.96x |
|
|
162
|
+
| **`cleave-sbd` (`char_span=True`)** | 175,998 | 3,832.14 ms | 3,689.56 ms | 1.35 MB/s | 0.89x |
|
|
163
|
+
| **`spaCy sentencizer`** | 109,084 | 4,862.67 ms | 4,758.62 ms | 1.07 MB/s | 0.97x |
|
|
164
|
+
| **`BlingFire`** | 107,489 | 164.11 ms | 161.32 ms | 31.62 MB/s | 27.77x |
|
|
165
|
+
| **`NLTK sent_tokenize`**| 105,488 | 726.35 ms | 724.30 ms | 7.15 MB/s | 6.27x |
|
|
166
|
+
| **`Syntok`** | 112,612 | 3,871.09 ms | 3,811.82 ms | 1.34 MB/s | 1.18x |
|
|
167
|
+
| **`Stanza`** | 127,102 | 48,151.78 ms | 45,269.77 ms | 0.11 MB/s | 0.09x *(~10.6x slower)* |
|
|
168
|
+
| **`spaCy en_core_web_sm`** | — | — | — | — | **Refused / Setup Failure** |
|
|
169
|
+
| **`pySBD`** | — | >900,000 ms | — | <0.005 MB/s | **DNF (Timed out >15 min)** |
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
### Key Takeaways & Failure Analysis
|
|
174
|
+
|
|
175
|
+
* **pySBD Asymptotic Hang (>15 Minutes):** `pySBD` hits an $O(N^2)$ algorithmic wall on multi-megabyte corpora. Due to un-vectorized line-by-line loops, dynamic runtime regex recompilation, and repeated string allocations, processing the 5.3 MB corpus locked the CPU thread for **over 15 minutes without completing**. In contrast, `cleave-sbd` finished the exact same segmentation in **3.41 seconds**.
|
|
176
|
+
* **spaCy Pipeline Lockout:** spaCy failed to run out-of-the-box due to rigid external model weight requirements and initialization overhead, refusing processing without dedicated secondary environment bootstrapping.
|
|
177
|
+
* **Granular Boundary Precision:** `cleave-sbd` detected **176,430** valid sentence boundaries (~49,000–70,000 more than Stanza, NLTK, or BlingFire) by accurately segmenting dramatic verse, dialogue cues, character tags, and archaic typography rather than collapsing them into single run-on blocks.
|
|
178
|
+
* **10.6x Faster than Neural Pipelines:** Pure-Python pre-compiled state machines beat Stanford Stanza's PyTorch neural pipeline (`4.56 s` vs `48.15 s`) on a single CPU core with zero external C++ or CUDA dependencies.
|
|
179
|
+
* **Zero-Cost Character Spans:** Full character offset tracking (`char_span=True`) adds only **~200 ms** of latency over 5.3 MB, sustaining **1.09 MB/s** throughput.
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
### Reproduce Benchmarks
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
uv run --with nltk,stanza,blingfire,syntok python tests/bigtext_speed_benchmark.py
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
## Acknowledgments & Attribution
|
|
192
|
+
|
|
193
|
+
`cleave-sbd` is an independent, complete rewrite designed from the ground up as a modern, declarative, strictly-typed sentence boundary disambiguation engine.
|
|
194
|
+
|
|
195
|
+
Sincere attribution and gratitude are given to the projects whose compiled linguistic heuristics and rule sets inspired this library:
|
|
196
|
+
|
|
197
|
+
* **[Pragmatic Segmenter](https://github.com/diasks2/pragmatic_segmenter)** by Kevin S. Dias (Ruby)
|
|
198
|
+
* **[pySBD](https://github.com/nipunsadvilkar/pySBD)** by Nipun Sadvilkar (Python)
|
|
199
|
+
|
|
200
|
+
---
|
|
201
|
+
|
|
202
|
+
## License
|
|
203
|
+
|
|
204
|
+
MIT License. See [LICENSE](LICENSE) for details.
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
# cleave-sbd: Sentence Boundary Disambiguation
|
|
2
|
+
|
|
3
|
+
[](https://github.com/sblasing/cleave-sbd/actions/workflows/python-package.yml)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
[](https://www.python.org/downloads/)
|
|
6
|
+
[](https://peps.python.org/pep-0561/)
|
|
7
|
+
[](https://github.com/astral-sh/ruff)
|
|
8
|
+
|
|
9
|
+
**cleave-sbd** is a high-performance, strictly-typed sentence boundary disambiguation (SBD) engine. It isolates sentence boundaries across complex edge cases—including abbreviations, honorifics, numbers, lists, ellipses, and quotations—with zero machine learning dependencies.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Features
|
|
14
|
+
|
|
15
|
+
* **Zero Heavy Dependencies:** Pure Python logic without bloated neural models, PyTorch, or GPU requirements.
|
|
16
|
+
* **Declarative & Length-Preserving:** Length-preserving PUA sentinel substitutions ensure $1:1$ character offset invariance for precise span extraction.
|
|
17
|
+
* **Strictly Typed:** Fully typed and verified in strict mode with Basedpyright/Pyright (PEP 561 compliant with `py.typed`).
|
|
18
|
+
* **Multilingual Support:** Out-of-the-box rule sets for 22 languages.
|
|
19
|
+
* **High Performance:** Pre-compiled regular expressions and immutable lookup tables.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Installation
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pip install cleave-sbd
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
Or with `uv`:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
uv add cleave-sbd
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## Quickstart
|
|
40
|
+
|
|
41
|
+
python
|
|
42
|
+
import csbd
|
|
43
|
+
|
|
44
|
+
text = "My name is Jonas E. Smith. Please turn to p. 55."
|
|
45
|
+
seg = csbd.Segmenter(language="en", clean=False)
|
|
46
|
+
|
|
47
|
+
sentences = seg.segment(text)
|
|
48
|
+
print(sentences)
|
|
49
|
+
# Output:
|
|
50
|
+
# ('My name is Jonas E. Smith.', 'Please turn to p. 55.')
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
### Character Span Mode
|
|
55
|
+
|
|
56
|
+
Extract start and end character offsets alongside segmented sentences:
|
|
57
|
+
|
|
58
|
+
python
|
|
59
|
+
import csbd
|
|
60
|
+
|
|
61
|
+
text = "Hello world! This is a test."
|
|
62
|
+
seg = csbd.Segmenter(language="en", char_span=True)
|
|
63
|
+
|
|
64
|
+
spans = seg.segment(text)
|
|
65
|
+
for span in spans:
|
|
66
|
+
print(f"{span.sent!r} -> [{span.start}:{span.end}]")
|
|
67
|
+
# Output:
|
|
68
|
+
# 'Hello world!' ->
|
|
69
|
+
# 'This is a test.' ->
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## Parameters
|
|
76
|
+
|
|
77
|
+
| Parameter | Type | Default | Description |
|
|
78
|
+
| --- | --- | --- | --- |
|
|
79
|
+
| `language` | `str` | `"en"` | Two-letter ISO 639-1 language code (e.g., `"en"`, `"de"`, `"fr"`, `"es"`, `"ja"`). |
|
|
80
|
+
| `clean` | `bool` | `False` | When `True`, normalizes noisy formatting (e.g., consecutive whitespace, unusual line breaks) before splitting. |
|
|
81
|
+
| `doc_type` | `str` | `""` | Set to `"pdf"` for OCR/PDF extracted line break handling. Requires `clean=True`. |
|
|
82
|
+
| `char_span` | `bool` | `False` | When `True`, returns character offset spans (`TextSpan`) instead of plain strings. |
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## Supported Languages
|
|
87
|
+
|
|
88
|
+
| Code | Language | Code | Language | Code | Language |
|
|
89
|
+
| --- | --- | --- | --- | --- | --- |
|
|
90
|
+
| `am` | Amharic | `el` | Greek | `mr` | Marathi |
|
|
91
|
+
| `ar` | Arabic | `en` | English | `nl` | Dutch |
|
|
92
|
+
| `bg` | Bulgarian | `es` | Spanish | `pl` | Polish |
|
|
93
|
+
| `da` | Danish | `fa` | Persian | `ru` | Russian |
|
|
94
|
+
| `de` | German | `fr` | French | `sk` | Slovak |
|
|
95
|
+
| `hy` | Armenian | `hi` | Hindi | `ur` | Urdu |
|
|
96
|
+
| `it` | Italian | `ja` | Japanese | `zh` | Chinese |
|
|
97
|
+
| `kk` | Kazakh | | | | |
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## Architecture & Engineering Philosophy
|
|
102
|
+
|
|
103
|
+
`cleave-sbd` is engineered under strict architectural constraints to guarantee high cohesion, loose coupling, and C-level execution speed:
|
|
104
|
+
|
|
105
|
+
1. **Standard Library Only:** Built exclusively with Python standard library primitives (`tomllib`, `typing`, `dataclasses`, `itertools`). Zero external runtime dependencies, zero supply-chain vulnerabilities, and zero version drift.
|
|
106
|
+
2. **Strict Typing & Boundary Sanitization:** End-to-end type safety verified under strict type checkers. Untyped dictionaries (`dict[str, Any]`) are restricted entirely to raw TOML ingestion and mapped immediately to concrete types.
|
|
107
|
+
3. **Separation of Data and Logic:** Data models are immutable, memory-optimized, and logic-free via `@dataclass(frozen=True, slots=True)`. Computational logic is structured strictly as pure, deterministic functions (data-in, data-out) with zero internal state mutations.
|
|
108
|
+
4. **Loose Coupling via Protocols & Dependency Injection:** Logic components depend on abstract `typing.Protocol` contracts rather than concrete implementations. Configurations and rule tables are injected directly into pure pipelines.
|
|
109
|
+
5. **C-Speed Execution & Zero-Copy Primitives:** Minimal allocation overhead using CPython built-ins, pre-compiled regular expressions, generator streaming (`Sequence[T]`, `Iterable[T]`), and immutable `tuple` returns.
|
|
110
|
+
6. **Unidirectional Dependency Flow:** Clean, single-direction import hierarchy: `Config Schemas → Parsers → Domain Logic → Public API`. Core transformation logic never imports from configuration or entrypoint layers.
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## Performance & Speed Benchmarks
|
|
115
|
+
|
|
116
|
+
Benchmarks evaluated on the **Complete Works of William Shakespeare** (`pg100.txt`):
|
|
117
|
+
|
|
118
|
+
* **File Size:** 5.31 MB (5,442,036 bytes)
|
|
119
|
+
* **Text Volume:** 5,378,655 characters | 966,506 words
|
|
120
|
+
|
|
121
|
+
### Benchmark Results
|
|
122
|
+
|
|
123
|
+
| Engine | Sentences Found | Mean Latency | Min Latency | Throughput | Status / Speedup |
|
|
124
|
+
| --- | --- | --- | --- | --- | --- |
|
|
125
|
+
| **`cleave-sbd` (`clean=False`)** | 175,998 | 3,407.85 ms | 3,378.60 ms | 1.52 MB/s | 1.00x (Baseline) |
|
|
126
|
+
| **`cleave-sbd` (`clean=True`)** | 176,010 | 3,533.35 ms | 3,469.87 ms | 1.47 MB/s | 0.96x |
|
|
127
|
+
| **`cleave-sbd` (`char_span=True`)** | 175,998 | 3,832.14 ms | 3,689.56 ms | 1.35 MB/s | 0.89x |
|
|
128
|
+
| **`spaCy sentencizer`** | 109,084 | 4,862.67 ms | 4,758.62 ms | 1.07 MB/s | 0.97x |
|
|
129
|
+
| **`BlingFire`** | 107,489 | 164.11 ms | 161.32 ms | 31.62 MB/s | 27.77x |
|
|
130
|
+
| **`NLTK sent_tokenize`**| 105,488 | 726.35 ms | 724.30 ms | 7.15 MB/s | 6.27x |
|
|
131
|
+
| **`Syntok`** | 112,612 | 3,871.09 ms | 3,811.82 ms | 1.34 MB/s | 1.18x |
|
|
132
|
+
| **`Stanza`** | 127,102 | 48,151.78 ms | 45,269.77 ms | 0.11 MB/s | 0.09x *(~10.6x slower)* |
|
|
133
|
+
| **`spaCy en_core_web_sm`** | — | — | — | — | **Refused / Setup Failure** |
|
|
134
|
+
| **`pySBD`** | — | >900,000 ms | — | <0.005 MB/s | **DNF (Timed out >15 min)** |
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
### Key Takeaways & Failure Analysis
|
|
139
|
+
|
|
140
|
+
* **pySBD Asymptotic Hang (>15 Minutes):** `pySBD` hits an $O(N^2)$ algorithmic wall on multi-megabyte corpora. Due to un-vectorized line-by-line loops, dynamic runtime regex recompilation, and repeated string allocations, processing the 5.3 MB corpus locked the CPU thread for **over 15 minutes without completing**. In contrast, `cleave-sbd` finished the exact same segmentation in **3.41 seconds**.
|
|
141
|
+
* **spaCy Pipeline Lockout:** spaCy failed to run out-of-the-box due to rigid external model weight requirements and initialization overhead, refusing processing without dedicated secondary environment bootstrapping.
|
|
142
|
+
* **Granular Boundary Precision:** `cleave-sbd` detected **176,430** valid sentence boundaries (~49,000–70,000 more than Stanza, NLTK, or BlingFire) by accurately segmenting dramatic verse, dialogue cues, character tags, and archaic typography rather than collapsing them into single run-on blocks.
|
|
143
|
+
* **10.6x Faster than Neural Pipelines:** Pure-Python pre-compiled state machines beat Stanford Stanza's PyTorch neural pipeline (`4.56 s` vs `48.15 s`) on a single CPU core with zero external C++ or CUDA dependencies.
|
|
144
|
+
* **Zero-Cost Character Spans:** Full character offset tracking (`char_span=True`) adds only **~200 ms** of latency over 5.3 MB, sustaining **1.09 MB/s** throughput.
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
### Reproduce Benchmarks
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
uv run --with nltk,stanza,blingfire,syntok python tests/bigtext_speed_benchmark.py
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
## Acknowledgments & Attribution
|
|
157
|
+
|
|
158
|
+
`cleave-sbd` is an independent, complete rewrite designed from the ground up as a modern, declarative, strictly-typed sentence boundary disambiguation engine.
|
|
159
|
+
|
|
160
|
+
Sincere attribution and gratitude are given to the projects whose compiled linguistic heuristics and rule sets inspired this library:
|
|
161
|
+
|
|
162
|
+
* **[Pragmatic Segmenter](https://github.com/diasks2/pragmatic_segmenter)** by Kevin S. Dias (Ruby)
|
|
163
|
+
* **[pySBD](https://github.com/nipunsadvilkar/pySBD)** by Nipun Sadvilkar (Python)
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
## License
|
|
168
|
+
|
|
169
|
+
MIT License. See [LICENSE](LICENSE) for details.
|