Blazingly fast text chunkers in Rust with pythonic bindings
Project description
konan
Like the paper angel of the Akatsuki, konan folds your documents into precise pieces — Rust chunkers behind a Python API.
Why konan?
- 🦀 Rust core — all chunking runs in native code, no Python-loop overhead
- ⚡ Multithreaded by default —
chunk_many()fans out across all cores via rayon and releases the GIL - 🌀 Real async —
chunk_async/chunk_many_asyncare nativeasync def, not thread-pool wrappers - 🎯 Char-accurate offsets —
text[chunk.start:chunk.end] == chunk.text, always (Python slicing semantics, emoji-safe) - 🧠 Semantic chunking — splits on topic shifts using any OpenAI-compatible embeddings endpoint, or your own async embedder
- 🔌 Ports & adapters — the
Embedderport is injectable; bring your own backend
Strategies
| Chunker | Splits by | Best for |
|---|---|---|
NaiveChunker |
fixed word count | quick & dirty baselines |
FixedSizeChunker |
chars, sentence-aware, overlap | classic RAG pipelines |
RecursiveChunker |
separator hierarchy (\n\n → \n → … ) |
general text, LangChain-compatible |
SentenceChunker |
unicode sentence boundaries | prose, multilingual text |
MarkdownChunker |
document structure + heading breadcrumbs | docs, wikis, READMEs |
TokenChunker |
exact token counts (cl100k_base, o200k_base) |
embedding-model token limits |
SemanticChunker |
embedding similarity drops | topic-coherent chunks |
Installation
uv add konan # or: pip install konan
Quickstart
from konan import RecursiveChunker
chunker = RecursiveChunker(chunk_size=1000, chunk_overlap=200)
chunks = chunker.chunk(open("moby_dick.txt").read())
print(chunks[0].text, chunks[0].start, chunks[0].end, chunks[0].hash)
Parallel — all cores, one call
# rayon work-stealing across every core, GIL released:
all_chunks = chunker.chunk_many(documents)
Async — real async def, no thread-pool wrappers
chunks = await chunker.chunk_async(text)
batches = await chunker.chunk_many_async(documents)
Markdown with breadcrumbs
from konan import MarkdownChunker
chunks = MarkdownChunker(chunk_size=800).chunk(readme_text)
# chunk text is prefixed with its heading trail: "# Guide > ## Install\n\n..."
# code fences are never split
Token-exact chunks
from konan import TokenChunker
chunker = TokenChunker(chunk_size=512, chunk_overlap=64, encoding="o200k_base")
Semantic chunking
Point it at any OpenAI-compatible /embeddings endpoint (OpenAI, vLLM,
Ollama, LiteLLM, …):
from konan import OpenAIEmbedder, SemanticChunker
embedder = OpenAIEmbedder(
base_url="https://api.openai.com/v1",
model="text-embedding-3-small",
api_key="sk-...",
batch_size=128, # texts per request
timeout=30.0, # request timeout, seconds
max_retries=2, # exponential backoff on 429/5xx/connect errors
dimensions=512, # optional: shorten text-embedding-3-* vectors
)
chunker = SemanticChunker(embedder=embedder, threshold=0.75)
chunks = await chunker.chunk_async(article)
Or inject your own embedder — any async callable works:
async def my_embedder(texts: list[str]) -> list[list[float]]:
return await my_model.embed(texts)
chunker = SemanticChunker(embedder=my_embedder, percentile=95.0)
chunks = await chunker.chunk_async(article) # async-only for Python embedders
Python embedders must return
list[list[float]]— call.tolist()on numpy arrays. They are async-only:chunk()/chunk_many()raise aRuntimeErrorpointing you at the_asyncvariants.
The Chunk object
chunk.text # the chunk's text
chunk.start # char offset into the source (Python slicing semantics)
chunk.end # char offset, exclusive
chunk.index # 0-based position
chunk.hash # xxh3-64 content hash
Benchmarks
Benchmarked on Apple M3 Pro (arm64), Python 3.12.10. Decimal MB/s, median
of 5 runs, measured from Python (the numbers you actually get). Reproduce
both tables and plots with uv run --extra bench benchmarks/bench.py;
Rust-level criterion benches: cargo bench -p konan-core.
vs other libraries (same 1 MB document, identical configs)
| Strategy | Library | Throughput | Chunks |
|---|---|---|---|
| recursive | konan | 279 MB/s | 1298 |
| recursive | semantic-text-splitter | 190 MB/s | 1368 |
| recursive | chonkie | 90 MB/s | 1413 |
| recursive | langchain-text-splitters | 25 MB/s | 1468 |
| token | konan | 62 MB/s | 438 |
| token | semchunk | 25 MB/s | 616 |
| token | langchain-text-splitters | 21 MB/s | 438 |
| token | chonkie | 18 MB/s | 438 |
| token | semantic-text-splitter | 3 MB/s | 505 |
| sentence | konan | 71 MB/s | 1042 |
| sentence | chonkie | 3 MB/s | 2124 |
| recursive (unicode) | konan | 170 MB/s | 1092 |
| recursive (unicode) | semantic-text-splitter | 165 MB/s | 1150 |
| recursive (unicode) | chonkie | 58 MB/s | 1171 |
Throughput per strategy (1 MB document)
| Chunker | Config | Throughput | Chunks |
|---|---|---|---|
NaiveChunker |
200 words | 549 MB/s | 804 |
FixedSizeChunker |
1000 chars, 200 overlap | 1,065 MB/s | 1255 |
RecursiveChunker |
1000 chars, 200 overlap | 273 MB/s | 1298 |
SentenceChunker |
1000 chars, 1 overlap | 71 MB/s | 1130 |
MarkdownChunker |
1000 chars, 200 overlap | 444 MB/s | 1511 |
TokenChunker |
512 tokens, 64 overlap (cl100k) | 61 MB/s | 438 |
Parallel scaling — rayon goes brrr
64 docs × 256 KB through RecursiveChunker:
| Mode | Time | Throughput | Speedup |
|---|---|---|---|
sequential chunk() loop |
61 ms | 267 MB/s | 1.0× |
chunk_many() (rayon, GIL released) |
11 ms | 1,454 MB/s | 5.5× |
(Pure-Rust criterion puts the same workload at ~6 GiB/s; the Python numbers include chunk-object conversion.)
Caveats, honestly: recursive/token use identical configs across libraries
(1000 chars / 200 overlap; cl100k, 512 / 64 — note the identical token chunk
counts for the libraries with the same windowing semantics). Sentence
configs are not directly comparable (konan groups by chars, chonkie by
tokens) — read those rows as per-library cost, not head-to-head. The
recursive (unicode) rows run mixed German/CJK/Cyrillic/emoji prose, off
konan's ASCII fast path. konan tokenizes with
bpe-openai (tiktoken-equivalent
output, much faster encoder).
Development
uv sync # set up the venv (builds the extension)
uv run maturin develop --uv # rebuild after Rust changes
cargo test --workspace # rust unit tests
uv run pytest -q # python integration tests
The workspace is hexagonal: crates/konan-core is pure
Rust (no PyO3) with Chunker and Embedder ports;
crates/konan-py adapts it to Python.
License
MIT
Named after Konan of the Akatsuki — the only one who could fold paper into anything. 🗞️
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file konan-0.2.3.tar.gz.
File metadata
- Download URL: konan-0.2.3.tar.gz
- Upload date:
- Size: 44.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
74c807793efd0bf4af7452eff6e094a9d583b93701bac02c1d5a589305e10672
|
|
| MD5 |
62c528cc52a1ea80f865b8c7b02cbbc7
|
|
| BLAKE2b-256 |
fdc928a71dacca5b0a0dd10ecc0ae3584d5c85901b9fd328eb2549ff15a7d4aa
|
Provenance
The following attestation bundles were made for konan-0.2.3.tar.gz:
Publisher:
release-please.yaml on 4thel00z/konan
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
konan-0.2.3.tar.gz -
Subject digest:
74c807793efd0bf4af7452eff6e094a9d583b93701bac02c1d5a589305e10672 - Sigstore transparency entry: 1744164473
- Sigstore integration time:
-
Permalink:
4thel00z/konan@14ea1d4ae1c313b469713ef0f5f207dbba032bc7 -
Branch / Tag:
refs/heads/master - Owner: https://github.com/4thel00z
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release-please.yaml@14ea1d4ae1c313b469713ef0f5f207dbba032bc7 -
Trigger Event:
push
-
Statement type:
File details
Details for the file konan-0.2.3-cp312-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: konan-0.2.3-cp312-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 29.2 MB
- Tags: CPython 3.12+, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f5e4a4e7c90858f39cfb2e91a16e8cefcf0f4ba6d62f87bfab4caec0ada9f038
|
|
| MD5 |
9b2bad14cd06dfcdc0045df08c4e6fa9
|
|
| BLAKE2b-256 |
8e73536efcf88d743a7e7398b3120e6eb70fdf872ad67428eaf8f5f054588bf0
|
Provenance
The following attestation bundles were made for konan-0.2.3-cp312-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:
Publisher:
release-please.yaml on 4thel00z/konan
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
konan-0.2.3-cp312-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl -
Subject digest:
f5e4a4e7c90858f39cfb2e91a16e8cefcf0f4ba6d62f87bfab4caec0ada9f038 - Sigstore transparency entry: 1744165001
- Sigstore integration time:
-
Permalink:
4thel00z/konan@14ea1d4ae1c313b469713ef0f5f207dbba032bc7 -
Branch / Tag:
refs/heads/master - Owner: https://github.com/4thel00z
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release-please.yaml@14ea1d4ae1c313b469713ef0f5f207dbba032bc7 -
Trigger Event:
push
-
Statement type:
File details
Details for the file konan-0.2.3-cp312-abi3-macosx_11_0_arm64.whl.
File metadata
- Download URL: konan-0.2.3-cp312-abi3-macosx_11_0_arm64.whl
- Upload date:
- Size: 29.2 MB
- Tags: CPython 3.12+, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e435c7a12a1ca1a0c0a65627ee270394986d74004f10deb10a03e055afc1372e
|
|
| MD5 |
17b2f4f1d6f0a18cb8d84626d1604439
|
|
| BLAKE2b-256 |
169c94435db79668438b70c4d0ce3b647d3f1e27d082fb055ce238e7c1171872
|
Provenance
The following attestation bundles were made for konan-0.2.3-cp312-abi3-macosx_11_0_arm64.whl:
Publisher:
release-please.yaml on 4thel00z/konan
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
konan-0.2.3-cp312-abi3-macosx_11_0_arm64.whl -
Subject digest:
e435c7a12a1ca1a0c0a65627ee270394986d74004f10deb10a03e055afc1372e - Sigstore transparency entry: 1744164931
- Sigstore integration time:
-
Permalink:
4thel00z/konan@14ea1d4ae1c313b469713ef0f5f207dbba032bc7 -
Branch / Tag:
refs/heads/master - Owner: https://github.com/4thel00z
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release-please.yaml@14ea1d4ae1c313b469713ef0f5f207dbba032bc7 -
Trigger Event:
push
-
Statement type: